Scratchからmicro:bitを操作できるC++な「micro:bit拡張機能」開発キットをMakeCodeで開発してみよう

:::note warn
ブロック型のビジュアルプログラミング言語について
ScratchやMakeCodeのような開発環境でプログラミングを楽しんだり学んだりすることに懸念する声が保護者の方などから聞かれることがありますが、ご心配は無用です。
なぜなら、その裏側では、枯れた技術や最新の技術を用いて開発環境が構築されています。さらに、MakeCodeにおいては、JavaScriptやPythonのようなスクリプト言語だけでなく、より低級言語であるC/C++による開発（ビルド）も可能です。ブロック型のビジュアルプログラミング言語を習得すれば、その内部構造やライブラリを理解するのも容易になり、さらに具体的なソースコードを読んだり書いたりすることで、プログラミングの技術が向上します。
:::

# Scratchからmicro:bitを操作できる「micro:bit拡張機能」

Scratch3.0からmicro:bitを**利用**する方法として、[micro:bit拡張機能](https://scratch.mit.edu/microbit)が提供されています。
この拡張機能を用いれば、micro:bitをScratchの**無線コントローラー**のように利用できます。ただし、あくまでも、Scratch3.0の拡張機能であり、Scratch3.0側でのプログラミングしか行えません。提供されるHEX形式ファイルでは、micro:bit側のプログラミングは行えません。

そこで、Scratch Linkの仕様に合わせたmicro:bit側の「micro:bit拡張機能」開発キット(pxt-s3link-udk)を開発しました。このキットを使えば、ブロック型のプログラミングであるMakeCode上で、「micro:bit拡張機能」に対応したmicro:bit用のHEX形式ファイルの開発が可能です。

従来の仕様通りに、LEDに文字列やシンボルを出力したり、センサーの入力をしたりもできますが、プログラミング可能な為、LEDに表示する文字列をコマンドと見立てて、サーボーモーターを動かしたり（出力）、傾きセンサーの代わりに、アナログコントローラーの値を入力したりもできるようになります。

そうです、**プログラミング可能な無線コントローラー**を作ることができるのです。

## micro:bit拡張機能 とは

https://ja.scratch-wiki.info/wiki/Micro:bit%E6%8B%A1%E5%BC%B5%E6%A9%9F%E8%83%BD

> micro:bit拡張機能は、Scratch 3.0の拡張機能で、micro:bitが利用できるようにするものである。

## Scratch Link とは

https://ja.scratch-wiki.info/wiki/Scratch_Link

> Scratch Link(スクラッチ リンク)は、Scratchチームが開発したScratch 3.0と連携して外部のデバイスの操作を可能にするソフトウェアである。これは、micro:bitなどを使う上で必要になる。Scratchアプリにはこれに相当する機能が含まれているので、Android/Chromebook上でScratch Linkは不要で、かつ提供予定はない。

## MakeCode とは

https://www.microsoft.com/ja-jp/makecode/about

> 魅力的なコンピューター サイエンス学習体験を作り出す Microsoft MakeCode は、現実世界のプログラミングにつながる無料のオープン ソース プラットフォームです。

# なぜ、micro:bit側でのプログラミングが必要なのか

「micro:bit拡張機能」があれば、Scratch3.0からScratchLink経由でmicro:bitと連携することで、micro:bitのボタンやセンサーに応答したり、micro:bitのLEDを表示したりできます。

ただし、micro:bit側でのプログラミングはできません。

これでは、**単なる無線コントローラー**です。micro:bitには、他にもセンサーを搭載していますし、外部センサーから入力したり、サーボーモーターや音などの出力も可能です。

これらを実現するためにも、micro:bit側でもプログラミングできる必要があるのです。


# micro:bit拡張機能のプログラミングとビルド

MakeCode上で、[S3Link UDK(pxt-s3link-udk)](https://github.com/jp-rad/pxt-s3link-udk)を用いて、micro:bit拡張機能に対応したHEX形式ファイルをプログラミングし、ビルドすることが可能です。

1. MakeCode - micro:bit を開く
1. [S3Link UDK(pxt-s3link-udk)](https://github.com/jp-rad/pxt-s3link-udk)を追加する
1. [BLADV(pxt-ubit-bladv)](https://github.com/jp-rad/pxt-ubit-bladv)を追加する
1. micro:bit拡張機能 をプログラミングする
1. プロジェクトの設定でbluetoothの構成情報を変更する
1. micro:bit拡張機能 をビルドする(HEX形式ファイルのダウンロード)

:::note warn
**プロジェクト設定のコツ**

プロジェクトの設定で、bluetoothの構成情報を変更します。
しかし、一部、その設定を有効化するために、コツが必要です。
（2022/11/18現在）

1. MakeCodeエディタで、右上の **[その他]** ボタンから **[プロジェクトの設定]** を選択します。
1. **`[Reset Enable Bluetooth]`以外** の任意の項目をオンにし、**[保存]** ボタンで保存し、**[← 戻る]** ボタンで戻ります（**`[Reset Enable Bluetooth]`** は **オフ** のまま）
1. 再度、**[プロジェクトの設定]** を開きます
1. **`[Reset Enable Bluetooth]`** を **オフ から オン** にし、 **[設定をテキストで編集する]** ボタンをクリックします
1. 編集ファイル(*pxt.json*)に、**`"enabled": 1`** が記述されていることを確認します
1. MakeCodeの **[保存]** ボタンでコンパイルします(１分以上かかる場合があります)
1. MakeCodeの **[ブロック]** ボタンをクリックして、編集画面に戻ります
1. 作成されたHEX形式ファイルや、その後、ダウンロードするHEX形式ファイルをmicro:bitへ転送してください

:::


## プログラミングの例

基本的なプログラミングの例を図に示しました。
`BLADV Complete list of 16-bit Service ID:`に指定している値 61445 は、16進数の0xF005を10進数で表した値です。この値を設定することで、Scrach3.0側からデバイスを検索することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/3f605d28-b3f2-2383-7055-b61cedbb3819.png)

https://makecode.microbit.org/_Mx647rU3RfHJ

# S3Link UDKの開発

[S3Link UDK(pxt-s3link-udk)](https://github.com/jp-rad/pxt-s3link-udk)は、MakeCodeで開発されており(主にC/C++言語)、micro:bitのv1とv2とに対応しています。
MakeCode上でC/C++言語を用いた開発をする場合、[pxt-microbit](https://github.com/microsoft/pxt-microbit)ライブラリを使うと実装が容易になります。ただし、BluetoothLE等の操作する場合は、２つのランタイムライブラリ(DAL/CODAL)をmicro:bitのバージョンに合わせて使い分ける必要があります。

- 開発環境： [MakeCode - micro:bit](https://makecode.microbit.org/)
- ビルドツール： [pxt](https://github.com/microsoft/pxt)
- 共通パッケージ： [pxt-common-packages](https://github.com/microsoft/pxt-common-packages)
- コンパイルキット： [pxt-microbit](https://github.com/microsoft/pxt-microbit)

**DAL/CODAL**

- v1用ランタイムライブラリ DAL： [lancaster-university/microbit-dal](https://github.com/lancaster-university/microbit-dal)
- v2用ランタイムライブラリ CODAL： [lancaster-university/codal-microbit-v2](https://github.com/lancaster-university/codal-microbit-v2)

## Scratch Link の仕様

Scratch Linkのソースコードは、[GitHub (LLK/scratch-link)](https://github.com/LLK/scratch-link)で公開されています。
たとえば、micro:bitを対象デバイスとする場合、[BluetoothLEの資料 (scratch-link/Documentation/BluetoothLE.md)](https://github.com/LLK/scratch-link/blob/develop/Documentation/BluetoothLE.md)を参考にし、BLEペリフェラル（⇔BLEセントラル）として実装します。

ただし、Scratch Link は、単にScratch3.0(JSON-web)とmicro:bit(BLE通信)との橋渡しをしているだけですので、実際のBLE通信仕様は、micro:bit拡張機能のソースコードを確認します([通信仕様](#microbit-のscratch-link通信仕様))。

- Scratch Linkのソースコード: [GitHub (LLK/scratch-link)](https://github.com/LLK/scratch-link)
- [BluetoothLEの資料 (scratch-link/Documentation/BluetoothLE.md)](https://github.com/LLK/scratch-link/blob/develop/Documentation/BluetoothLE.md)

## micro:bit の Scratch Link 通信仕様

micro:bit拡張機能のソースコードで通信仕様を確認できます。
ただし、[ソースコード(L.L.47-57)](https://github.com/LLK/scratch-vm/blob/develop/src/extensions/scratch3_microbit/index.js#L47)のコメントでは、通信仕様のドキュメントへのリンクが記してありますが、リンク先が存在しない為、参照できません。したがって、ソースコードから（BLEセントラル側の）通信仕様を読み取ります。

- Enum for micro:bit protocol.  
[https://github.com/LLK/scratch-microbit-firmware/blob/master/protocol.md](https://github.com/LLK/scratch-microbit-firmware/blob/master/protocol.md)

```LLK/scratch-vm/blob/develop/src/extensions/scratch3_microbit/index.js
// L.L.47-57

/**
 * Enum for micro:bit protocol.
 * https://github.com/LLK/scratch-microbit-firmware/blob/master/protocol.md
 * @readonly
 * @enum {string}
 */
const BLEUUID = {
    service: 0xf005,
    rxChar: '5261da01-fa7e-42ab-850b-7c80220097cc',
    txChar: '5261da02-fa7e-42ab-850b-7c80220097cc'
};
```

https://github.com/LLK/scratch-vm/blob/develop/src/extensions/scratch3_microbit/index.js

## サービスとキャラクタスティック

[ソースコード(L.L.47-57)](https://github.com/LLK/scratch-vm/blob/develop/src/extensions/scratch3_microbit/index.js#L47)から、micro:bit拡張機能のサービスIDは、`0xF005`です。
キャラクタスティックには、`rxChar`と`txChar`とがあり、それぞれは、micro:bitからScracth3.0への受信データ用、Scratch3.0からmicro:bitへの送信データ用です。

| # |項目|ベースUUID|16bit|説明|
|---|---|---------|-----|---|
| 1 | service |00000000-0000-1000-8000-00805F9B34FB|0xF005|サービス|
| 2 | rxChar |52610000-FA7E-42AB-850B-7C80220097CC|0XDA01|受信用(Scratch3.0←micro:bit)|
| 3 | txChar |52610000-FA7E-42AB-850B-7C80220097CC|0XDA02|送信用(Scratch3.0→micro:bit)|


## 受信用キャラクタスティック(rxChar)

`rxChar`は、micro:bitからScracth3.0への受信データ用キャラクタスティックです。
プロパティには、readとnotifyとを指定します。

- GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ
- GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY

受信データのフォーマットは、つぎのとおりです。

- データ形式 配列
- データサイズ 8ビット
- データ長 20(固定)

|#|data[]|内容|備考|
|---|---|----|----|
| 1 | data[0] | 傾きX軸の上位(8ビット) | ビッグエンディアン|
| 2 | data[1] | 傾きX軸の下位(8ビット) | (INT16)|
| 3 | data[2] | 傾きY軸の上位(8ビット) | ビッグエンディアン|
| 4 | data[3] | 傾きY軸の下位(8ビット) | (INT16)|
| 5 | data[4] | ボタンAの状態 |0:UP, 1:DOWN|
| 6 | data[5] | ボタンBの状態 |0:UP, 1:DOWN|
| 7 | data[6] | ピン0の状態 |0:オフ, 1:オン(タッチ)|
| 8 | data[7] | ピン1の状態 |0:オフ, 1:オン(タッチ)|
| 9 | data[8] | ピン2の状態 |0:オフ, 1:オン(タッチ)|
| 10| data[9] | ジェスチャーの状態 |bit2:動いた(moved), bit1:跳ねた(jumped), bit0:振った(shaken)|
| 11| (10～19) | 予約 ||

参照：[ソースコード(L.L.319-348)](https://github.com/LLK/scratch-vm/blob/develop/src/extensions/scratch3_microbit/index.js#L319)

```LLK/scratch-vm/blob/develop/src/extensions/scratch3_microbit/index.js
// L.L.319-348

    /**
     * Process the sensor data from the incoming BLE characteristic.
     * @param {object} base64 - the incoming BLE data.
     * @private
     */
    _onMessage (base64) {
        // parse data
        const data = Base64Util.base64ToUint8Array(base64);

        this._sensors.tiltX = data[1] | (data[0] << 8);
        if (this._sensors.tiltX > (1 << 15)) this._sensors.tiltX -= (1 << 16);
        this._sensors.tiltY = data[3] | (data[2] << 8);
        if (this._sensors.tiltY > (1 << 15)) this._sensors.tiltY -= (1 << 16);

        this._sensors.buttonA = data[4];
        this._sensors.buttonB = data[5];

        this._sensors.touchPins[0] = data[6];
        this._sensors.touchPins[1] = data[7];
        this._sensors.touchPins[2] = data[8];

        this._sensors.gestureState = data[9];

        // cancel disconnect timeout and start a new one
        window.clearTimeout(this._timeoutID);
        this._timeoutID = window.setTimeout(
            () => this._ble.handleDisconnectError(BLEDataStoppedError),
            BLETimeout
        );
    }
```

## 送信用キャラクタスティック(txChar)

`txChar`は、cracth3.0からmicro:bitへの送信データ用キャラクタスティックです。
プロパティには、writeを指定します。

- GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE

送信データのフォーマットは、つぎのとおりです。

- データ形式 配列
- データサイズ 8ビット
- データ長 可変長(最大20)

|#|要素|内容|備考|
|---|---|----|----|
| 1 | 1バイト目 | 表示コマンド部 | 0x80: (予約), 0x81: 文字列表示, 0x82: LED表示|
| 2 | 2バイト目～| 表示データ部 | (データ長まで) |

### 文字列表示コマンド(0x81)

表示データ部は、19文字までの可変長です。BLEで受け取ったデータ長から求めます（表示データ長 = BLEデータ長 - 1）。
参照：[ソースコード(L.L.837-863)](https://github.com/LLK/scratch-vm/blob/develop/src/extensions/scratch3_microbit/index.js#L837)

```LLK/scratch-vm/blob/develop/src/extensions/scratch3_microbit/index.js
// L.L.837-863

    /**
     * Display text on the 5x5 LED matrix.
     * @param {object} args - the block's arguments.
     * @return {Promise} - a Promise that resolves after the text is done printing.
     * Note the limit is 19 characters
     * The print time is calculated by multiplying the number of horizontal pixels
     * by the default scroll delay of 120ms.
     * The number of horizontal pixels = 6px for each character in the string,
     * 1px before the string, and 5px after the string.
     */
    displayText (args) {
        const text = String(args.TEXT).substring(0, 19);
        if (text.length > 0) this._peripheral.displayText(text);
        const yieldDelay = 120 * ((6 * text.length) + 6);

        return new Promise(resolve => {
            setTimeout(() => {
                resolve();
            }, yieldDelay);
        });
    }
```

### LED表示コマンド(0x82)

表示データ部は、0x1Fでマスクされた5バイトの長さです（5x5のマトリックス）。
|要素| 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
|0|bit0|bit1|bit2|bit3|bit4|
|1|bit0|bit1|bit2|bit3|bit4|
|2|bit0|bit1|bit2|bit3|bit4|
|3|bit0|bit1|bit2|bit3|bit4|
|4|bit0|bit1|bit2|bit3|bit4|

参照：[ソースコード(L.L.837-863)](https://github.com/LLK/scratch-vm/blob/develop/src/extensions/scratch3_microbit/index.js#L837)

```LLK/scratch-vm/blob/develop/src/extensions/scratch3_microbit/index.js
// L.L.837-863

    /**
     * Display a predefined symbol on the 5x5 LED matrix.
     * @param {object} args - the block's arguments.
     * @return {Promise} - a Promise that resolves after a tick.
     */
    displaySymbol (args) {
        const symbol = cast.toString(args.MATRIX).replace(/\s/g, '');
        const reducer = (accumulator, c, index) => {
            const value = (c === '0') ? accumulator : accumulator + Math.pow(2, index);
            return value;
        };
        const hex = symbol.split('').reduce(reducer, 0);
        if (hex !== null) {
            this._peripheral.ledMatrixState[0] = hex & 0x1F;
            this._peripheral.ledMatrixState[1] = (hex >> 5) & 0x1F;
            this._peripheral.ledMatrixState[2] = (hex >> 10) & 0x1F;
            this._peripheral.ledMatrixState[3] = (hex >> 15) & 0x1F;
            this._peripheral.ledMatrixState[4] = (hex >> 20) & 0x1F;
            this._peripheral.displayMatrix(this._peripheral.ledMatrixState);
        }

        return new Promise(resolve => {
            setTimeout(() => {
                resolve();
            }, BLESendInterval);
        });
    }
```

# おわりに

Scratch Link 通信仕様を確認し、[S3Link UDK(pxt-s3link-udk)](https://github.com/jp-rad/pxt-s3link-udk)を開発しました。これを用いれば、micro:bit側の入力や出力を自由にプログラミングできます。

## 参考：[BLADV](https://github.com/jp-rad/pxt-ubit-bladv)

https://qiita.com/jp-96/items/da4413c03670a5e0b195