Developing The Internet of Things: WAHOO BALANCE Smart Body Scale
Keeping tack of personal health and fitness programs is a central aspect of the Internet of Things. Smart devices, aka Things, gather bioinformatic data from our bodies and from our daily routines. This data is collected and sorted by Connected Apps giving end users and doctors alike a data driven roadmap to provide preventative health behavior, remote monitoring, and diagnosis.
In this blog post, find out how Appmethod and the Bluetooth LE component streamlines the integration of the WAHOO Balance Smart Body Scale with a Connected App.
Wahoo Balance Smart Body Scale App
The Wahoo Balance Smart Body Scale provides body weight data over Bluetooth LE.
Download
Project files for this demo application can be found here.
Bluetooth Low Energy (aka Smart Bluetooth) provides a new environment for devices with small amount of data to transfer and lower power consumption.
Please refer to the Appmethod DocWiki for an overview of how to use the Bluetooth Low Energy Component.
The following steps will guide you through connecting to the Wahoo Balance Scale over Bluetooth LE implemented in C++.
Step 1: Create App UI
First, from the Tool Palette add a TBluetoothLE Component to the Form Designer.
This demo app consists of three TPanels, one for the header, one for the body, and one for the log console.
The HeaderPanel Align property is set to Top.
The BodyPanel Align property is set to Client.
The ConsolePanel Align property is set to Bottom.
The HeaderPanel contains a TLabel nested inside a TRectangle which displays the name of the app at the top of the screen. It also has a TToolBar which contains two TButtons; one to initiate the BluetoothLE connection and one to clear the memo console.
The BodyPanel consists of a TCircle and two nested TLabels. The label_WeightLabel will dynamically be set to display the converted weight data provided by the scale.
The ConsolePanel contains a TMemo which will display the raw Bluetooth LE data.
Event Handlers for the header buttons
1
2
3
4
5
|
//--------------------------------------------------------------------------- void __fastcall TForm2::ClearMemoBtnClick(TObject *Sender) { memo_Console->Lines->Clear(); } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//--------------------------------------------------------------------------- void __fastcall TForm2::ConnectBtnClick(TObject *Sender) { //- disable connection button during bluetooth discovery ConnectBtn->Enabled = false ; //- reset weight label label_WeightData->Text = "0" ; //- connect to Wahoo Balance Scale GUID Devices[] = {WEIGHT_DEVICE}; //- The EXISTINGARRAY macro defined in sysopen.h can be used to // pass an existing array where an open array is expected. BluetoothLE1->DiscoverDevices(2500, EXISTINGARRAY(Devices)); memo_Console->Lines->Add( "Connecting to Scale..." ); } |
Step 2: Set Wahoo Balance UUID
In the global space, define the UUID as follows:
1
2
3
4
5
6
7
8
|
//- Wahoo Fitness Bluetooth // 0x1901 is the weight service // 0x2B01 is the live weight characteristic readings // 0x84ae0000 0001 - 38 lbs 17kg // 0b 10000100101011100000000000000000 const TBluetoothUUID WEIGHT_DEVICE = StringToGUID( "{00001901-0000-1000-8000-00805F9B34FB}" ), WEIGHT_SERVICE = StringToGUID( "{00001901-0000-1000-8000-00805F9B34FB}" ), WEIGHT_CHARACTERISTIC = StringToGUID( "{00002B01-0000-1000-8000-00805F9B34FB}" ); |
Step 3: Add TBluetoothLE Component Event Handlers
Select the BluetoothLE1 component just added and from within the Object Inspector open the events tab and double click the following events:
- OnCharacteristicRead
- OnEndDiscoverDevices
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
//--------------------------------------------------------------------------- void __fastcall TForm2::BluetoothLE1CharacteristicRead(TObject * const Sender, TBluetoothGattCharacteristic * const ACharacteristic, TBluetoothGattStatus AGattStatus) { if (AGattStatus != TBluetoothGattStatus::Success) { memo_Console->Lines->Add( "Error reading Characteristic " + ACharacteristic->UUIDName + ": " + IntToStr( static_cast < int >(AGattStatus))); } else { String LSValue = IntToStr( ACharacteristic->GetValueAsInteger() ); memo_Console->Lines->Add( "Value: " + LSValue); //- convert to pounds float wPounds = ( ACharacteristic->GetValueAsInteger() >> 8 ) * 0.2205; //- update UI memo_Console->Lines->Add( String(). sprintf ( L "Pounds: %0.2f" , wPounds ) ); label_WeightData->Text = String(). sprintf ( L "%0.2f" , wPounds ); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
//--------------------------------------------------------------------------- void __fastcall TForm2::BluetoothLE1EndDiscoverDevices(TObject * const Sender, TBluetoothLEDeviceList * const ADeviceList) { memo_Console->Lines->Add(IntToStr(ADeviceList->Count) + " devices discovered:" ); for ( int i = 0; i < ADeviceList->Count; i++) { memo_Console->Lines->Add(ADeviceList->Items[i]->DeviceName); } if (BluetoothLE1->DiscoveredDevices->Count > 0) { BTLEDevice = BluetoothLE1->DiscoveredDevices->First(); if (BluetoothLE1->GetServices(BTLEDevice)->Count == 0) { memo_Console->Lines->Add( "No services found!" ); } else { GetServiceAndCharacteristics(); } } else { memo_Console->Lines->Add( "Device not found." ); //- re-inable bluetooth discovery ConnectBtn->Enabled = true ; } } |
Step 4: Get Services and Characteristics
Once the ConnectBtnClick event handler has succesffuly discovered the Wahoo Balance Bluetooth device, the following function GetServiceAndCharacteristics() is called.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
//--------------------------------------------------------------------------- void __fastcall TForm2::GetServiceAndCharacteristics( void ) { TBluetoothGattCharacteristicList * CharList = NULL; TBluetoothGattDescriptorList * Descriptor = NULL; //- log bluetooth device services and characteristics to the console for ( int i = 0; i < BTLEDevice->Services->Count; i++) { CharList = BTLEDevice->Services->Items[i]->Characteristics; memo_Console->Lines->Add( BTLEDevice->Services->Items[i]->UUIDName + " : " + GUIDToString(BTLEDevice->Services->Items[i]->UUID) ); for ( int j = 0; j < CharList->Count; j++) { memo_Console->Lines->Add( "--> " + CharList->Items[j]->UUIDName + " : " + GUIDToString(CharList->Items[j]->UUID)); Descriptor = CharList->Items[j]->Descriptors; for ( int k = 0; k < Descriptor->Count; k ++) { memo_Console->Lines->Add( "--> " + Descriptor->Items[k]->UUIDName + " : " + GUIDToString(Descriptor->Items[k]->UUID)); } } } //- create Bluetooth GATT Service handle BTGattService = BTLEDevice->GetService(WEIGHT_SERVICE); //- ensure the Bluetooth GATT service is available if (BTGattService != NULL) { memo_Console->Lines->Add( "Service found" ); BTGattWeightCharact = BTGattService->GetCharacteristic(WEIGHT_CHARACTERISTIC); } else { memo_Console->Lines->Add( "Service not found" ); } if (BTGattWeightCharact != NULL) { //- start listening for new characteristic data BTLEDevice->SetCharacteristicNotification(BTGattWeightCharact, true ); } else { memo_Console->Lines->Add( "HRM Characteristic not found" ); } } |
Step 5: Initalize State
Bluetooth connectivitiy state is maintained by a few variables from the main TForm class:
1
2
3
4
5
|
private : // User declarations TBluetoothLEManager * BTLEManager; TBluetoothLEDevice * BTLEDevice; TBluetoothGattService * BTGattService; TBluetoothGattCharacteristic * BTGattWeightCharact; |
They are initalized as null at the app's point of entry:
1
2
3
4
5
6
7
8
|
//--------------------------------------------------------------------------- __fastcall TForm2::TForm2(TComponent* Owner) : TForm(Owner) { BTGattService = NULL; BTGattWeightCharact = NULL; BTLEDevice = NULL; } |


Comments
-
Please login first in order for you to submit comments