Working with ThingConnect Devices
Go Up to ThingConnect
To use a Bluetooth LE device in your application, you need to follow these procedures.
Contents
Connecting with the Devices
To connect with a Bluetooth LE device in your application, you need to:
- Drop a
TBluetoothDeviceDiscoveryManager
component. - Drop the corresponding component for the Bluetooth LE device.
- In the Object Inspector, set the DiscoveryManager property of the Bluetooth LE component to the
TBluetoothDeviceDiscoveryManager
. - Set the appropriate discovery mechanism in the
DiscoveryMethod
property of theTBluetoothDeviceDiscoveryManager
. - To connect to the actual device you need to call the method
DiscoverDevices
of theTBluetoothDeviceDiscoveryManager
.Delphi:
FDiscoveryManager.DiscoverDevices;
C++:
FDiscoveryManager->DiscoverDevices();
- After connecting to the device, the
OnDeviceConnected
event of the corresponding component is called. - Add the following units to the Uses clause of the application:
- Iot.Family.BluetoothLE.GattTypes
- The custom Types unit (if the component defines custom data types).
Setting the Discovery Mechanism Method
To set the discovery mechanism method of the Bluetooth LE devices in your application, you can either:
- In the Object Inspector, use the
DiscoveryMethod
property. - Set the discovery mechanism by code before calling the
DiscoverDevices
method:
Delphi:
{$IF Defined(WIN32) or Defined(WIN64)}
FDiscoveryManager.DiscoveryMethod := TDiscoveryMethod.Connect;
{$ELSE}
FDiscoveryManager.DiscoveryMethod := TDiscoveryMethod.ScanResponse;
{$EndIf}
C++:
#ifdef _Windows
FDiscoveryManager->DiscoveryMethod = TDiscoveryMethod::Connect;
#else
FDiscoveryManager->DiscoveryMethod = TDiscoveryMethod::ScanResponse;
#endif
- Note: For 32-bit Windows and 64-bit Windows platforms applications, you need to set Connect as the discovery method type.
- Warning: Some devices do not broadcast their name or services in the advertised data. You need to use the Connect discovery method instead, and set manually the name of the device you want to connect to.
Using the ScanResponseAllDevices Discovery Mechanism Method
If you use the ScanResponseAllDevices
discovery mechanism method to connect to a given device, you need to retrieve the name of the DiscoveredDevice from the list.
- Implement the
OnGenericHeartRateMonitor1DeviceListDiscovered
event handler to retrieve the list of discovered devices. In this code sample, the discovered devices are added to a TlistBox.Delphi:
procedure TForm1.GenericHeartRateMonitor1DeviceListDiscovered(const Sender: TObject; const ADiscoveredDeviceCount: Integer); var I: Integer; begin for I := 0 to ADiscoveredDeviceCount - 1 do ListBox1.Items.Add(GenericHeartRateMonitor1.DiscoveredDevice[I].DeviceName); end;
C++:
void __fastcall TForm1::GenericHeartRateMonitor1DeviceListDiscovered(TObject * const Sender, const int ADiscoveredDeviceCount) { for (int I = 0; I < ADiscoveredDeviceCount; I++) { ListBox1->Items->Add() = GenericHeartRateMonitor1->DiscoveredDevice[I]->DeviceName; } }
- Use the
ConnectDevice
method to connect to the selectedDiscoveredDevice
. This item is retrieved from the TListBox.Delphi:
procedure TForm1.ListBox1ItemClick(const Sender: TCustomListBox; const Item: TListBoxItem); begin GenericHeartRateMonitor1.ConnectDevice(GenericHeartRateMonitor1.DiscoveredDevice[Item.Index]); end;
C++:
void __fastcall TForm1::ListBox1ItemClick(TCustomListBox * const Sender, TListBoxItem * const Item) { TBluetoothLEDevice *LDevice; LDevice = GenericHeartRateMonitor1->DiscoveredDevice[AItem->Index]; GenericHeartRateMonitor1->ConnectDevice(LDevice); }
Reading a Property Data
To read the data value of a property you need to call the Refresh method and implement the event handler of the event.
For example, to read the data value of the BatteryLevel
property add the following code:
Delphi:
procedure TForm1.ReadPropertyClick(Sender: TObject);
begin
ZephyrHeartRateMonitor.RefreshBatteryLevel;
end;
C++:
void __fastcall TForm1::ReadPropertyClick(TObject *Sender)
{
ZephyrHeartRateMonitor1->RefreshBatteryLevel();
}
Event Handler
On the readable properties, the event handler is raised when the data is received after calling the Refresh method. This kind of events are called On<NameOfTheProperty>Update
.
Following the above example, to implement the event handler of the OnBatteryLevelUpdate
event use the following code snippet:
Delphi:
uses
Iot.Family.BluetoothLE.GattTypes, Iot.Device.ZephyrHeartRateMonitorTypes;
var BatteryLevelText : UnicodeString;
procedure TForm1.ZephyrHeartRateMonitorBatteryLevelUpdate(Sender: TObject; Value: Byte);
begin
BatteryLevelText := IntToStr(Value) + '%';
end;
C++:
- In the .h file, add the Uses clause:
#include <Iot.Family.BluetoothLE.GattTypes.hpp>
#include <Iot.Device.ZephyrHeartRateMonitorTypes.hpp>
- In the .cpp file:
void __fastcall TForm1::ZephyrHeartRateMonitor1BatteryLevelUpdate (TObject *Sender, BYTE Value)
{
UnicodeString BateryLevel = IntToStr(Value) + "%";
}
Subscribing to a Characteristic
To subscribe to a characteristic, you need to call the Subscribe method of the characteristic and implement the event handler of the event.
For example, to subscribe to the Heart Rate Measurement characteristic add the following code:
Delphi:
procedure TForm1.HRMSubscribeBtnClick(Sender: TObject);
begin
ZephyrHeartRateMonitor.SubscribeHeartRateMeasurement;
end;
C++:
void __fastcall TForm1::HRMSubscribeBtnClick(TObject *Sender)
{
FZephyrHeartRateMonitor->SubscribeHeartRateMeasurement();
}
Event Handler
To start receiving data updates from a subscribed characteristic, you need to implement the event handler of the event.
For example, to receive heart rate measurement updates, implement the event handler of the OnHeartRateMeasurementUpdate
event as follow:
Delphi:
uses
Iot.Family.BluetoothLE.GattTypes, Iot.Device.ZephyrHeartRateMonitorTypes;
var HRMValueText : UnicodeString;
procedure TForm1.ZephyrHeartRateMonitorHeartRateMeasurementUpdate(Sender: TObject; const Value: TGattHeartRateMeasurement);
begin
HRMValueText := 'HR: ' + IntToStr(Value.HeartRateMeasurement) + 'bpm RRInterval: ' + IntToStr(Value.RRInterval) + ' EnergyExp: ' + IntToStr(Value.EnergyExpended) +
' Contact: ' + IntToStr(Ord(Value.ContactStatus));
end;
C++:
- In the .h file:
#include <Iot.Family.BluetoothLE.GattTypes.hpp>
#include <Iot.Device.ZephyrHeartRateMonitorTypes.hpp>
- In the .cpp file:
void __fastcall TForm1::FZephyrHeartRateMonitorHeartRateMeasurementUpdate(TObject *Sender, const TGattHeartRateMeasurement &Value)
{
TGattHeartRateMeasurement Ghrm = Value;
UnicodeString HRMValueText = "HR: " + IntToStr(Ghrm.HeartRateMeasurement) + " bpm RRInterval : " + IntToStr(Ghrm.RRInterval) + " EnergyExp : " + IntToStr(Ghrm.EnergyExpended) +
" Contact:" + IntToStr((int)Ghrm.ContactStatus);
}
Unsubscribing from a Characteristic
To unsubscribe from a characteristic, you need to call the Unsubscribe method of that characteristic.
For example, to unsubscribe from the Heart Rate Measurement characteristic add the following code:
Delphi:
procedure TForm1.HRMUnSubscribeBtnClick(Sender: TObject);
begin
ZephyrHeartRateMonitor.UnsubscribeHeartRateMeasurement;
end;
C++:
void __fastcall TForm1::HRMUnSubscribeBtnClick(TObject *Sender)
{
FZephyrHeartRateMonitor->UnsubscribeHeartRateMeasurement();
}
Writing to a Characteristic
To write to a characteristic, you need to set the corresponding property of the component. To do so, use the Create
method of that characteristic, pass the needed values and then set the values to it. Moreover, you can implement an event handler to obtain the status information of the writing operation.
For example, to update the light color of the IoT light bulb you need to set the ControlCharacter property of the component. To write to this property add the following code:
Delphi:
uses
System.UIConsts;
procedure TForm1.SetColorClick(Sender: TObject);
var
LColor: TAlphaColor;
LIntensity: Integer;
begin
LIntensity := 80;
LColor := claOrange; //System.UITypes.TAlphaColor
YeelightBlue1.ControlCharacter := TControlCharacter.Create(LColor, LIntensity);
end;
C++:
void __fastcall TForm1::SetColorClick(TObject *Sender)
{
int LIntensity = 80;
TAlphaColor LColor = TAlphaColor(claGreen); //System.UITypes.TAlphaColor
YeelightBlue1->ControlCharacter = TControlCharacter(LColor, LIntensity);
}
Event Handler
On the writable properties, you can implement an event to obtain information about the status of the writing operation. This kind of events, which are named On<NameOfTheProperty>WriteStatus
, return the status at the end of the writing operation. These are the possible statuses.
To implement the write status event handler use the following code snippet:
Delphi:
procedure TForm1.YeelightBlue1ControlCharacterWriteStatus(Sender: TObject; const Status: TBluetoothGattStatus);
begin
case Status of
TBluetoothGattStatus.Success: ShowMessage('The operation finished successfully');
TBluetoothGattStatus.ReadNotPermitted: ShowMessage('The read operation is not permitted');
TBluetoothGattStatus.WriteNotPermitted: ShowMessage('The write operation is not permitted');
TBluetoothGattStatus.InsufficientAutentication: ShowMessage('Your authentication data is valid but it does not authorize you to perform the operation. You do not have the
required permissions');
TBluetoothGattStatus.RequestNotSupported: ShowMessage('The operation is not supported');
TBluetoothGattStatus.InvalidOffset: ShowMessage('The offset specified in your operation is not valid');
TBluetoothGattStatus.InvalidAttributeLength: ShowMessage('The length of the specified value is not valid for the target attribute');
TBluetoothGattStatus.InsufficientEncryption: ShowMessage('The encryption used for the operation is not strong enough');
TBluetoothGattStatus.Failure: ShowMessage('An unspecified error prevented the operation from succeeding.');
end;
end;
C++:
void __fastcall TForm1::YeelightBlue1ControlCharacterWriteStatus(TObject *Sender, const TBluetoothGattStatus Status);
{
switch (status) {
case TBluetoothGattStatus::Success:
ShowMessage('The operation finished successfully');
break;
case TBluetoothGattStatus::ReadNotPermittedReadNotPermitted
ShowMessage('The read operation is not permitted');
break;
case TBluetoothGattStatus::WriteNotPermitted:
ShowMessage('The write operation is not permitted');
break;
case TBluetoothGattStatus::InsufficientAutentication
ShowMessage('The read operation is not permitted');
break;
case TBluetoothGattStatus::RequestNotSupported:
ShowMessage('Your authentication data is valid but it does not authorize you to perform the operation. You do not have the required permissions');
break;
case TBluetoothGattStatus::InvalidOffset
ShowMessage('The operation is not supported');
break;
case TBluetoothGattStatus::InsufficientAutentication
ShowMessage('The offset specified in your operation is not valid');
break;
case TBluetoothGattStatus::InvalidAttributeLength:
ShowMessage('The length of the specified value is not valid for the target attribute');
break;
case TBluetoothGattStatus::InsufficientEncryption
ShowMessage('The encryption used for the operation is not strong enough');
break;
case TBluetoothGattStatus::Failure
ShowMessage('An unspecified error prevented the operation from succeeding');
break;
}
}
Error Handling
While connecting to the device, different errors may raise that you need to handle:
Error | Component | Description |
---|---|---|
OnGeneralDiscoveryError
|
TBluetoothDeviceDiscoveryManager
|
The manager can not find any nearby device from that type. The procedure also has an additional parameter, TGeneralDiscoveryErrorEvent = procedure(const Sender: TObject; const AException: Exception; var Handled: Boolean) of object;
|
OnDeviceConnectionError
|
Bluetooth LE component | The component can not find the device. |