micro:bit - BLE - FireMonkey(Delphi) 연동

OS/micro:bit 2018.01.24 23:57 Posted by 파란크리스마스

본 체험 제품은 element14 의 후원을 받아 아이씨뱅큐㈜ 에서 진행하는 무상 체험단 활동으로 작성한 것입니다.

출처 : Blog Yamasho: Delphi 10.2.2:Bleで Microbitと通信する。
googlesamples/android-BluetoothLeGatt - GitHub
ButtonService - micro:bit runtime
microbit-samples/source/examples/bluetooth-services/main.cpp - GitHub
https://github.com/lancaster-university/microbit-dal/issues/337
Advanced - micro:bit runtime - GitHub Pages
Send/Receive Data Using BLE and MBED | BBC:Microbit - Instructables

micro:bit - BLE - FireMonkey(Delphi) 연동

micro:bit에서 지원하는 BLE 서비스와 Delphi의 크로스 컴파일인 FireMonkey로 BLE 연동 하는 예제를 구현했습니다. FireMonkey는 아이폰도 지원하지만 실행 영상은 안드로이드 실행 영상을 올립니다.

컴파일 환경 - config.json

{
    "microbit-dal": {
        "bluetooth": {
            "enabled": 1,
            "pairing_mode": 1,
            "private_addressing": 0,
            "open": 1,
            "security_level": "SECURITY_MODE_ENCRYPTION_NO_MITM",
            "whitelist": 1,
            "advertising_timeout": 0,
            "tx_power": 7,
            "dfu_service": 0,
            "event_service": 1,
            "device_info_service": 1
        },
        "gatt_table_size": "0x600"
    }
}

소스 - main.cpp

#include "MicroBit.h"
#include "MicroBitUARTService.h"

MicroBit uBit;
MicroBitUARTService *uart;

// we use events abd the 'connected' variable to keep track of the status of the Bluetooth connection
void onConnected(MicroBitEvent)
{
    uBit.display.print("C");
    
    // 종료 문자열
    ManagedString eom(":");

    while(1) {
    	//ManagedString msg = uart->read(1);
        ManagedString msg = uart->readUntil(eom);
        uBit.display.scroll(msg);
    }
}

void onDisconnected(MicroBitEvent)
{
    uBit.display.print("D");
}

int main()
{
    // Initialise the micro:bit runtime.
    uBit.init();

    // Services/Pairing Config/Power Level
    uBit.display.scroll("BLE ABDILMT/P/0");

    uBit.messageBus.listen(MICROBIT_ID_BLE, MICROBIT_BLE_EVT_CONNECTED, onConnected);
    uBit.messageBus.listen(MICROBIT_ID_BLE, MICROBIT_BLE_EVT_DISCONNECTED, onDisconnected);
    
    //new MicroBitAccelerometerService(*uBit.ble, uBit.accelerometer);
    new MicroBitButtonService(*uBit.ble);
    //new MicroBitIOPinService(*uBit.ble, uBit.io);
    //new MicroBitLEDService(*uBit.ble, uBit.display);
    //new MicroBitMagnetometerService(*uBit.ble, uBit.compass);
    //new MicroBitTemperatureService(*uBit.ble, uBit.thermometer);
    
    // Note GATT table size increased from default in MicroBitConfig.h
    // #define MICROBIT_SD_GATT_TABLE_SIZE             0x500
    uart = new MicroBitUARTService(*uBit.ble, 32, 32); 
    uBit.display.scroll("UART ready");
    
    // If main exits, there may still be other fibers running or registered event handlers etc.
    // Simply release this fiber, which will mean we enter the scheduler. Worse case, we then
    // sit in the idle task forever, in a power efficient sleep.
    release_fiber();
}

Delphi 화면 디자인

Delphi 소스 - Main.pas

unit Main;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
  FMX.Objects, FMX.Edit, FMX.Controls.Presentation, System.Bluetooth,
  System.Bluetooth.Components, FMX.Layouts, FMX.ListBox;

const
 UUID_BUTTON_SERVICE          : TBluetoothUUID  = '{E95D9882-251D-470A-A062-FA1922DFA9A8}';
 UUID_BUTTON1_CHARACTERRISTIC : TBluetoothUUID  = '{E95DDA90-251D-470A-A062-FA1922DFA9A8}';
 UUID_BUTTON2_CHARACTERRISTIC : TBluetoothUUID  = '{E95DDA91-251D-470A-A062-FA1922DFA9A8}';

 UUID_UART_SERVICE            : TBluetoothUUID  = '{6E400001-B5A3-F393-E0A9-E50E24DCCA9E}';
 UUID_UART_TX_CHARACTERRISTIC : TBluetoothUUID  = '{6E400003-B5A3-F393-E0A9-E50E24DCCA9E}';

type

  TForm42 = class(TForm)
    Connect: TButton;
    Edit1: TEdit;
    Send: TButton;
    ButtonA: TCircle;
    Label1: TLabel;
    BTLableA: TLabel;
    ButtonB: TCircle;
    BtLabelB: TLabel;
    BluetoothLE1: TBluetoothLE;
    ListBox1: TListBox;
    StyleBook1: TStyleBook;
    procedure BluetoothLE1CharacteristicRead(const Sender: TObject;
      const ACharacteristic: TBluetoothGattCharacteristic;
      AGattStatus: TBluetoothGattStatus);
    procedure BluetoothLE1EndDiscoverDevices(const Sender: TObject;
      const ADeviceList: TBluetoothLEDeviceList);
    procedure BluetoothLE1EndDiscoverServices(const Sender: TObject;
      const AServiceList: TBluetoothGattServiceList);
    procedure ConnectClick(Sender: TObject);
    procedure SendClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ListBox1ItemClick(const Sender: TCustomListBox;
      const Item: TListBoxItem);
  private
    { private 먬뙻 }
  public
    { public 먬뙻 }
  end;

var
  Form42: TForm42;
  GBleDevice         : TBluetoothLEDevice;
  BackColor          : TColor;

implementation

{$R *.fmx}
{$R *.iPhone55in.fmx IOS}

procedure TForm42.BluetoothLE1CharacteristicRead(const Sender: TObject;
  const ACharacteristic: TBluetoothGattCharacteristic;
  AGattStatus: TBluetoothGattStatus);
begin
  if(ACharacteristic.UUID = UUID_BUTTON1_CHARACTERRISTIC) then
  begin
    if  ACharacteristic.GetValueAsInt8(0) <> 0 then
        ButtonA.Fill.Color := TAlphaColorRec.Red
    else
        ButtonA.Fill.Color := BackColor;
  end
  else
  if(ACharacteristic.UUID = UUID_BUTTON2_CHARACTERRISTIC) then
  begin
    if  ACharacteristic.GetValueAsInt8(0) <> 0 then
        ButtonB.Fill.Color := TAlphaColorRec.Red
    else
        ButtonB.Fill.Color := BackColor;
  end;
end;

procedure TForm42.BluetoothLE1EndDiscoverDevices(const Sender: TObject;
  const ADeviceList: TBluetoothLEDeviceList);
var
i : uint32;   // ARM 궶궻궳띍밙돸
device : TBluetoothLEDevice;
begin

  ListBox1.Clear;
  for i := 0 to ADeviceList.Count-1 do begin
    device := ADeviceList.Items[i];
    ListBox1.Items.AddObject(device.DeviceName +'/' + device.Address, device);
  end;

  {
  if ADeviceList.Count > 0 then begin
    for i := 0 to Pred(ADeviceList.Count) do begin
      if Pos('BBC micro:bit', ADeviceList[i].DeviceName) > 0 then begin
        Label1.Text := ADeviceList[i].DeviceName ;// ' (' + ADeviceList[i].Address + ')';
        BluetoothLE1.CancelDiscovery;
        GBleDevice  :=  ADeviceList[i];
        GBleDevice.DiscoverServices;
        exit;
      end;
    end;
  end;
  }
end;

procedure TForm42.BluetoothLE1EndDiscoverServices(const Sender: TObject;
  const AServiceList: TBluetoothGattServiceList);
var
  i : uint32;   // ARM 궶궻궳띍밙돸
  LGattService   : TBluetoothGattService;
begin
  LGattService := BluetoothLE1.GetService(GBleDevice, UUID_BUTTON_SERVICE);
  if LGattService <> nil then
  begin
    for i := 0 to Pred(LGattService.Characteristics.Count) do
    begin
        if LGattService.Characteristics[i].UUID = UUID_BUTTON1_CHARACTERRISTIC then
        begin
           GBleDevice.SetCharacteristicNotification(LGattService.Characteristics[i], True);
        end
        else
        if LGattService.Characteristics[i].UUID = UUID_BUTTON2_CHARACTERRISTIC then
        begin
           GBleDevice.SetCharacteristicNotification(LGattService.Characteristics[i], True);
        end;
    end;
  end;

end;

procedure TForm42.ConnectClick(Sender: TObject);
begin
  if( not BluetoothLE1.Enabled ) then
  begin
    BluetoothLE1.Enabled := True;
    BluetoothLE1.DiscoverDevices(1000);  // 10뷳
    Connect.Text := 'Disconnect';
    Label1.Text  := '';
  end
  else
  begin
    ListBox1.Clear;
    BluetoothLE1.CancelDiscovery;
    BluetoothLE1.Enabled := False;
    GBleDevice           := NIL;
    Connect.Text := 'Connect';
  end;
end;

procedure TForm42.FormCreate(Sender: TObject);
begin
    BackColor :=  ButtonA.Fill.Color;
end;

procedure TForm42.ListBox1ItemClick(const Sender: TCustomListBox; const Item: TListBoxItem);
var
  BLEDevice : TBluetoothLEDevice;
begin
  BLEDevice := Item.Data as TBluetoothLEDevice;
  Label1.Text := BLEDevice.DeviceName ;// ' (' + ADeviceList[i].Address + ')';
  BluetoothLE1.CancelDiscovery;
  GBleDevice  :=  BLEDevice;
  GBleDevice.DiscoverServices;
end;

procedure TForm42.SendClick(Sender: TObject);
var
  i : uint32;   // ARM 궶궻궳띍밙돸
  LGattService : TBluetoothGattService;
  LCharact     : TBluetoothGattCharacteristic;
begin
  if GBleDevice             = NIL   then exit;
  if GBleDevice.IsConnected = false then exit;

  LGattService := BluetoothLE1.GetService(GBleDevice, UUID_UART_SERVICE);
  if  LGattService   = NIL   then exit;
//  LCharact     :=  LGattService.GetCharacteristic(UUID_UART_TX_CHARACTERRISTIC);
  for i := 0 to Pred(LGattService.Characteristics.Count) do
  begin
    if LGattService.Characteristics[i].UUID = UUID_UART_TX_CHARACTERRISTIC then
    begin
       LGattService.Characteristics[i].SetValueAsString(Edit1.Text+':', True);
       GBleDevice.WriteCharacteristic(LGattService.Characteristics[i]);
       exit;
    end;
  end;
end;

end.

실행

예제 파일 컴파일 (참고용)

(workspace) D:\tmp>git clone https://github.com/lancaster-university/microbit-samples.git
(workspace) D:\tmp>cd microbit-samples
(workspace) D:\tmp\microbit-samples>yotta target bbc-microbit-classic-gcc
(workspace) D:\tmp\microbit-samples>copy source\examples\bluetooth-services\main.cpp source
(workspace) D:\tmp\microbit-samples>copy source\examples\bluetooth-services\config.json .
(workspace) D:\tmp\microbit-samples>yotta clean
(workspace) D:\tmp\microbit-samples>yotta build


마이크로비트 공식 구입처 : 아이씨뱅큐 http://www.icbanq.com/
엘리먼트14의 특장점 - 글로벌 전자부품 유통회사, 6만원 이상 무료배송, 60만가지 재고 보유, MOQ 없음, 한글 웹사이트, 국내서비스센터 운영 (http://kr.element14.com/?CMP=DSP-ODB-KR-JAN2018-BLOG-ICBanQ1-HOMEPAGE)

마이크로비트 공식 카페 : http://cafe.naver.com/bbcmicro
아이씨뱅큐 공식 블로그 : http://blog.naver.com/icbanq