[따라하기] 건강데이터 수집 및 기록 시스템 #1 - BLE 기반 스마트 체중계에서 실시간 데이터 받기

2015.10.02 13:06


위 동영상(건강데이터 수집 및 기록 시스템 데모) 중 스마트 체중계의 체중정보를 모바일에서 실시간으로 받아오는 기능을 따라하기로 진행합니다.


스마트 체중계에서 체중정보 수신앱 만들기

❑ 앱소개

앱소개

이 앱은 스마트체중계와 블루투스 LE로 연결 해 체중정보를  실시간으로 받아와 화면에 표시하는 앱입니다.

따라하기의 목적

스마트 체중계 연결을 통해 블루투스 LE 인터페이스를 제공하는 다양한 기기(또는 센서)에 연결하고 데이터를 수신하는 방법을 이해합니다.


따라하기에서는 다음 내용을 다룹니다.

  • RAD Studio로 멀티-디바이스(안드로이드, iOS, 윈도우, OS X) 앱 프로젝트 생성 및 화면 디자인
  • TBluetoothLE 컴포넌트로 스마트 체중계와 연결 및 데이터 수신(구독)
  • 실 기기에 배포 및 테스트(안드로이드 폰, 아이폰)

따라하기

0, 사전 준비사항

1, 빈 멀티-디바이스 애플리케이션 프로젝트 생성

2, 메인 UI 제작

3, 블루투스LE 컴포넌트 추가

4, 주변의 디바이스 탐색

5, 탐색된 디바이스 목록에서 디바이스 선택 후 서비스 탐색

6, 탐색된 서비스 목록에서 서비스를 선택 후 특성 구독

7, 특성 데이터 수신 후 체중 데이터 가져오기

8, 블루투스 권한설정 

9, 디바이스에 배포 및 테스트

0, 사전 준비사항

개발도구 준비하기

델파이(또는 RAD Studio)를 이용해 실습 진행합니다.(이 실습은 델파이 10 시애틀 버전으로 작성되었습니다. 델파이 XE8 이상에서 따라할 수 있습니다.) 

만약, 델파이가 설치되지 않았다면 아래의 페이지에서 델파이를 다운로드 받아 설치 후 진행하시기 바랍니다. 

스마트 체중계 준비하기

따라하기에서는 Wahoo Balance Bluetooth Smart Scale 제품을 이용해 실습을 진행합니다.

https://www.underarmour.com/en-us/wahoo-balance-bluetooth-smart-scale/pid1276806-100-OSFA


Wahoo Balance Bluetooth Smart Scale은 다음과 같은 디바이스 이름과 UUID를 갖습니다.

디바이스 이름 : Wahoo Scale v1.3

서비스 UUID : 00001901-0000-1000-8000-00805F9B34FB

특성 UUID : 00002B01-0000-1000-8000-00805F9B34FB

위 정보는 구현에 필요한 정보입니다.(시중의 다른 스마트 체중계는 위 값들이 다릅니다.)

BLE 구조에 대한 소개

블루투스 LE는 프로파일(Profile), 서비스(Service), 특성(Characteristic) 기반으로 아래 그림과 같이 프로파일은 하나 이상의 서비스를 갖고, 서비스는 하나이상의 특성을 갖는 계층구조로 구성되어 있습니다.



(출처: GENERIC ATTRIBUTE PROFILE (GATT) - https://developer.bluetooth.org/TechnologyOverview/Pages/GATT.aspx)


블루투스 LE 인터페이스를 제공하는 장비와 연결하고 데이터를 받기 위해 다음 과정이 필요합니다.(실제 구현도 다음 단계대로 진행합니다.)

1, 주변의 블루투스 LE 프로파일을 탐색합니다.

2, 탐색된 프로파일에서 서비스를 탐색합니다.

3, 탐색된 서비스에서 특성을 찾아 구독(subscribe)합니다.

4, 구독한 특성에서 발생되는 특성값에서 필요한 데이터를 추출합니다.

1, 빈 멀티-디바이스 애플리케이션 프로젝트 생성

  1. File > New > Multi-Device Application - Delphi 메뉴를 선택 하고, Blank Application을 선택해 프로젝트를 생성합니다.
  2. File > Save all 메뉴를 선택 해 유닛이름을 "MainForm.pas"로 프로젝트 이름을 "SmartScale.dproj"로 저장합니다.

2, 메인 UI를 제작

모바일 앱 스타일로 화면 구성하기 위해 폼디자이너 스타일을 Android로 변경합니다.


다음화면과 표를 이용해 체중정보 수신 앱 메인 UI를 개발합니다.


상위 오브젝트 

오브젝트 

속성 

값(또는 설명) 

 Form1

 ToolBar1

 

 

 Layout1

 Align

 Top
 Height 81
 Memo1

 Align

 Client

 ToolBar1

 Label1

 Align

 Client

 Text

 스마트 체중계 데모

 TextSettings.HorzAlign

 Center

 Switch1 Align Right
 Layout1 Text1

 Align

 Center

 Text

 0.0
 TextSettings.Font.Size

 30

 TextSettings.HorzAlign Trailing
 Width 114
 Text1 Text2 Position.X 120
 Text Kg

 TextSettings.HorzAlign

 Leading

3, 블루투스 LE 컴포넌트 추가

블루투스 LE 컴포넌트 추가

Tool Palette에서 TBluetoothLE 컴포넌트 선택 해 폼에 추가합니다.


필요한 변수, 메소드, 상수 추가

구현에 필요한 변수(디바이스, 서비스, 특성), 메소드 선언(시작, 중지), 상수(장비명과 서비스, 특성 UUID)를 아래와 같이 추가합니다.

  private
    { Private declarations }
    FBLEDevice: TBluetoothLEDevice;
    FBLEGattService: TBluetoothGattService;
    FBLEGattChar: TBluetoothGattCharacteristic;

    procedure StartScale; // 체중계 연결 동작 시작(블루투스LE 활성화, 주변 디바이스 탐색)
    procedure StopScale; // 체중계 연결 동작 중지(블루투스LE 비활성화, 특성 구독 해제)
  public
    { Public declarations }
  end;

const
  ScaleDeviceName = 'Wahoo';

  WEIGHT_SERVICE: TBluetoothUUID          = '{00001901-0000-1000-8000-00805F9B34FB}';
  WEIGHT_CHARACTERISTIC: TBluetoothUUID   = '{00002B01-0000-1000-8000-00805F9B34FB}';

메소드 구현부 생성

StartScale, StopScale 메소드 구현부를 자동 생성하기 위해 procedure StartScale;에 마우스 커서를 옮기고 키보드에서 Ctrl + Shift + C를 동시에 누릅니다.


4, 주변의 디바이스 탐색

주변 디바이스 탐색

Switch1의 OnSwitch 이벤트 핸드러 생성(Switch1 컴포넌트 선택 > Object Inspector 창에서 Event 탭 선택 > OnSwitch 우측의 콤보박스 더블클릭) 후 아래 코드를 입력합니다.

procedure TForm1.Switch1Switch(Sender: TObject);
begin
  if Switch1.IsChecked then
    StartScale
  else
    StopScale;
end;

StartScale 메소드에서 블루투스 LE를 활성화하고, 주변 디바이스를 탐색하는 코드를 다음과 같이 입력합니다.
(StopScale은 제일 마지막에 다시 구현합니다.)

procedure TForm1.StartScale;
begin
  BluetoothLE1.Enabled := True;
  BluetoothLE1.DiscoverDevices(1000); // 1초(1000 ms)동안 주변 디바이스 탐색, 완료 시 OnEndDiscoveryDevices 이벤트 발생
end;

5, 탐색된 디바이스 목록에서 디바이스 선택 후 서비스 탐색

StartScale 메소드의 DiscoverDevices 메소드 호출하면 (주변의 디바이스 탐색 후) OnEndDiscoverDevices 이벤트가 발생합니다. 

OnEndDiscoverDevices 이벤트는 파라메터로 탐색된 디바이스 목록(ADeviceList)을 제공합니다.

이 디바이스 목록에서 'Wahoo'(위에서 ScaleDeviceName 상수로 선언)로 시작하는 디바이스를 선택합니다.

디바이스를 선택했다면, 디바이스에서 제공하는 서비스 탐색을 시도합니다.

procedure TForm1.BluetoothLE1EndDiscoverDevices(const Sender: TObject;
  const ADeviceList: TBluetoothLEDeviceList);
var
  Device: TBluetoothLEDevice;
begin
  if ADeviceList.Count = 0 then
  begin
    Memo1.Lines.Add('발견된 디바이스가 없습니다.');
    Switch1.IsChecked := False; // 디바이스 미발견 시 스위치 끄기
    Exit;
  end;

  FBLEDevice := nil;
  Memo1.Lines.Add('Device List');
  for Device in ADeviceList do
  begin
    Memo1.Lines.Add(' - ' + Device.DeviceName); // 로그-디바이스 이름

    // 디바이스 이름으로 디바이스 선택
    if Device.DeviceName.StartsWith(ScaleDeviceName) then
    begin
      FBLEDevice := Device;
      Memo1.Lines.Add('디바이스를 찾았습니다.');
    end;
  end;

  if FBLEDevice = nil then
    Exit;

  // [TIP] 서비스 발견이 실패하는 경우가 종종 있음
    // 실패한 경우 한번더 발견요청
  if not FBLEDevice.DiscoverServices then
    FBLEDevice.DiscoverServices; // 서비스 탐색 완료 시 OnEndDiscoverServices 이벤트 발생
end;

6, 탐색된 서비스 목록에서 서비스를 선택 후 특성 구독

서비스 탐색(DiscoverServices)이 완료되면 OnEndDiscoverServices 이벤트가 발생합니다.

OnEndDiscoverServices 이벤트는 파라메터로 탐색된 서비스 목록(AServiceList)을 제공합니다.

이 서비스 목록에서 서비스 UUID와 같은 UUID를 갖는 서비스를 선택합니다.

선택한 서비스에서 특성 UUID로 특성을 찾습니다.

특성에서 체중정보를 포함한 특성데이터를 수신할 수 있도록 구독(SubscribeToCharacteristic) 합니다.

procedure TForm1.BluetoothLE1EndDiscoverServices(const Sender: TObject;
  const AServiceList: TBluetoothGattServiceList);
var
  Service: TBluetoothGattService;
begin
  Memo1.Lines.Add('Service List');
  for Service in AServiceList do
  begin
    Memo1.Lines.Add(' - ' + Service.UUID.ToString); // 로그-디바이스 이름
    if Service.UUID = WEIGHT_SERVICE then
    begin
      FBLEGattService := Service;
      Memo1.Lines.Add('서비스를 찾았습니다.');
    end;
  end;

  if FBLEGattService = nil then
  begin
    Memo1.Lines.Add('서비스를 찾지 못했습니다.');
    Exit;
  end;

  // [TIP][예외] 특성목록을 생성하기 위해 아래 코드 호출
    // GetCharacteristic 바로 호출 시 특성을 가져오지 못해
    // GetCharacteristics을 먼저 호출
  BluetoothLE1.GetCharacteristics(FBLEGattService);

  FBLEGattChar := BluetoothLE1.GetCharacteristic(FBLEGattService, WEIGHT_CHARACTERISTIC);
  if FBLEGattChar = nil then
  begin
    Memo1.Lines.Add('서비스 특성을 찾지 못했습니다.');
    Exit;
  end;

  if BluetoothLE1.SubscribeToCharacteristic(FBLEDevice, FBLEGattChar) then // 구독하면 OnCharacteristicRead 이벤트를 통해 특성 값을 받음
    Memo1.Lines.Add('특성에 구독했습니다.'); 
end;

7, 특성 데이터 수신 후 체중 데이터 가져오기

특성에 구독(SubscribeToCharacteristic)하면 TBluetooth 컴포넌트의 OnCharacteristicRead 이벤트로 특성 값을 받을 수 있습니다. 스마트 체중계의 경우 체중계의 체중이 변경될 때마다 특성 값을 받을 수 있습니다.

Wahoo Balance Blueeoth Smart Scale은 다음과 같은 코드로 특성값에서 체중 정보를 가져옵니다.

(특성 값을 분석하는 방법은 서비스마다 다릅니다.)

procedure TForm1.BluetoothLE1CharacteristicRead(const Sender: TObject;
  const ACharacteristic: TBluetoothGattCharacteristic;
  AGattStatus: TBluetoothGattStatus);
var
  Weight: Single;
begin
  Weight := (ACharacteristic.GetValueAsInteger shr 8) / 10;
    // 특성 값에서 앞의 3byte 이용, 마지막 숫자는 소숫점이므로 10으로 나누기

  Text1.Text := Format('%3.1f', [Weight]);
end;

마지막으로 앞에서 선언한 StopScale에 특성 구독을 해지하고, 블루투스 LE를 비활성화하는 코드를 추가합니다.

procedure TForm1.StopScale;
begin
  if Assigned(FBLEDevice) and Assigned(FBLEGattChar) then
    BluetoothLE1.UnSubscribeToCharacteristic(FBLEDevice, FBLEGattChar);
  BluetoothLE1.Enabled := False;
end;

8, 블루투스 권한설정

(이 과정은 안드로이드 플랫폼을 사용하지 않는 경우 생략할 수 있습니다.)

비콘은 블루투스 LE(Low Energy) 기반으로 통신합니다.  블루투스 사용권한을 설정합니다.

Project > Options 메뉴를 선택 후 Uses Permissions 화면으로 이동합니다.


Bluetooth와 Bluetooth admin 두개의 권한을 사용하도록 설정합니다.


9, 디바이스에 배포 및 테스트

처음 디바이스에 배포하는 경우 먼저 모바일 개발환경 설정하고 진행하기 바랍니다.


개발을 완료했습니다. 이제 준비된 개발환경으로 플랫폼 선택 후 Run > Run Without Debugging 메뉴를 이용해 실행하고 테스트 합니다.



테스트 방법:

  1. 디바이스의 블루투스 기능을 킵니다.
  2. 앱을 실행 후 스위치를 커서 주변의 블루투스 LE를 탐색해 스마트 체중계와 연결합니다.
    (메모 컨트롤에 "디바이스를 찾았습니다." > "서비스를 찾았습니다." > "특성에 구독했습니다." 메시지가 표시되면 정상 연결된 것입니다.)
  3. 체중계에 올라가면 실시간으로 앱에 체중정보가 표시되는 것을 확인합니다.

저작자 표시 비영리 동일 조건 변경 허락
신고

험프리.김현수 파이어몽키

  1. Blog Icon
    이승욱

    안녕하세요.
    지난 9월에 세미나를 교수님과 다녀온 후에 학과에서 간단하게 시연해보려고 계속 작업중에 있습니다. 비콘을 이용한 경보 앱은 구현을 하고 이번에는 체중계 앱을 작업하고 있는데 문제가 있어 조언을 얻고자 글을 남기게 되었습니다.
    기존에 포스팅 하신데로 모두 설정하였고 UUID도 맞게 입력이 되었는데 컴파일 하고 연결하려고 하면 디바이스가 1개 검색이 되지만 weight_service를 불러오지 못한다고 합니다.
    현재 작업하는 디바이스는 탭이고 개별로 블루투스로 연결해볼까 해서 연결하면 Wahoo Scale v1.3 에서 연결을 거절했다는 메시지가 뜨네요...
    답변해주시면 감사하겠습니다.

  2. 디바이스 발견 시 본문의 화면과 같이 Wahoo Scale v1.3으로 표시되나요?
    쳬중계를 연결할때 미리 체중계에 올라가 체중계를 키고 시도해 보세요.

    그리고, 혹시 모르니 다른 안드로이드 기기로도 테스트해보시기 바랍니다.
    (그럴리 없지만 체중계의 이상을 확인하려면 아이폰에서 Wahoo Wellness 앱을 설치해 연결을 확인해보세요. 무료입니다.)

    그래도 안되면 다음 링크에 데모 샘플코드가 있으니 해당 소스코드로 테스트해보시기 바랍니다.
    https://github.com/devgear/RADStudio_IoTDemos

    마지막으로 시간되시면 제가 사물인터넷 1일 과정 교육을 진행하고 있습니다.
    해당 교육에서 스마트 체중계 연결 실습도 진행되니 오프라인으로 확인하고 싶으시면 교육신청해주세요.^^
    http://devgear.co.kr/support/education/radstudio_iot.html

  3. Blog Icon
    김헌성

    안녕하세요~

    블루투스 저울로 스마트폰 앱을 만드는것을 공부하고 있습니다.

    혹시 체중계별로 UUID가 다르다고 하셨는데요..

    Wahoo Balance Bluetooth Smart Scale 이제품의 UUID는 어떻게 아신건가요??

    공개를 해주나요??아니면 다른방법으로 아는 방법이 있나요??

    저는 블루투스 정밀저울로 테스트를 해야해서요..ㅠ

    제가 테스트하려는 제품은 Smart Connect Bluetooth Kitchen Scale 입니다.

    답변주시면 감사하겠습니다. ㅎ

  4. Blog Icon
    박상우

    안녕하십니까. 비콘과 앱 개발을 처음 해보는 학생입니다.
    비콘에 대한 정확한 개념도 없이 막 시작하는 터라 궁금한 점이 한두가지가 아닌데, 초면에 무례함을 무릎쓰고 몇가지 질문 드려도 될까요?

    우선 첫 번째로 비콘을 이용하여 자동 출석체크 시스템을 만드려는데, 강의장이라고 인식되는 범위 내에 단말이 들어오면 단말에 앱 알람이 뜨며 해당 강의실의 출석 버튼이 오픈되고, 출석 버튼을 누르면 출석이 인정되게 하는 앱을 구현하고 싶습니다. 가능할지 궁금합니다.

    첫번째단계까지 구현하고 난 다음에는 출석 후 단말이 30분 이상 해당 지역에 없으면 출석인정이 안되도록 하고 싶은데, 비콘에 따라 로그를 남겨 그 기록을 바탕으로 빅데이터 처리를 할 수 있다고 해서 이 기능을 이용하고 싶은데 가능할지 궁금합니다.

    세 번째로는 비콘의 종류는 상관이 없는것인지, 비콘에 따라 구현이 다르게 되는지에 대한 궁금증입니다.

    바쁘신 와중에 질문드려 죄송합니다. 그리고 읽어주셔서 감사합니다.

  5. 1, 원하는 기능 구현 가능합니다. 미들웨어 형태의 서버가 필요하겠네요. 모바일 앱에서 비콘 신호를 받아 출석버튼을 활성화 하고 버튼 누르면 서버를 통해 출석처리하도록 요청하도록 하세요.

    2, 모바일 앱을 계속 켜놓고, (예로)1분마다 서버로 비콘과의 거리등을 전송하면 서버에서 30분이상 해당 지역에 있었는지 판단할 수 있습니다.

    3, 비콘이 아이비콘, 알트비콘 주종의 표준을 따랐다면 비콘의 종류와 관계없이 대부분의 비콘을 연동할 수 있습니다.

    비콘 연동하는 내용은 http://blog.hjf.pe.kr/384 을 참고하세요.

  6. Blog Icon
    박상우

    빠른 답변 대단히 감사드립니다. 혹시 eclipse에서도 구현할 수 있는 기능인가요? 제가 pascal어를 공부해본 적도, rad studio를 사용해본 적도 없어서 잘 할 수 있을지가 걱정이 됩니다..답변 다시한번 대단히 감사합니다. 큰 도움이 되었습니다.

  7. 개념과 구성만 잘 잡고 개발하신다면 툴(언어)가 문제가 되진 않을 것입니다.
    자바에서도 비슷한 기능을 제공하니까 살펴보시기 바랍니다.

    기회되면 델파이도 한번 관심 갖어주세요^^
    (델파이 교육과정 : http://devgear.co.kr/edu
    대학생분들은 5명까지 무료로 참석할 수 있습니다.)

  8. Blog Icon

    비밀댓글입니다

  9. 제가 테스트한 시점에 인바디 정보를 다루지 않아 정확한 내용을 모르겠습니다.

    만약, 체중계가 인바디 정보를 제공한다면,
    충분히 가져올 수 있습니다.

    위 샘플에서는 원하는 체중정보를
    프로필 > 서비스 > 특성 > 속성 > 값으로 원하는 값을 찾아서 사용한 것이기 때문에
    위 매커니즘으로 인바디 정보를 제공한다면 가져올 수 있을 것으로 보입니다.

  10. Blog Icon
    이원석

    감사합니다. 공부하는데 많은 도움이 되었습니다.
    다름이 아니오라, 여러개의 블루투스에서 동시에 값을 받아올수 있을까요?
    이를테면 체중계를 여러개 연결하여, 1번 체중계, 2번 체중계 데이터를 동시에 받는
    형태입니다.
    염치없이 질문 드려봅니다.
    감사합니다.

  11. 컴포넌트 구조 상 컴포넌트 여러개를 두고 연결하면 가능할 것으로 생각합니다.

    하지만, 직접해보시고 가능여부를 판단하는 것이 좋습니다.