micro:bitの無線通信でBufferを送受信

# micro:bitの無線通信

micro:bitには、無線通信のRadio機能が備わっており、MakeCode for micro:bitでのブロックコーディングが可能です。

## 基本的な送受信

送受信に関して次のブロックを使用できます。

|#| 種類 | 送信(ブロードキャスト) | 受信(イベント) |
|:-:|---|---|---|
|1| 数値       | radio.sendNumber() | radio.onReceivedNumber() |
|2| 名前と数値  | radio.sendValue() | radio.onReceivedValue() |
|3| 文字列     | radio.sendString()  | radio.onReceivedString() |



**JavaScript**

```js
let v = 0
radio.onReceivedNumber(function (receivedNumber) {
    console.log("数値: " + receivedNumber)
})
radio.onReceivedValue(function (name, value) {
    console.log(name + ": " + value)
})
radio.onReceivedString(function (receivedString) {
    console.log("文字列: " + receivedString)
})
basic.forever(function () {
    v = randint(0, 10)
    basic.pause(1000)
    radio.sendNumber(v)
    basic.pause(1000)
    radio.sendValue("v", v)
    basic.pause(1000)
    radio.sendString("v=" + v)
})

```

**Blocks**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/d79cac62-7e76-f03a-8c75-0e7a883e49b8.png)

:::note info
**Radioの送受信**
送信は、ブロードキャストされ、他のすべてのmicro:bitで受信可能です。
送信された情報は、イベントとして受信することができます。
送受信において、そのグループIDが同一であれば、イベントが発生します。
:::

## 隠されたBuffer送受信

Radio機能では、Bufferの送受信も実装されていますが、そのブロックの定義で非表示としてブロックが隠されています。
その為、ドラッグアンドドロップでは、コーディングできませんので、JavaScriptモードで、テキストベースのコーディングをします。

|#| 種類 | 送信(ブロードキャスト) | 受信(イベント) |
|:-:|---|---|---|
|1| Buffer | radio.sendBuffer() | radio.onReceivedBuffer() |

**JavaScript**

```js
radio.onReceivedBuffer(function (receivedBuffer) {
    console.log("buffer(length): " + receivedBuffer.length)
})
basic.forever(function () {
    basic.pause(1000)
    let buffer = Buffer.create(12)
radio.sendBuffer(buffer)
})

```

**Blocks**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/036c0c5b-4d16-a26c-82c3-f9e70fd53c2b.png)

:::note info
**Buffer(バッファ)とは**
[Buffer](https://makecode.microbit.org/types/buffer): temporary part of memory used to transfer data between your program and devices - https://makecode.microbit.org/types/buffer

バイト配列のデータであり、Radio機能の場合、最大19バイトのデータを一度に送受信できます。

次のコードは、12バイト長のBufferを生成しています。
```
    let buffer = Buffer.create(12)
```
:::

# コーディング例：加速度データの送受信

加速度データ(x,y,z)の送受信を例に、**基本的な方法での送受信**と**Bufferを使った送受信**とをコーディングしてみます。

## 基本的な方法での送受信

まずは、基本的な方法として、**名前と数値**を使った送受信を試してみます。

**送受信データの設計**

| 加速度 | 名前 | 数値 |
|:--:|:--:|----|
| 加速度x | x  | (X軸の加速度) |
| 加速度y | y  | (Y軸の加速度) |
| 加速度z | z  | (Z軸の加速度) |

**※ 役割のルール： Aボタンを押した方が、送信側**

**JavaScript**

```js
let K = 0
input.onButtonPressed(Button.A, function () {
    K = 9.8 / 1023
})
radio.onReceivedValue(function (name, value) {
    if ("x" == name) {
        console.log("x: " + value)
    } else if ("y" == name) {
        console.log("y: " + value)
    } else if ("z" == name) {
        console.log("z: " + value)
    } else {
        console.log("???")
    }
})
basic.forever(function () {
    if (0 != K) {
        radio.sendValue("x", K * input.acceleration(Dimension.X))
        radio.sendValue("y", K * input.acceleration(Dimension.Y))
        radio.sendValue("z", K * input.acceleration(Dimension.Z))
    }
})

```

**Blocks**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/500a50d4-fa87-22c4-c424-2b819bdd2546.png)

:::note warn
**Aボタンを押した方が送信側**
無線通信を開始すると、MakeCode for micro:bitのシミュレータ上にもうひとつ別のmicro:bitが現れ、Radio機能をシミュレーションすることができます。ただし、同一のプロジェクトが実行されますので、Aボタンを押しすことにより、送信側を決定しています(変数Kの値が0以外になると送信側)。
:::


## Bufferを使った送受信

**名前と数値**を使って、X軸、Y軸、Z軸の加速度データを順に送信し、順に受信しましたが、**Buffer**に３つのデータを詰め込めば、一括で送信と受信ができます。

Bufferにデータを詰め込むには、送受信するデータのフォーマットとサイズ、オフセットを確認する必要があります。

加速度データは、整数ではなく小数として送受信したいので、32ビットのFloat型とします。エンディアンは、リトルエンディアンです。`Float32LE`のサイズは、4バイトです。加速度x,y,zの順に詰め込むので、オフセットは、4バイトずつずらしています。

**送受信データの設計**

| # | 加速度 | フォーマット | サイズ | オフセット | 
|:-:|:--:|:--:|---:|---:|
| 1 | 加速度x | Float32LE  | 4バイト | 0 |
| 2 | 加速度y | Float32LE  | 4バイト | 4 |
| 3 | 加速度z | Float32LE  | 4バイト | 8 |

**※ Bufferのサイズは、3つのサイズを合計した12バイト長が必要**

:::note info
**ビッグエンディアンとリトルエンディアン**
ビッグエンディアン(big endian)とリトルエンディアン(little endian)は、そのサイズが大きい・小さいという意味ではありません。複数バイトからなる値をバイト配列に収める際の方式（順序）を意味するものです。
micro:bitでは、リトルエンディアンが採用されていますので、フォーマットもリトルエンディアン(LE)を選択しました。しかし、ビッグエンディアンを選択しても構いません。**送受信の双方で同じ方式を選択すればよいのです。**
:::

**JavaScript**

```js
input.onButtonPressed(Button.A, function () {
    K = 9.8 / 1023
})
radio.onReceivedBuffer(function (receivedBuffer) {
    if (12 == receivedBuffer.length) {
        console.log("x: " + receivedBuffer.getNumber(NumberFormat.Float32LE, 0))
        console.log("y: " + receivedBuffer.getNumber(NumberFormat.Float32LE, 4))
        console.log("z: " + receivedBuffer.getNumber(NumberFormat.Float32LE, 8))
    } else {
        console.log("???")
    }
})
let K = 0
let buffer = Buffer.create(12)
basic.forever(function () {
    if (0 != K) {
        buffer.setNumber(NumberFormat.Float32LE, 0, K * input.acceleration(Dimension.X))
        buffer.setNumber(NumberFormat.Float32LE, 4, K * input.acceleration(Dimension.Y))
        buffer.setNumber(NumberFormat.Float32LE, 8, K * input.acceleration(Dimension.Z))
        radio.sendBuffer(buffer)
    }
})

```

**Blocks**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/44a3e1fe-b27a-28aa-c830-528626190b57.png)

# なぜ、Bufferを使って送受信するのか

Radio機能のデータ送受信において、**名前と数値**ではなく、**Buffor**を使うと次のメリットがあります。

1. 送受信データのサイズが小さくなる
   **名前の情報**が送受信データに含まれなくなりますので、その分だけ、サイズが小さくなります
1. 通信のオーバーヘッドが削減される
   通信には、送受信データだけでなく、**通信のオーバーヘッド**もありますので、3回分が1回分に削減されます
1. ３つのデータをセットで送受信できる
   別々に送信すると受信時にどれかを**取りこぼす**ことがありますが、３つのデータを１つのセットとして受信できます

特に、３つ目のメリットが重要で、複数のデータが一つのセットとして構成されている場合、その構成する要素がひとつでも欠けてしまえば（取りこぼしてしまえば）、そのデータは意味を成さなくなってしまいます。

# まとめ

micro:bitは無線通信をサポートしており、MakeCode for micro:bit上で、`数値`、`名前と数値`、`文字列`の送受信といった、これらのブロックコーディングが可能です。

また、JavaScript等のテキストベースであれば、`Buffer`を使った効率的な送受信のコーディングも可能です。データの送受信に、この`Buffer`を用いると、**データサイズが小さくなり、通信オーバーヘッドが削減され、完全なデータ受信が保証されます。**

実際に、加速度データの送受信を例に、`名前と数値`を使った送受信と`Buffer`を使った送受信を試してみました。

**共有プロジェクト：Bufferを使った送受信**

https://makecode.microbit.org/_Ypodc91hzbCP