V1とV2での実装の違い
次の記事で、micro:bitにおけるDAL(V1)とCODAL(V2)とのBLEサービス実装の違いを確認していますが、本記事では、BLEHF2Serviceサービスを例に、その違いを確認しました。
おさらい(pxt-microbitにおけるbluetoothの実装)
pxt-microbitでは、bluetoothが実装されており、ソースコードが公開されています。
https://github.com/microsoft/pxt-microbit/tree/master/libs/bluetooth
DALとCODAL
micro:bitには、大きく分けて、V1とV2とが存在し、V1では、DALを使用し、V2では、CODALを使用します。
DAL/CODALで、V1とV2の違いを吸収して共通化されているのですが、bluetoothに関しては、共通化されていません。
micro:bit ランタイムとNordic nRF5 SDK
MakeCodeで、HEX形式ファイルをビルドする際に、micro:bitランタイムやNordic nRF5 SDKといったライブラリが必要です。これらのライブラリは、micro:bitのバージョンにより異なります。
micro:bit | ランタイム | nRF5 |
---|---|---|
V1 | dal | Soft Device 110 |
V2 | codal | Soft Device 113 |
その為、拡張機能を独自に実装する際には、micro:bitの各バージョンに合わせたライブラリに対する実装が必要ですので、プリプロセッサ ディレクティブによる切り分けた実装を行います(MICROBIT_CODALがゼロ以外かどうか)。
MICROBIT_CODAL | micro:bit |
---|---|
ゼロ | V1 - DAL |
ゼロ以外 | V2 - CODAL |
BLE(Gattサーバー)の操作
ペリフェラルとしてBLEサービスを提供するために、Gattサーバーを操作しますが、V1とV2とではその実装方法が異なります。
Gattサーバーを操作する為に、V1の場合は、uBit.ble
に委譲した実装を行い、V2の場合は、MicroBitBLEService
クラスを継承した実装を行います。
uBit.bleの正体
V1/V2ともにuBit.ble
は、BLEDevice
型(ble.h
)ですが、V2におけるuBit.ble
の実体は、MicrobitBLEManager
型(クラス)です。
→ ソースコード
class MicroBitBLEManager;
typedef MicroBitBLEManager BLEDevice;
BLEHF2Service における実装の違い
pxt-microbit/bluetoothのソースコードに含まれるBLEHF2Service
サービスを例に、実装の違いを確認します。
- (github:microsoft) pxt-microbit/libs/bluetooth/BLEHF2Service.h
- (github:microsoft) pxt-microbit/libs/bluetooth/BLEHF2Service.cpp
BLEHF2Service.h - 違い
BLEHF2Service.hにおける、大きな違いは、次の2つです。
- MicroBitBLEServiceを継承しているかどうか
- Characteristicハンドルを配列で保持するかどうか
# | 項目 | (V1)DAL | (V2)CODAL |
---|---|---|---|
1 | #include "MicroBitConfig.h" | (共通) | (共通) |
2 | #include "MicroBitThermometer.h" | (共通) | (共通) |
3 | #include "EventModel.h" | (共通) | (共通) |
4 | #include "pxt.h" | (共通) | (共通) |
5 | #define HF2_ID 9501 | (共通) | (共通) |
6 | #define BLEHF2_FLAG_SERIAL_OUT 0x80 | (共通) | (共通) |
7 | #define BLEHF2_FLAG_SERIAL_ERR 0xC0 | (共通) | (共通) |
8 | #define BLEHF2_DATA_LENGTH 19 | (共通) | (共通) |
9 | extern const uint8_t BLEHF2ServiceUUID[]; | (共通) | (共通-未使用) |
10 | extern const uint8_t BLEHF2TxCharacteristicUUID[]; | (共通) | (共通-未使用) |
11 | struct BLEHF2Packet {...}; | (共通) | (共通) |
11 | #include "ble/BLE.h" | (V1) | |
11 | #include "MicroBitBLEManager.h" | (V2) | |
12 | #include "MicroBitBLEService.h" | (V2) | |
13 | 継承元 | (なし) | MicroBitBLEService |
14 | コンストラクタ | (同等) | (同等) |
15 | void sendSerial(const char *data, int len, bool isError); | (同等) | (同等) |
16 | BLEDevice &ble; | (同等) | (同等) |
17 | BLEHF2Packet txCharacteristicMessage; | (同等) | (同等) |
18 | Charactersiticハンドル等の保持 | 変数 | 継承と配列 |
BLEHF2Service.h - 共通(はじめ)
#ifndef BLE_HF2_SERVICE_H
#define BLE_HF2_SERVICE_H
#include "MicroBitConfig.h"
#include "MicroBitThermometer.h"
#include "EventModel.h"
#include "pxt.h"
#define HF2_ID 9501
#define BLEHF2_FLAG_SERIAL_OUT 0x80
#define BLEHF2_FLAG_SERIAL_ERR 0xC0
#define BLEHF2_DATA_LENGTH 19
// UUIDs for our service and characteristics
extern const uint8_t BLEHF2ServiceUUID[];
extern const uint8_t BLEHF2TxCharacteristicUUID[];
struct BLEHF2Packet {
uint8_t command;
uint8_t data[BLEHF2_DATA_LENGTH];
};
BLEHF2Service.h - (V2) CODAL
//================================================================
#if MICROBIT_CODAL
//================================================================
#include "MicroBitBLEManager.h"
#include "MicroBitBLEService.h"
class BLEHF2Service : public MicroBitBLEService
{
public:
/**
* Constructor.
* Create a representation of the TemperatureService
* @param _ble The instance of a BLE device that we're running on.
*/
BLEHF2Service(BLEDevice &_ble);
/**
* Sends text
*/
void sendSerial(const char *data, int len, bool isError);
private:
// Bluetooth stack we're running on.
BLEDevice &ble;
// memory for buffers.
BLEHF2Packet txCharacteristicMessage;
// Index for each charactersitic in arrays of handles and UUIDs
typedef enum mbbs_cIdx
{
mbbs_cIdxMESSAGE,
mbbs_cIdxCOUNT
} mbbs_cIdx;
// UUIDs for our service and characteristics
static const uint8_t service_base_uuid[ 16];
static const uint8_t char_base_uuid[ 16];
static const uint16_t serviceUUID;
static const uint16_t charUUID[ mbbs_cIdxCOUNT];
// Data for each characteristic when they are held by Soft Device.
MicroBitBLEChar chars[ mbbs_cIdxCOUNT];
public:
int characteristicCount() { return mbbs_cIdxCOUNT; };
MicroBitBLEChar *characteristicPtr( int idx) { return &chars[ idx]; };
};
BLEHF2Service.h - (V1) DAL
//================================================================
#else // MICROBIT_CODAL
//================================================================
#include "ble/BLE.h"
class BLEHF2Service
{
public:
/**
* Constructor.
* Create a representation of the TemperatureService
* @param _ble The instance of a BLE device that we're running on.
*/
BLEHF2Service(BLEDevice &_ble);
/**
* Sends text
*/
void sendSerial(const char *data, int len, bool isError);
private:
// Bluetooth stack we're running on.
BLEDevice &ble;
// memory for buffers.
BLEHF2Packet txCharacteristicMessage;
// Handles to access each characteristic when they are held by Soft Device.
GattAttribute::Handle_t txCharacteristicHandle;
};
BLEHF2Service.h - 共通(おわり)
//================================================================
#endif // MICROBIT_CODAL
//================================================================
#endif
BLEHF2Service.cpp - 違い
BLEHF2Service.cppにおける、大きな違いは、次の2つです。
- UUIDをロング(ベース)とショートで扱っているかどうか
- bleインスタンスを操作するか、継承元に委譲しているかどうか
# | 項目 | (V1)DAL | (V2)CODAL |
---|---|---|---|
1 | MicroBitConfig.h | (共通) | (共通) |
2 | BLEHF2Service.h | (共通) | (共通) |
3 | MicroBitEvent.h | (共通) | (共通) |
4 | ble/UUID.h | (V1) | |
5 | const uint8_t BLEHF2Service::service_base_uuid[]; | (V2) | |
6 | const uint8_t BLEHF2Service::char_base_uuid[]; | (V2) | |
7 | const uint16_t BLEHF2Service::serviceUUID = 0xf5e6; | (V2) | |
8 | const uint16_t BLEHF2Service::charUUID[ mbbs_cIdxCOUNT] = { 0xf5e6 }; | (V2) | |
9 | GattCharacteristic | (V1) | |
10 | GattService | (V1) | |
11 | ble.addService(service); | (V1) | |
12 | CreateService( serviceUUID); | (V2) | |
13 | CreateCharacteristic | (V2) | |
14 | 接続状況 | ble.getGapState().connected | getConnected() |
15 | notify | ble.gattServer().notify() | notifyChrValue() |
16 | const uint8_t BLEHF2ServiceUUID[]; | (V1) | |
17 | const uint8_t BLEHF2TxCharacteristicUUID[]; | (V1) |
違いの補足説明(BLEHF2Service.cpp)
【(V1) DAL】
UUIDをロング形式で定義しています。
サービスの構築のために、コンストラクタで、GattCharacteristicインスタンスやGattServiceインスタンスを生成し、bleインスタンスにサービスを追加しています。
sendSerialメソッドでは、bleインスタンスを操作し、接続状況の取得やnotify呼び出しを行っています。
【(V2) CODAL】
UUIDをロング形式のベース部分とショート形式とで定義しています。
サービス構築のために、コンストラクタで、継承元のメソッドを呼び出してサービスを追加しています。
sendSerialメソッドでも、継承元のメソッドを呼び出し、接続状況の取得やnotify呼び出しを行っています。尚、notifyChrValue()メソッドの引数では、Characteristicハンドルの値ではなく、配列変数の添え字を渡しています。
BLEHF2Service.cpp - 共通(はじめ)
#include "MicroBitConfig.h"
#include "BLEHF2Service.h"
#include "MicroBitEvent.h"
BLEHF2Service.cpp - (V2) CODAL
//================================================================
#if MICROBIT_CODAL
//================================================================
const uint8_t BLEHF2Service::service_base_uuid[ 16] =
{ 0xb1,0x12,0x00,0x00,0x26,0x79,0x30,0xda,0xa2,0x6e,0x02,0x73,0xb6,0x04,0x38,0x49 };
const uint8_t BLEHF2Service::char_base_uuid[ 16] =
{ 0xb1,0x12,0x00,0x00,0x26,0x79,0x30,0xda,0xa2,0x6e,0x02,0x73,0xb6,0x04,0x38,0x4a };
const uint16_t BLEHF2Service::serviceUUID = 0xf5e6;
const uint16_t BLEHF2Service::charUUID[ mbbs_cIdxCOUNT] = { 0xf5e6 };
BLEHF2Service::BLEHF2Service(BLEDevice &_ble) :
ble(_ble)
{
// Initialise our characteristic values.
memset(&txCharacteristicMessage, 0, sizeof(txCharacteristicMessage));
// Register the base UUID and create the service.
RegisterBaseUUID( service_base_uuid);
CreateService( serviceUUID);
RegisterBaseUUID( char_base_uuid);
CreateCharacteristic( mbbs_cIdxMESSAGE, charUUID[ mbbs_cIdxMESSAGE],
(uint8_t *)&txCharacteristicMessage,
sizeof(txCharacteristicMessage), sizeof(txCharacteristicMessage),
microbit_propNOTIFY);
}
void BLEHF2Service::sendSerial(const char *data, int len, bool isError) {
if (getConnected())
{
int32_t sent = 0;
while(sent < len) {
int32_t n = min(BLEHF2_DATA_LENGTH, len - sent);
txCharacteristicMessage.command = (isError ? BLEHF2_FLAG_SERIAL_OUT : BLEHF2_FLAG_SERIAL_ERR) | n;
memcpy(&txCharacteristicMessage.data, data + sent, n);
notifyChrValue(mbbs_cIdxMESSAGE,(uint8_t *)&txCharacteristicMessage, sizeof(txCharacteristicMessage));
sent += n;
}
}
}
BLEHF2Service.cpp - (V1) DAL
//================================================================
#else // MICROBIT_CODAL
//================================================================
#include "ble/UUID.h"
BLEHF2Service::BLEHF2Service(BLEDevice &_ble) :
ble(_ble)
{
GattCharacteristic txCharacteristic(BLEHF2TxCharacteristicUUID, (uint8_t *)&txCharacteristicMessage, 0,
sizeof(txCharacteristicMessage), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
// Initialise our characteristic values.
memset(&txCharacteristicMessage, 0, sizeof(txCharacteristicMessage));
// Set default security requirements
txCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
// setup GATT table
GattCharacteristic *characteristics[] = {&txCharacteristic};
GattService service(BLEHF2ServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));
ble.addService(service);
// retreive handles
txCharacteristicHandle = txCharacteristic.getValueHandle();
// initialize data
ble.gattServer().write(txCharacteristicHandle,(uint8_t *)&txCharacteristicMessage, sizeof(txCharacteristicMessage));
}
void BLEHF2Service::sendSerial(const char *data, int len, bool isError) {
if (ble.getGapState().connected)
{
int32_t sent = 0;
while(sent < len) {
int32_t n = min(BLEHF2_DATA_LENGTH, len - sent);
txCharacteristicMessage.command = (isError ? BLEHF2_FLAG_SERIAL_OUT : BLEHF2_FLAG_SERIAL_ERR) | n;
memcpy(&txCharacteristicMessage.data, data + sent, n);
ble.gattServer().notify(txCharacteristicHandle,(uint8_t *)&txCharacteristicMessage, sizeof(txCharacteristicMessage));
sent += n;
}
}
}
const uint8_t BLEHF2ServiceUUID[] = {
0xb1,0x12,0xf5,0xe6,0x26,0x79,0x30,0xda,0xa2,0x6e,0x02,0x73,0xb6,0x04,0x38,0x49
};
const uint8_t BLEHF2TxCharacteristicUUID[] = {
0xb1,0x12,0xf5,0xe6,0x26,0x79,0x30,0xda,0xa2,0x6e,0x02,0x73,0xb6,0x04,0x38,0x4a
};
BLEHF2Service.cpp - 共通(おわり)
//================================================================
#endif // MICROBIT_CODAL
//================================================================
まとめ
BLEHF2Serviceサービスの実装においても、Gattサーバーを操作する為に、V1の場合は、uBit.ble
に委譲した実装を行い、V2の場合は、MicroBitBLEService
クラスを継承した実装を行っていることを確認しました。