MakeCodeでmicro:bitのBLEサービスを実装したい：インサイド MakeCode for micro:bit - bluetooth編

# micro:bitでBluetooth

micro:bitのMakeCodeには、Bluetoothのブロックが標準(の拡張機能)で用意されています。これらのBLEサービス（ペリフェラル）を使ったプログラミングが簡単にできます。また、MakeCodeでは、独自の拡張機能を開発することが可能です。もちろん、標準(の拡張機能)にはないBLEサービスの開発と追加も可能です。

また、micro:bitには、V1とV2の２つのバージョンがあり、MakeCodeの内部で使用されているライブラリも、dalとcodalとに分かれています。それぞれのライブラリに則った設計と実装を行えば、独自のBLEサービスの実装も容易に行えます。


## Bluetooth 拡張機能（標準）

標準（の拡張機能）で、次のBLEサービスのブロックが用意されています。
[MakeCode for micro:bit エディタ](https://makecode.microbit.org/#editor) を開き、拡張機能の追加で、"bluetooth"をキーワードに検索して、[「bluetooth」](https://makecode.microbit.org/reference/bluetooth)を追加すると利用できます（"Radio"との共存はできません）。

1. [**Bluetooth** 加速度計サービス](https://makecode.microbit.org/reference/bluetooth/start-accelerometer-service)
1. [**Bluetooth** ボタンサービス](https://makecode.microbit.org/reference/bluetooth/start-button-service)
1. [**Bluetooth** 入出力端子サービス](https://makecode.microbit.org/reference/bluetooth/start-io-pin-service)
1. [**Bluetooth** LEDサービス](https://makecode.microbit.org/reference/bluetooth/start-led-service)
1. [**Bluetooth** 温度計サービス](https://makecode.microbit.org/reference/bluetooth/start-temperature-service)
1. [**Bluetooth** 磁力計サービス](https://makecode.microbit.org/reference/bluetooth/start-magnetometer-service)
1. [**Bluetooth その他** UARTサービス](https://makecode.microbit.org/reference/bluetooth/start-uart-service)

BLEサービスに関連するブロックも用意されています。

1. [**Bluetooth** 接続されたとき](https://makecode.microbit.org/reference/bluetooth/on-bluetooth-connected)
1. [**Bluetooth** 接続が切断されたとき](https://makecode.microbit.org/reference/bluetooth/on-bluetooth-disconnected)
1. [**Bluetooth** データを受信したとき](https://makecode.microbit.org/reference/bluetooth/on-uart-data-received)
1. [**Bluetooth その他** UART 数値を文字で書き出す](https://makecode.microbit.org/reference/bluetooth/uart-write-number)
1. [**Bluetooth その他** UART 文字列を書き出す](https://makecode.microbit.org/reference/bluetooth/uart-write-string)
1. [**Bluetooth その他** UART write line](https://makecode.microbit.org/reference/bluetooth/uart-write-line)
1. [**Bluetooth その他** UART 名前と値を書き出す](https://makecode.microbit.org/reference/bluetooth/uart-write-value)
1. [**Bluetooth その他** UART つぎのいずれかの文字の手前まで読み取る](https://makecode.microbit.org/reference/bluetooth/uart-read-until)
1. [**Bluetooth その他** 送信強度を設定](https://makecode.microbit.org/reference/bluetooth/set-transmit-power)

これらを含むbluetoothに関する仕様は、[プロファイル（Bluetooth Developer Studio Level 3 Profile Report）](https://lancaster-university.github.io/microbit-docs/resources/bluetooth/bluetooth_profile.html)として、まとめられています。

## 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 |


``` custom.h

#include "pxt.h"

//================================================================
#if MICROBIT_CODAL
//================================================================
// for V2 - codal
#include "MicroBitBLEManager.h"
#include "MicroBitBLEService.h"



//================================================================
#else // MICROBIT_CODAL
//================================================================
// for V1 - codal
#include "ble/BLE.h"



//================================================================
#endif // MICROBIT_CODAL
//================================================================

```

# Bluetooth拡張機能の実装（の内部）

Bluetooth 拡張機能のソースコードは、GitHubで公開されていますので、V1とV2の実装を確認することができます。

**micro:bit V1** [lancaster-university/microbit-dal](https://github.com/lancaster-university/microbit-dal.git)
**micro:bit V2** [lancaster-university/codal-microbit-v2](https://github.com/lancaster-university/codal-microbit-v2.git)

## 継承元

各BLEサービスのヘッダーファイル(`inc/bluetooth/MicroBit*Service.h`)で、継承元を確認できます。
**V2**の場合は、`MicroBitBLEService`クラスを継承しています。**V2**では、この`MicroBitBLEService`クラスがBLEサービス（ペリフェラル）に関する実装を提供している為です(**V1**の場合はuBit.bleに処理を委譲)。
また、**入出力端子サービス**のみ、`MicroBitComponent`クラスを継承してますが、`idleTick()`(V1)や`idleCallback()`(V2)を実装する為です。

|BLEサービス       |V1        |V2        |
|-----------------|----------|----------|
|加速度計サービス  | [(なし)](https://github.com/lancaster-university/microbit-dal/blob/master/inc/bluetooth/MicroBitAccelerometerService.h#L44) | [MicroBitBLEService](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitAccelerometerService.h#L43)
|ボタンサービス    | [(なし)](https://github.com/lancaster-university/microbit-dal/blob/master/inc/bluetooth/MicroBitButtonService.h#L43) |[MicroBitBLEService](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitButtonService.h#L42)|
|入出力端子サービス |[MicroBitComponent](https://github.com/lancaster-university/microbit-dal/blob/master/inc/bluetooth/MicroBitIOPinService.h#L68)|[MicroBitBLEService, MicroBitComponent](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitIOPinService.h#L64)|
|LEDサービス      |[(なし)](https://github.com/lancaster-university/microbit-dal/blob/master/inc/bluetooth/MicroBitLEDService.h#L48)|[MicroBitBLEService](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitLEDService.h#L46)|
|温度計サービス    |[(なし)](https://github.com/lancaster-university/microbit-dal/blob/master/inc/bluetooth/MicroBitTemperatureService.h#L44)|[MicroBitBLEService](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitTemperatureService.h#L43)|
|磁力計サービス    |[(なし)](https://github.com/lancaster-university/microbit-dal/blob/master/inc/bluetooth/MicroBitMagnetometerService.h#L50)|[MicroBitBLEService](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitMagnetometerService.h#L48)|
|UARTサービス     |[(なし)](https://github.com/lancaster-university/microbit-dal/blob/master/inc/bluetooth/MicroBitUARTService.h#L46)|[MicroBitBLEService](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitUARTService.h#L48)|

## idleコールバック

**入出力端子サービス**だけが、その継承元を`MicroBitComponent`クラスとしていますが、これは、MicroBitスケジューラからの定期的なコールバックを実装する為です。
**V1**の場合は、`virtual void MicroBitComponent::idleTick()`を[オーバーライド](https://github.com/lancaster-university/microbit-dal/blob/master/inc/bluetooth/MicroBitIOPinService.h#L87)し、**V2**の場合は、`virtual void MicroBitComponent::idleCallback()`を[オーバーライド](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitIOPinService.h#L83)しています。
両者ともにその中で、同名の`updateBLEInputs()`メソッドを呼び出すことによって、ピンの値を取得し、その値に変化があれば、BLEで、データ通知（Notify）しています。


**V1** - [idleTick()の実装内容](https://github.com/lancaster-university/microbit-dal/blob/master/source/bluetooth/MicroBitIOPinService.cpp#L268)

``` microbit-dal/source/bluetooth/MicroBitIOPinService.cpp
// (L.L.268-278)

/**
 * Periodic callback from MicroBit scheduler.
 *
 * Check if any of the pins we're watching need updating. Notify any connected
 * device with any changes.
 */
void MicroBitIOPinService::idleTick()
{
    if (ble.getGapState().connected)
        updateBLEInputs();
}

```

**V2** - [idleCallback()の実装内容](https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/bluetooth/MicroBitIOPinService.cpp#L282)

```codal-microbit-v2/source/bluetooth/MicroBitIOPinService.cpp
// (L.L.282-299)

/**
 * Periodic callback from MicroBit scheduler.
 *
 * Check if any of the pins we're watching need updating. Notify any connected
 * device with any changes.
 */
void MicroBitIOPinService::idleCallback()
{
    if ( getConnected())
    {
        int pairs = updateBLEInputs( false);
        // If there's any data, issue a BLE notification.
        if ( pairs)
        {
            notifyChrValue( mbbs_cIdxDATA, (uint8_t *)ioPinServiceDataCharacteristicBuffer, pairs * sizeof(IOData));
        }
    }
}
```

## idleコールバックの開始と停止

`idleTick()`や`idleCallback()`をオーバーライドしただけでは、コールバックされません。
コールバックを開始するには`fiber_add_idle_component(MicroBitComponent * component)`を、停止するには`fiber_remove_idle_component(MicroBitComponent * component)`もしくは`CodalComponent::removeComponent()`を呼び出します。

実際には、コンストラクタ内で`fiber_add_idle_component(this);`と呼び出しています([V1](https://github.com/lancaster-university/microbit-dal/blob/master/source/bluetooth/MicroBitIOPinService.cpp#L85)/[V2](https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/bluetooth/MicroBitIOPinService.cpp#L84))。

### V1での呼び出し元

`idleTick()`メソッドは、[MicroBitFiber.h](https://github.com/lancaster-university/microbit-dal/blob/master/inc/core/MicroBitFiber.h)/[MicroBitFiber.cpp](https://github.com/lancaster-university/microbit-dal/blob/master/source/core/MicroBitFiber.cpp)から呼び出されます。

|項目|メソッド|説明|
|----|--------|----|
|開始|[int fiber_add_idle_component(MicroBitComponent *component);](https://github.com/lancaster-university/microbit-dal/blob/master/inc/core/MicroBitFiber.h#L340)|idleThreadComponents[]配列でインスタンスを保持します。|
|呼出元|[void idle();](https://github.com/lancaster-university/microbit-dal/blob/master/inc/core/MicroBitFiber.h#L327)|idleThreadComponents[]配列で保持するインスタンスのidleTick()メソッドを呼び出します。|
|停止|[int fiber_remove_idle_component(MicroBitComponent *component);](https://github.com/lancaster-university/microbit-dal/blob/master/inc/core/MicroBitFiber.h#L349)|idleThreadComponents[]配列からインスタンスを除外します。|


### V2での呼び出し元

`idleCallback()`メソッドは、[CodalComponent.h](https://github.com/lancaster-university/codal-core/blob/master/inc/core/CodalComponent.h)/[CodalComponent.cpp](https://github.com/lancaster-university/codal-core/blob/master/source/core/CodalComponent.cpp)から呼び出されます。
尚、`codal::CodalComponent`クラスは、[`typedef codal::CodalComponent MicroBitComponent;`](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/compat/MicroBitCompat.h#L71)によって、別名の`MicroBitComponent`として定義されています。
→ [inc/compat/MicroBitCompat.h](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/compat/MicroBitCompat.h#L71)

|項目|メソッド|説明|
|----|--------|----|
|開始|[void fiber_add_idle_component(CodalComponent *c);](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/compat/MicroBitCompat.h#L400)|MicroBitComponentクラスのstatusメンバー変数で、DEVICE_COMPONENT_STATUS_IDLE_TICKビットフラグを立てます。|
|呼出元|[void component_callback(Event evt);](https://github.com/lancaster-university/codal-core/blob/master/source/core/CodalComponent.cpp#L40)|statusメンバー変数のDEVICE_COMPONENT_STATUS_IDLE_TICKビットフラグが立っているインスタンスのidleCollback()メソッドを呼び出します。|
|解除|(なし)|インスタンスの破棄で除外され(`void CodalComponent::removeComponent()`)、呼び出しが停止します。|


## コンストラクタの引数

各BLEサービスのヘッダーファイル(`inc/bluetooth/MicroBit*Service.h`)で、コンストラクタの引数を確認できます。
全てのサービスで`BLEDevice`インスタンスの参照を渡していますが、**V2**のクラス内部では、メンバー変数への保持も、アクセスもありません。これは単に引数の数と順序を統一しているだけのようです。統一されていることで、利用元の一つであるpxt-microbitのbluetoothブロックでは、バージョンの違いを切り分ける必要がありません([bluetooth.cpp](https://github.com/microsoft/pxt-microbit/blob/master/libs/bluetooth/bluetooth.cpp))。
**ボタンサービス**と**UARTサービス**以外は、センサー・ドライバのインスタンスの参照を渡しています。
**ボタンサービス**では、ボタンA/Bのイベントをクラス内で`listen`しています（[V1](https://github.com/lancaster-university/microbit-dal/blob/master/source/bluetooth/MicroBitButtonService.cpp#L71)/[V2](https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/bluetooth/MicroBitButtonService.cpp#L76)）。
**UARTサービス**では、送受信バッファの既定値を変更できるようにしています（ボタンやセンサーを使用しません）。

|BLEサービス       |V1        |V2        |
|-----------------|----------|----------|
|加速度計サービス  |[MicroBitAccelerometerService(BLEDevice &_ble, MicroBitAccelerometer &_acclerometer);](https://github.com/lancaster-university/microbit-dal/blob/master/inc/bluetooth/MicroBitAccelerometerService.h#L54)|[MicroBitAccelerometerService( BLEDevice &_ble, codal::Accelerometer &_accelerometer);](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitAccelerometerService.h#L52)|
|ボタンサービス    |[MicroBitButtonService(BLEDevice &_ble);](https://github.com/lancaster-university/microbit-dal/blob/master/inc/bluetooth/MicroBitButtonService.h#L52)|[MicroBitButtonService( BLEDevice &_ble);](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitButtonService.h#L51)|
|入出力端子サービス |[MicroBitIOPinService(BLEDevice &_ble, MicroBitIO &_io);](https://github.com/lancaster-university/microbit-dal/blob/master/inc/bluetooth/MicroBitIOPinService.h#L79)|[MicroBitIOPinService(BLEDevice &_ble, MicroBitIO &_io);](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitIOPinService.h#L75)|
|LEDサービス      |[MicroBitLEDService(BLEDevice &_ble, MicroBitDisplay &_display);](https://github.com/lancaster-university/microbit-dal/blob/master/inc/bluetooth/MicroBitLEDService.h#L58)|[MicroBitLEDService( BLEDevice &_ble, MicroBitDisplay &_display);](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitLEDService.h#L56)|
|温度計サービス    |[MicroBitTemperatureService(BLEDevice &_ble, MicroBitThermometer &_thermometer);](https://github.com/lancaster-university/microbit-dal/blob/master/inc/bluetooth/MicroBitTemperatureService.h#L54)|[MicroBitTemperatureService( BLEDevice &_ble, MicroBitThermometer &_thermometer);](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitTemperatureService.h#L52)|
|磁力計サービス    |[MicroBitMagnetometerService(BLEDevice &_ble, MicroBitCompass &_compass);](https://github.com/lancaster-university/microbit-dal/blob/master/inc/bluetooth/MicroBitMagnetometerService.h#L60)|[MicroBitMagnetometerService(BLEDevice &_ble, codal::Compass &_compass);](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitMagnetometerService.h#L58)|
|UARTサービス     |[MicroBitUARTService(BLEDevice &_ble, uint8_t rxBufferSize = MICROBIT_UART_S_DEFAULT_BUF_SIZE, uint8_t txBufferSize = MICROBIT_UART_S_DEFAULT_BUF_SIZE);](https://github.com/lancaster-university/microbit-dal/blob/master/inc/bluetooth/MicroBitUARTService.h#L98)|[MicroBitUARTService(BLEDevice &_ble, uint8_t rxBufferSize = MICROBIT_UART_S_DEFAULT_BUF_SIZE, uint8_t txBufferSize = MICROBIT_UART_S_DEFAULT_BUF_SIZE);](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitUARTService.h#L122)|

## BLE(Gattサーバー)の操作

ペリフェラルとしてBLEサービスを提供するために、Gattサーバーを操作しますが、V1とV2とではその実装方法が異なります。
Gattサーバーを操作する為に、V1の場合は、`uBit.ble`に委譲した実装を行い、V2の場合は、`MicroBitBLEService`クラスを継承した実装を行います。

### サービスとキャラクタスティックの構築（コンストラクタ）

コンストラクタで、サービスとキャラクタスティックを構築しています。
V1の場合は、キャラクタスティックのハンドルを個々に保持しますが、V2の場合は、配列として保持し、その添え字でアクセスします。

|BLEサービス       |V1        |V2        |
|-----------------|----------|----------|
|加速度計サービス  |[MicroBitAccelerometerService](https://github.com/lancaster-university/microbit-dal/blob/master/source/bluetooth/MicroBitAccelerometerService.cpp#L35)|[MicroBitAccelerometerService](https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/bluetooth/MicroBitAccelerometerService.cpp#L41)|
|ボタンサービス    |[MicroBitButtonService](https://github.com/lancaster-university/microbit-dal/blob/master/source/bluetooth/MicroBitButtonService.cpp#L36)|[MicroBitButtonService](https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/bluetooth/MicroBitButtonService.cpp#L40)|
|入出力端子サービス |[MicroBitIOPinService](https://github.com/lancaster-university/microbit-dal/blob/master/source/bluetooth/MicroBitIOPinService.cpp#L36)|[MicroBitIOPinService](https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/bluetooth/MicroBitIOPinService.cpp#L41)|
|LEDサービス      |[MicroBitLEDService](https://github.com/lancaster-university/microbit-dal/blob/master/source/bluetooth/MicroBitLEDService.cpp#L35)|[MicroBitLEDService](https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/bluetooth/MicroBitLEDService.cpp#L40)|
|温度計サービス    |[MicroBitTemperatureService](https://github.com/lancaster-university/microbit-dal/blob/master/source/bluetooth/MicroBitTemperatureService.cpp#L35)|[MicroBitTemperatureService](https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/bluetooth/MicroBitTemperatureService.cpp#L41)|
|磁力計サービス    |[MicroBitMagnetometerService](https://github.com/lancaster-university/microbit-dal/blob/master/source/bluetooth/MicroBitMagnetometerService.cpp#L35)|[MicroBitMagnetometerService](https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/bluetooth/MicroBitMagnetometerService.cpp#L42)|
|UARTサービス     |[MicroBitUARTService](https://github.com/lancaster-university/microbit-dal/blob/master/source/bluetooth/MicroBitUARTService.cpp#L57)|[MicroBitUARTService](https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/bluetooth/MicroBitUARTService.cpp#L66)|

**UARTサービス (V1) のコンストラクタ**

```microbit-dal/source/bluetooth/MicroBitUARTService.cpp

/**
 * Constructor for the UARTService.
 * @param _ble an instance of BLEDevice
 * @param rxBufferSize the size of the rxBuffer
 * @param txBufferSize the size of the txBuffer
 *
 * @note defaults to 20
 */
MicroBitUARTService::MicroBitUARTService(BLEDevice &_ble, uint8_t rxBufferSize, uint8_t txBufferSize) : ble(_ble)
{
    rxBufferSize += 1;
    txBufferSize += 1;

    txBuffer = (uint8_t *)malloc(txBufferSize);
    rxBuffer = (uint8_t *)malloc(rxBufferSize);

    rxBufferHead = 0;
    rxBufferTail = 0;
    this->rxBufferSize = rxBufferSize;

    txBufferHead = 0;
    txBufferTail = 0;
    this->txBufferSize = txBufferSize;

    GattCharacteristic rxCharacteristic(UARTServiceRXCharacteristicUUID, rxBuffer, 1, rxBufferSize, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);

    txCharacteristic = new GattCharacteristic(UARTServiceTXCharacteristicUUID, txBuffer, 1, txBufferSize, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE);

    GattCharacteristic *charTable[] = {txCharacteristic, &rxCharacteristic};

    GattService uartService(UARTServiceUUID, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));

    _ble.addService(uartService);

    this->rxCharacteristicHandle = rxCharacteristic.getValueAttribute().getHandle();

    _ble.gattServer().onDataWritten(this, &MicroBitUARTService::onDataWritten);
    _ble.gattServer().onConfirmationReceived(on_confirmation);
}

```

**UARTサービス (V2) のコンストラクタ**

```codal-microbit-v2/source/bluetooth/MicroBitUARTService.cpp

/**
 * Constructor for the UARTService.
 * @param _ble an instance of BLEDevice
 * @param rxBufferSize the size of the rxBuffer
 * @param txBufferSize the size of the txBuffer
 *
 * @note defaults to 20
 */
MicroBitUARTService::MicroBitUARTService(BLEDevice &_ble, uint8_t rxBufferSize, uint8_t txBufferSize)
{
    // Initialise our characteristic values.
    txBufferHead = 0;
    txBufferTail = 0;
    
    rxBufferSize += 1;
    txBufferSize += 1;

    // Allocate memory for rxBuffer, rx characteristic, txBuffer, tx characteristic
    int size = rxBufferSize + txBufferSize + 2 * MICROBIT_UART_S_ATTRSIZE;
    rxBuffer = (uint8_t *)malloc(size);
    txBuffer = rxBuffer + rxBufferSize + MICROBIT_UART_S_ATTRSIZE;
    
    memclr( rxBuffer, size);

    rxBufferHead = 0;
    rxBufferTail = 0;
    this->rxBufferSize = rxBufferSize;

    txBufferHead = 0;
    txBufferTail = 0;
    this->txBufferSize = txBufferSize;

    txValueSize  = 0;

    waitingForEmpty = false;

    // Register the base UUID and create the service.
    RegisterBaseUUID( base_uuid);
    CreateService( serviceUUID);

    // Create the data structures that represent each of our characteristics in Soft Device.
    CreateCharacteristic( mbbs_cIdxRX, charUUID[ mbbs_cIdxRX],
                          rxBuffer + rxBufferSize,
                          0, MICROBIT_UART_S_ATTRSIZE,
                          microbit_propWRITE | microbit_propWRITE_WITHOUT);

    CreateCharacteristic( mbbs_cIdxTX, charUUID[ mbbs_cIdxTX],
                          txBuffer + txBufferSize,
                          0, MICROBIT_UART_S_ATTRSIZE,
                          microbit_propINDICATE);
}

```



### キャラクタスティックとそのプロパティ

各コンストラクタでは、サービスの仕様に合わせて、キャラクタスティックとそのプロパティを指定しています。
**READAUTH**プロパティに関しては、V1とV2とで仕様が異なりますので、詳細は「[onDataRead()](#ondataread)」の章を参照してください。

| # |プロパティ|V1 |V2 |
|---|---------|---|---|
| 1 |READ|GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ|microbit_propREAD|
| 2 |READAUTH|(setReadAuthorizationCallback)|microbit_propREADAUTH|
| 3 |NOTIFY|GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY|microbit_propNOTIFY|
| 4 |WRITE|GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE|microbit_propWRITE|
| 5 |WRITE_WITHOUT|GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE|microbit_propWRITE_WITHOUT|
| 6 |INDICATE|GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE|microbit_propINDICATE|


**加速度計サービス - MicroBitAccelerometerService**
- *(V1) accelerometerData*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY
- *(V1) accelerometerPeriod*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE
- *(V2) mbbs_cIdxDATA*
       microbit_propREAD | microbit_propNOTIFY
- *(V2) mbbs_cIdxPERIOD*
       microbit_propREAD | microbit_propWRITE

**ボタンサービス - MicroBitButtonService**
- *(V1) buttonAData*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY
- *(V1) buttonBData*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY
- *(V2) mbbs_cIdxA*
       microbit_propREAD | microbit_propNOTIFY
- *(V2) mbbs_cIdxB*
       microbit_propREAD | microbit_propNOTIFY

**入出力端子サービス - MicroBitIOPinService**
- *(V1) ioPinServiceAD*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE
- *(V1) ioPinServiceIO*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE
- *(V1) ioPinServicePWM*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE
- *(V1) ioPinServiceData*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE
       | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY
- *(V2) mbbs_cIdxADC*
       microbit_propREAD | microbit_propWRITE
- *(V2) mbbs_cIdxIO*
       microbit_propREAD | microbit_propWRITE
- *(V2) mbbs_cIdxPWM*
       microbit_propWRITE
- *(V2) mbbs_cIdxDATA*
       microbit_propREAD | microbit_propWRITE
       | microbit_propNOTIFY **| microbit_propREADAUTH**

**LEDサービス - MicroBitLEDService**
- *(V1) matrix*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ
- *(V1) text*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE
- *(V1) scrollingSpeed*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ
- *(V2) mbbs_cIdxMATRIX*
       microbit_propWRITE | microbit_propREAD **| microbit_propREADAUTH**
- *(V2) mbbs_cIdxTEXT*
       microbit_propWRITE
- *(V2) mbbs_cIdxSPEED*
       microbit_propWRITE | microbit_propREAD


**温度計サービス - MicroBitTemperatureService**
- *(V1) temperatureData*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY
- *(V1) temperaturePeriod*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE
- *(V2) mbbs_cIdxDATA*
       microbit_propREAD | microbit_propNOTIFY
- *(V2) mbbs_cIdxPERIOD*
       microbit_propREAD | microbit_propWRITE

**磁力計サービス - MicroBitMagnetometerService**
- *(V1) magnetometerData*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY
- *(V1) magnetometerBearing*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY
- *(V1) magnetometerPeriod*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE
- *(V1) magnetometerCalibration*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY
- *(V2) mbbs_cIdxDATA*
       microbit_propREAD | microbit_propNOTIFY
- *(V2) mbbs_cIdxBEARING*
       microbit_propREAD | microbit_propNOTIFY
- *(V2) mbbs_cIdxPERIOD*
       microbit_propREAD | microbit_propWRITE
- *(V2) mbbs_cIdxCALIB*
       microbit_propWRITE | microbit_propNOTIFY

**UARTサービス - MicroBitUARTService**
- *(V1) rx*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE
       | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE
- *(V1) tx*
       GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE
- *(V2) mbbs_cIdxRX*
       microbit_propWRITE
       | microbit_propWRITE_WITHOUT
- *(V2) mbbs_cIdxTX*
       microbit_propINDICATE

### Gattサーバーからのコールバック

各サービスでは、Gattサーバーからのコールバック関数が実装されています。
V1の場合は、キャラクタスティックのハンドルをメンバー変数ので保持していますので、その値とパラメータの`handle`値とを比較して、キャラクタスティックを特定します。V2の場合は、ヘルパー関数`valueHandle()`でハンドル値に変換して、特定します。

|BLEサービス       |onDataRead|onConfirmationReceived|onDataWritten|
|-----------------|----------|----------------------|-------------|
|加速度計サービス  |||onDataWritten|
|ボタンサービス    ||||
|入出力端子サービス |onDataRead||onDataWritten|
|LEDサービス      |onDataRead||onDataWritten|
|温度計サービス    |||onDataWritten|
|磁力計サービス    |||onDataWritten|
|UARTサービス     ||(V1)on_confirmation/(V2)onConfirmation|onDataWritten|

:::note info

**valueHandleの使用例(V2)**

```codal-microbit-v2/source/bluetooth/MicroBitUARTService.cpp
/**
  * A callback function for whenever a Bluetooth device writes to our RX characteristic.
  */
void MicroBitUARTService::onDataWritten(const microbit_ble_evt_write_t *params)
{
    if (params->handle == valueHandle( mbbs_cIdxRX))
    {
        uint16_t bytesWritten = params->len;
        
        // ...
        
    }
}
```
:::

#### onDataRead()

**入出力端子サービス**と**LEDサービス**では、`onDataRead()`コールバック関数が実装されています。
入力値を最新化し、更新しています。

**V1の場合**
対象のキャラクタスティックの`setReadAuthorizationCallback`関数によって、コールバック関数(`onDataRead()`)を登録します。V2のようなプロパティは用意されていませんので、プロパティの指定は不要です。

``` 
void MicroBitXxxxService::onDataRead(GattReadAuthCallbackParams *params){}
```

``` microbit-dal/source/bluetooth/MicroBitIOPinService.cpp
    ioPinServiceDataCharacteristic->setReadAuthorizationCallback(this, &MicroBitIOPinService::onDataRead);
```

``` microbit-dal/source/bluetooth/MicroBitLEDService.cpp
    matrixCharacteristic.setReadAuthorizationCallback(this, &MicroBitLEDService::onDataRead);
```

**V2の場合**
対象のキャラクタスティックのプロパティに`microbit_propREADAUTH`を追加し、`onDataRead()`をオーバーライドします。Gattサーバーから`onDataRead()`が呼び出されます。

```codal-microbit-v2/inc/MicroBitBLEService.h
    virtual void onAuthorizeRead(       const microbit_ble_evt_t *p_ble_evt);
```

#### onDataWritten()

**ボタンサービス**以外のサービスでは、`onDataWritten()`コールバック関数が実装されています。
セントラルからの書き込みに対して、処理しています。

**V1の場合**
コールバック関数(`onDataWritten()`)を実装し、Gattサーバーの`onDataWritten`関数で、コールバック関数(`onDataWritten()`)を登録します。

```
void MicroBitXxxService::onDataWritten(const GattWriteCallbackParams *params) {}
```

```microbit-dal/source/bluetooth/MicroBitUARTService.cpp
    _ble.gattServer().onDataWritten(this, &MicroBitUARTService::onDataWritten);
```

**V2の場合**

`onDataWritten()`関数をオーバーライドします。Gattサーバーから`onDataWritten()`が呼び出されます。

```codal-microbit-v2/source/bluetooth/MicroBitUARTService.cpp

/**
  * A callback function for whenever a Bluetooth device writes to our RX characteristic.
  */
void MicroBitUARTService::onDataWritten(const microbit_ble_evt_write_t *params)
{
    // ...
}
```

#### onConfirmationReceived()

**UARTサービス**には、`onConfirmationReceived()`コールバック関数が実装されています。
TXバッファを空にし、イベント(`MICROBIT_ID_NOTIFY`, `MICROBIT_UART_S_EVT_TX_EMPTY`)を生成しています。

**V1の場合**
コールバック関数(`on_confirmation()`)を実装し、Gattサーバーの`onConfirmationReceived`関数で、コールバック関数(`on_confirmation()`)を登録します。
尚、`on_confirmation()`は、`MicroBitUARTService`クラスのメンバー関数ではありません。

```microbit-dal/source/bluetooth/MicroBitUARTService.cpp

/**
  * A callback function for whenever a Bluetooth device consumes our TX Buffer
  */
void on_confirmation(uint16_t handle)
{
    if(handle == txCharacteristic->getValueAttribute().getHandle())
    {
        txBufferTail = txBufferHead;
        MicroBitEvent(MICROBIT_ID_NOTIFY, MICROBIT_UART_S_EVT_TX_EMPTY);
    }
}

```

```microbit-dal/source/bluetooth/MicroBitUARTService.cpp
    _ble.gattServer().onConfirmationReceived(on_confirmation);
```

**V2の場合**

`onConfirmation()`関数をオーバーライドします。Gattサーバーから`onConfirmation()`が呼び出されます。

```codal-microbit-v2/source/bluetooth/MicroBitUARTService.cpp

/**
  * A callback function for whenever a Bluetooth device consumes our TX Buffer
  */
void MicroBitUARTService::onConfirmation( const microbit_ble_evt_hvc_t *params)
{
    if ( params->handle == valueHandle( mbbs_cIdxTX))
    {
        txBufferTail = ( (int)txBufferTail + txValueSize) % txBufferSize;
        txValueSize = 0;
        bool async = !waitingForEmpty;
        MicroBitEvent(MICROBIT_ID_NOTIFY, MICROBIT_UART_S_EVT_TX_EMPTY);
        if ( async)
            sendNext();
    }
}

```

## MakeCodeのBluetoothブロック

MakeCodeのBluetoothブロックのソースコードは、[microsoft/pxt-microbit](https://github.com/microsoft/pxt-microbit.git)の[pxt-microbit/libs/bluetooth/](https://github.com/microsoft/pxt-microbit/tree/master/libs/bluetooth)にあり、[bluettoth.ts](https://github.com/microsoft/pxt-microbit/blob/master/libs/bluetooth/bluetooth.ts)または[bluetooth.cpp](https://github.com/microsoft/pxt-microbit/blob/master/libs/bluetooth/bluetooth.cpp)で定義・実装されています。
[lancaster-university/microbit-dal](https://github.com/lancaster-university/microbit-dal.git)や[lancaster-university/codal-microbit-v2](https://github.com/lancaster-university/codal-microbit-v2.git)で実装されているクラス（ライブラリ）が、[bluetooth.cpp](https://github.com/microsoft/pxt-microbit/blob/master/libs/bluetooth/bluetooth.cpp)から呼び出されています。


### サービスの開始（追加）

`startXxxService`関数でサービスを追加し、開始することができます。単にクラスのインスタンスを生成するだけで、サービスが有効化される仕組みになっています。

|サービス開始|bluetooth.ts |bluetooth.cpp|
|--------|---|---|
|加速度計サービス| | void startAccelerometerService()|
|ボタンサービス| | void startButtonService()|
|入出力端子サービス| | void startIOPinService()|
|LEDサービス| | void startLEDService()|
|温度計サービス| | void startTemperatureService()|
|磁力計サービス| | void startMagnetometerService()|
|UARTサービス| | void startUartService()|

**UARTサービス**に関しては、他のブロックで、インスタンスを操作するために、インスタンス変数をファイル内で保持しています。

```pxt-microbit/libs/bluetooth/bluetooth.cpp
namespace bluetooth {
    MicroBitUARTService *uart = NULL;
    // ...

    /**
    *  Starts the Bluetooth UART service
    */
    //% help=bluetooth/start-uart-service
    //% blockId=bluetooth_start_uart_service block="bluetooth uart service"
    //% parts="bluetooth" advanced=true
    void startUartService() {
        if (uart) return;
        // 61 octet buffer size is 3 x (MTU - 3) + 1
        // MTU on nRF51822 is 23 octets. 3 are used by Attribute Protocol header data leaving 20 octets for payload
        // So we allow a RX buffer that can contain 3 x max length messages plus one octet for a terminator character
        uart = new MicroBitUARTService(*uBit.ble, 61, 60);
    }

    // ...
}
```

### Bluetoothのブロック

Bluetooth共通のブロックとして、次の3つがあります。

|ブロック|bluetooth.ts |bluetooth.cpp|
|--------|---|---|
|接続されたとき| | void onBluetoothConnected(Action body)|
|接続が切断されたとき| | void onBluetoothDisconnected(Action body)|
|送信強度を設定| | void setTransmitPower(int power)|


**onBluetoothConnected**や**onBluetoothDisconnected**の実装を確認すると、デバイスID:`MICROBIT_ID_BLE`の値:`MICROBIT_BLE_EVT_CONNECTED`や値:`MICROBIT_BLE_EVT_DISCONNECTED`といったイベント識別子を`registerWithDal()`で登録しています。
これらのイベントが発生するとそれぞれの`body`(無名関数)がコールバックされる仕組みです。

```pxt-microbit/libs/bluetooth/bluetooth.cpp

    /**
     * Register code to run when the micro:bit is connected to over Bluetooth
     * @param body Code to run when a Bluetooth connection is established
     */
    //% help=bluetooth/on-bluetooth-connected weight=20
    //% blockId=bluetooth_on_connected block="on bluetooth connected" blockGap=8
    //% parts="bluetooth"
    void onBluetoothConnected(Action body) {
        registerWithDal(MICROBIT_ID_BLE, MICROBIT_BLE_EVT_CONNECTED, body);
    }    

     /**
     * Register code to run when a bluetooth connection to the micro:bit is lost
     * @param body Code to run when a Bluetooth connection is lost
     */
    //% help=bluetooth/on-bluetooth-disconnected weight=19
    //% blockId=bluetooth_on_disconnected block="on bluetooth disconnected"
    //% parts="bluetooth"
    void onBluetoothDisconnected(Action body) {
        registerWithDal(MICROBIT_ID_BLE, MICROBIT_BLE_EVT_DISCONNECTED, body);
    } 

```

**setTransmitPower**の実装を確認すると、`uBit.bleManager`の`setTransmitPower`関数を呼び出していることがわかります。

```pxt-microbit/libs/bluetooth/bluetooth.cpp

    /**
    * Sets the bluetooth transmit power between 0 (minimal) and 7 (maximum).
    * @param power power level between 0 (minimal) and 7 (maximum), eg: 7.
    */
    //% parts=bluetooth weight=5 help=bluetooth/set-transmit-power advanced=true
    //% blockId=bluetooth_settransmitpower block="bluetooth set transmit power %power"
    void setTransmitPower(int power) {
        uBit.bleManager.setTransmitPower(min(MICROBIT_BLE_POWER_LEVELS-1, max(0, power)));
    }

```

この`uBit.bleManager`の実装は、V1とV2とで異なります。

- **(V1)** MicroBitBLEManager - [microbit-dal/inc/bluetooth/MicroBitBLEManager.h](https://github.com/lancaster-university/microbit-dal/blob/master/inc/bluetooth/MicroBitBLEManager.h#L94)
- **(V2)** MicroBitBLEManager - [codal-microbit-v2/inc/bluetooth/MicroBitBLEManager.h](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitBLEManager.h#L69)

:::note warn
**uBit.bleの正体**
V1/V2ともに`uBit.ble`は、`BLEDevice`型(`ble.h`)ですが、V2における`uBit.ble`の実体は、`MicrobitBLEManager`型(クラス)です。
→ [ソースコード](https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/bluetooth/MicroBitBLEManager.h#L66)
```codal-microbit-v2/inc/bluetooth/MicroBitBLEManager.h

class MicroBitBLEManager;
typedef MicroBitBLEManager BLEDevice;

```
:::

### UARTサービスに関するブロック

**UARTサービス**に関するブロックとして、次の3つがあります。

|ブロック|bluetooth.ts |bluetooth.cpp|
|--------|---|---|
|データを受信したとき| | void onUartDataReceived(String delimiters, Action body)|
|手前まで読み取る| | String uartReadUntil(String del)|
|文字列を書き出す| | void uartWriteString(String data)|
|数値を文字で書き出す| export function uartWriteNumber(value: number): void| |
|write line| export function uartWriteLine(data: string): void| |
|名前と値を書き出す| export function uartWriteValue(name: string, value: number): void| |

**onUartDataReceived**関数では、`MicroBitUARTService`クラスの`eventOn()`で受信待ちの区切り文字を指定し、イベント（`MICROBIT_ID_BLE_UART`, `MICROBIT_UART_S_EVT_DELIM_MATCH`）で、`body`がコールバックされるように登録しています。

```pxt-microbit/libs/bluetooth/bluetooth.cpp

    /**
    * Registers an event to be fired when one of the delimiter is matched.
    * @param delimiters the characters to match received characters against.
    */
    //% help=bluetooth/on-uart-data-received
    //% weight=18 blockId=bluetooth_on_data_received block="bluetooth|on data received %delimiters=serial_delimiter_conv"
    void onUartDataReceived(String delimiters, Action body) {
      startUartService();
      uart->eventOn(MSTR(delimiters));
      registerWithDal(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH, body);
    }

```

`uartWriteString`関数や`uartReadUntil`関数もまた`MicroBitUARTService`クラスの関数を呼び出しています。

```pxt-microbit/libs/bluetooth/bluetooth.cpp

    //%
    void uartWriteString(String data) {
        startUartService();
    	uart->send(MSTR(data));
    }    

    //%
    String uartReadUntil(String del) {
        startUartService();
        return PSTR(uart->readUntil(MSTR(del)));
    }    

```


:::note info
文字列型の相互変換(StringとManagedString)
`String`と`ManagedString`とを相互に変換するために、`MSTR()`や`PSTR()`が使われています。

- **MSTR()** - StringからManagedStringへ変換
- **PSTR()** - ManagedStringからStringへ変換 
:::

残りの3つの関数（`uartWriteLine`,`uartWriteNumber`,`uartWriteValue`）は、TypeScriptで`uartWriteString`関数を呼び出しています。

```pxt-microbit/libs/bluetooth/bluetooth.ts

    /**
    *  Writes to the Bluetooth UART service buffer. From there the data is transmitted over Bluetooth to a connected device.
    */
    //% help=bluetooth/uart-write-line weight=79
    //% blockId=bluetooth_uart_line block="bluetooth uart|write line %data" blockGap=8
    //% parts="bluetooth" advanced=true
    export function uartWriteLine(data: string): void {
        uartWriteString(data + serial.NEW_LINE);
    }

    /**
     * Prints a numeric value to the serial
     */
    //% help=bluetooth/uart-write-number weight=79
    //% weight=89 blockGap=8 advanced=true
    //% blockId=bluetooth_uart_writenumber block="bluetooth uart|write number %value"
    export function uartWriteNumber(value: number): void {
        uartWriteString(value.toString());
    }

    /**
     * Writes a ``name: value`` pair line to the serial.
     * @param name name of the value stream, eg: x
     * @param value to write
     */
    //% weight=88 weight=78
    //% help=bluetooth/uart-write-value advanced=true
    //% blockId=bluetooth_uart_writevalue block="bluetooth uart|write value %name|= %value"
    export function uartWriteValue(name: string, value: number): void {
        uartWriteString((name ? name + ":" : "") + value + NEW_LINE);
    }

```

:::note info
**TypeScriptからC++の呼び出し**
pxtではTypeScriptからC++を呼び出すしくみが備わっています。

TypeScript側でブロックの定義をし、`//% shim=bluetooth::uartWriteString`で、C++側の関数に関連付けます。
尚、このTypeScript側の関数内の処理は、MakeCodeエディタのシミュレーターで実行され、実機では実行されません。
```pxt-microbit/libs/bluetooth/bluetooth.ts

    /**
    *  Writes to the Bluetooth UART service buffer. From there the data is transmitted over Bluetooth to a connected device.
    */
    //% help=bluetooth/uart-write-string weight=80
    //% blockId=bluetooth_uart_write block="bluetooth uart|write string %data" blockGap=8
    //% parts="bluetooth" shim=bluetooth::uartWriteString advanced=true
    export function uartWriteString(data: string): void {
        console.log(data)
    }

```

C++側で関連付けられた関数には、`//%`だけを記述し、関数内に実装します。これが、実機で実行されます。

```pxt-microbit/libs/bluetooth/bluetooth.cpp

    //%
    void uartWriteString(String data) {
        startUartService();
    	uart->send(MSTR(data));
    }

```

:::

# BLEサービスを実装するためのヒント

MakeCode for micro:bitで提供される標準(の拡張機能)である**bluetooth**の実装を確認しましたが、確認する中で得られた関連するヒントを残しておきます。

## BLEサービスを実装するためのクラス設計（例）

架空の**Oneサービス**を実装するためのクラス設計の例を次のUML図に示しました。尚、図中のnamespace（`dal::`, `codal::`）は不要です。V1/V2のクラスモジュールを区別するために便宜上、namespaceを記しています。

`MicroBitComponent`を継承した`MicroBitOneServiceBase`仮想クラスを定義し、バージョンに依存する部分を`MicroBitOneService`クラスで、実装します。BLEサービスの振る舞いが複雑な場合、仮想クラス（基底クラス）を設けることで、ソースコードの重複を少なくすることができそうです。

```plantuml
class dal::MicroBitComponent
{
+virtual void idleTick()
}
class CodalComponent
{
+virtual void idleCallback()
}
class codal::MicroBitComponent
class BLEDevice
class MicroBitBLEService

abstract class MicroBitOneServiceBase
{
MicroBitOneServiceBase()
+void sendOneText(ManagedString text)
+ManagedString getOneData()
#onOneDataDataWritten(ManagedString data)
-virtal void notifyOneText(const uint8_t *data, unit16_t length)
}


class dal::MicroBitOneService
{
MicroBitOneService()
-virtal void notifyOneText(const uint8_t *data, unit16_t length)
-GattAttribute::Handle_t oneTextCharacteristicHandle;
-GattAttribute::Handle_t oneDataCharacteristicHandle;
+virtual void idleTick()
-void onDataRead(GattReadAuthCallbackParams *params)
-void onDataWritten(const GattWriteCallbackParams *params)
-{static} void on_confirmation(uint16_t handle)
}


class codal::MicroBitOneService
{
MicroBitOneService()
-virtal void notifyOneText(const uint8_t *data, unit16_t length)
+virtual void idleCallback()
-virtual void onAuthorizeRead(const microbit_ble_evt_t *p_ble_evt)
-virtual void onDataWritten(const microbit_ble_evt_write_t *params)
-virtual void onConfirmation( const microbit_ble_evt_hvc_t *params)
-typedef enum mbbs_cIdx
-{static} const uint16_t serviceUUID;
-{static} const uint16_t charUUID[ mbbs_cIdxCOUNT];
-MicroBitBLEChar chars[mbbs_cIdxCOUNT];
+int characteristicCount()
+MicroBitBLEChar *characteristicPtr( int idx)
}

dal::MicroBitComponent <|-- MicroBitOneServiceBase : (dal-v1)
codal::MicroBitComponent <|-- MicroBitOneServiceBase : (codal-v2)

CodalComponent <|-- codal::MicroBitComponent

BLEDevice --o dal::MicroBitOneService
MicroBitOneServiceBase <|-- dal::MicroBitOneService

MicroBitOneServiceBase <|-- codal::MicroBitOneService
MicroBitBLEService <|-- codal::MicroBitOneService

```

:::note warn
**`MicroBitOneServiceBase`仮想クラスの継承元はどちらか一方です**
dal/codal(V1/V2)の両者を１つのクラス図で説明しているため、`MicroBitOneServiceBase`仮想クラスが多重継承しているように見えますが、V1/V2用にそれぞれ、どちらか一方を継承します。

ソースコード上では、プリプロセッサ ディレクティブによる切り分けた実装を行います（`#if MICROBIT_CODAL`/`#else`/`#endif`）。
→ [「micro:bit ランタイムとNordic nRF5 SDK」](#microbit-%E3%83%A9%E3%83%B3%E3%82%BF%E3%82%A4%E3%83%A0%E3%81%A8nordic-nrf5-sdk)
:::



## bluetoothに関する構成情報(pxt.json)

MakeCodeのブロック（ライブラリ）やMakeCodeエディタで作成したプログラム（ブロックベースやテキストベース）は、[Microsoft Programming Experience Toolkit (略してPXT)](https://github.com/microsoft/pxt)を使ってビルドされますが、そのビルドに関する構成情報を`pxt.json`ファイルで定義します。

例えば、bluetooth拡張機能の[pxt-microbit/libs/bluetooth/pxt.json](https://github.com/microsoft/pxt-microbit/blob/master/libs/bluetooth/pxt.json)では、次のようにbluetoothに関する構成情報が定義されています。

- **"config"** - bluetoothに関する設定
- **"optionalConfig"** - BLEスタック（内部ライブラリ）の設定
- **"userConfigs"** - MakeCodeエディタの「プロジェクトの設定」で変更可能な項目の定義

尚、名称が「**"microbit-dal"**」となっているキーがありますが、 **DAL(V1)** と **CODAL(V2)** とで共通です。

これ以外にも、設定可能な項目として、[サンプルコード（lancaster-university/microbit-samples）とそのコメント](https://github.com/lancaster-university/microbit-samples/blob/master/source/examples/bluetooth-services/main.cpp#L45)にいくつかの例があります。

``` pxt-microbit/libs/bluetooth/pxt.json

    "yotta": {
        "config": {
            "microbit-dal": {
                "bluetooth": {
                    "enabled": 1
                }
            }
        },
        "optionalConfig": {
            "microbit-dal": {
                "stack_size": 1280,
                "gatt_table_size": "0x700"
            }
        },
        "userConfigs": [
            {
                "description": "Disable Bluetooth Event Service",
                "config": {
                    "microbit-dal": {
                        "bluetooth": {
                            "event_service": 0
                        }
                    }
                }
            }
        ]
    }

```

## ブロック定義

拡張機能のブロックの実装は、[Defining blocks](https://makecode.com/defining-blocks)の仕様に従って、TypeScript(\*.ts)やC++(\*.cpp)で実装します。
MakeCodeエディタがブロックとして認識するために、JsDoc形式のコメントとアノテーション(`//%`)を関数に記述します。C++(\*.cpp)側のみでの実装も可能ですが、MakeCodeエディタが警告を表示することがありますので、TypeScript(\*.ts)側の関数でコメントとアノテーションを記述し、C++(\*.cpp)側では、`//%`のみのアノテーションを記述することを推奨します。

:::note warn
<b>プログラムのエラー : Cannot read properties of undefined (reading '*\<functionName\>*')</b>

C++(*.cpp)側のみで、ブロックの定義を記述すると、MakeCodeエディタで**プログラムのエラー**が発生します。

:::

# まとめ

MakeCodeでmicro:bitのBLEサービスを実装したい為、標準のbluetooth拡張機能に関するソースコードを確認しました。

1. 標準のbluetooth拡張機能のブロックを列挙しました
1. micro:bitのバージョンの違いを確認しました
1. クラス構造を確認しました
1. 各サービスの実装をソースコードで確認しました
1. ブロックの実装をソースコードで確認しました

確認する中で、いくつかのヒントが得られました。

- BLEサービスを実装するためのクラス設計
- pxt.jsonの構成情報
- ブロック定義

これを参考に、独自のBLEサービスを実装することができそうです。