Raspberry Piを使ってBluetooth Meshをやってみた

# はじめに

Raspberry PiとnRF52シリーズでのBluetooth Mesh通信を試しました。
試した通信はnRF52シリーズ搭載の評価ボード(サーバ)のLEDをRaspberry Pi(クライアント)からOn/Off制御する内容です。
Bluetooth Mesh通信に関してはまだ詳しくないため苦労しましたが、うまく通信出来るようになりましたので記録を残すため記事を書きました。（そして初Qiitaです）

# 用意したもの(ハードウェア)

- Raspberry Pi 3 Model B
- 評価ボード MDBT50Q-DB-33(RAYTAC)  
[SWITCH SCIENCE](https://www.switch-science.com/catalog/6275/)さん等で入手できます。こちらの評価ボードはnRF52833が搭載されておりますが、nRF52シリーズであれば問題ないと思います。
- Segger J-Link EDU mini - JTAG/SWD デバッガー  
[マルツオンラインさん](https://www.marutsu.co.jp/pc/i/26595451/)等で入手できます。  
仕事ではJ-Link EDUを使用しましょう。

![IMG_20211114_102421_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/4e06a00b-7989-1fae-0b41-408b47251f94.jpeg)

# 用意したもの(評価ボード関連)
- Segger Embedded Studio for ARM 5.60
- nRF5 SDK 17.0.2
[NORDIC SEMICONDUCTORのWebサイト](https://www.nordicsemi.com/Products/Development-software/nrf5-sdk/download)からダウンロードできます。
- nRF5 SDK for Mesh 5.0.0  
こちらも[NORDIC SEMICONDUCTORのWebサイト](https://www.nordicsemi.com/Products/Development-software/nRF5-SDK-for-Mesh/Download?lang=en#infotabs)からダウンロードできます。

# 用意したもの(Raspberry Pi関連)
- BlueZ 5.62  
当時の最新バージョンを
[GitHUB](https://github.com/bluez/bluez)からダウンロードしました。
- Kernel 5.10.63-v7  
当時の最新バージョンを[GitHUB](https://github.com/raspberrypi/linux)からダウンロードしました。
- Python3
- TeraTerm

# 評価ボードへプログラムのインストール

## 開発環境準備

Segger Embedded Studio for ARMはあらかじめインストールしてください。開発環境の構築は[こちらの記事](https://qiita.com/Kosuke_Matsui/items/5d61ce77e928b9f117cc)が参考になりました。
nRF5 SDKとnRF5 SDK for Meshは対応するバージョンが決まっています。
下記のエクスプローラのようにダウンロードしたnRF5 SDKとnRF5 SDK for Meshを適当なフォルダへ展開します。
![画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/9eb14487-0a22-ee51-f59a-82785a5fa6e4.png)
nRF5_SDK_17.0.2_d674ddeフォルダ中身の例
![画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/a429f8f2-f288-2259-dd84-88eca1d03812.png)
nrf5_SDK_for_Mesh_v5.0.0_srcフォルダ中身の例
![画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/967daadc-2fc2-3d99-8cf6-95e041d7eee4.png)

## プログラムの書込み

評価ボードへnRF5 SDK for Meshに付属のサンプルプログラムを書き込みます。

Segger Embedded Studio for ARM のメニューから"Open Solution..."を選択してnRF5 SDK for MeshのSolution File を開きます。
![画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/f707928b-05e1-fb8e-1491-10b39a25cd97.png)
nRF52833の場合は、light_switch_server_nrf52833_xxAA_s113_7_2_0.emProject を開きます。
![画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/4d2c486a-872b-2e27-04ae-857cbb47701d.png)
ビルドが成功したら"Target"メニューの "Connect J-link"でターゲットと接続してプログラムの書込みを行います。
![画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/cf251493-cfb2-ebd7-19e4-2d665bc92ed5.png)

以上で評価ボードへプログラムのインストールが完了となります。

# Raspberry Piの準備

## Raspberry PiのカーネルビルドとBlueZのインストール

bluetooth.comの[PDFファイル](https://www.bluetooth.com/wp-content/uploads/2020/04/Developer-Study-Guide-How-to-Deploy-BlueZ-on-a-Raspberry-Pi-Board-as-a-Bluetooth-Mesh-Provisioner.pdf)に従ってKernelのビルド＆インストール、BlueZのビルド＆インストールを行います。
説明に記載されているフォルダ名は使用するバージョンに応じて読み替えてください。
12ページのBlueZのインストールまで進めます。
※私がこちらの手順に従って作業した内容は今後、記事にしていきたいと思います。

## Raspberry Piでmesh通信のデーモンを起動

ターミナルで下記コマンドを入力してデーモンを起動します。
BlueZのディレクトリ名はお使いのバージョンに応じて読み替えてください。
デーモンが起動した後、こちらのターミナルは閉じないでください。

```terminal:ターミナル
sudo systemctl stop bluetooth
sudo ~/bluez-5.62/mesh/bluetooth-meshd -nd
```

## その他インストールが必要なソフトウェア

後の説明で実行するサンプルプログラムがPythonで記述されているため、Python3のインストールを行います。

# ノードのプロビジョニング

## 評価ボードのプロビジョニング

*mesh-cfgclient*を起動するため新しくターミナルを起動して下記コマンドを入力してください。

```terminal:ターミナル
mesh-cfgclient
```

正常に起動すると下記のように *[mesh-cfgclient]#* プロンプトが表示されます。
![画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/074ece20-7eaa-480b-ea43-5c45a72f3444.png)
初めて*mesh-cfgclient*を起動すると、"Warning: config file "/home/pi/.config/meshcfg/config_db.json" not found"のようなメッセージが表示されます。その場合は下記のコマンドを入力してください。

```:mesh-cfgclient
create
```

NetKeyとAppKeyを作成するため下記コマンドを入力します。

```:mesh-cfgclient
appkey-create 0 0
```

評価ボードを探すために下記コマンドを入力します。

```:mesh-cfgclient
discover-unprovisioned on
```

見つかったら下記コマンドで探すのを中止してください。

```:mesh-cfgclient
discover-unprovisioned off
```
ターミナルに表示されている緑色の文字が評価ボードの情報になります。*表示されているUUIDは後の作業で必要になる*のでメモしてください。
![画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/31a4f951-ea75-176c-7f2e-9f5ba23d3c2f.png)

評価ボードのプロビジョニングを行うため下記コマンドを入力します。
UUIDは上でメモした16進文字列です。"Provisioning started"というメッセージが表示されてプロビジョニングが開始されます。

```:mesh-cfgclient
provision <UUID>
```

しばらくするとキーの入力が要求されます。  
入力するキーが記載されている箇所はnRF5 SDK for Meshのフォルダ(\nrf5_SDK_for_Mesh_v5.0.0_src\examples\common\include)に格納されているexample_common.hになります。
*STATIC_AUTH_DATA*というdefine宣言を確認してそちらの値を入力します。
![画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/906dde05-1fb8-12df-0af2-870e51b95e43.png)
Keyを正しく入力すると下記のように表示されプロビジョニングが完了します。primryの値は後の作業で使用しますのでメモしてください。
![画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/4cc32def-4faa-c8a2-1073-17a454d07f24.png)
評価ボードの設定を行うため下記のコマンドでconfigメニューへ移ります。

```:mesh-cfgclient
menu config
```

設定対象を評価ボードへ定めるため下記コマンドを入力します。\<unicast\>となっている箇所にプロビジョニング完了の際に表示されたprimaryの値を指定します。
  primaryとはプロビジョニングされたノードの先頭エレメントの番号を示しています。評価ボードにはエレメントが１つしかありませんが、例として、エレメントが２つの場合は２つ目のエレメントのアドレスは*00ab*となります。

```:mesh-cfgclient
target <unicast>
```
コマンド実行後にプロンプトが下記の表示のようになっていれば正常に設定されています。
![画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/b1f635d5-7111-c619-53be-26976b47e7c7.png)
評価ボードへキーを追加するため下記のコマンドを入力します。
  私の環境では時々"No response for AppKeyAdd from 00aa"と表示されましたが、再度コマンドを実行することで正常に完了しました。

```:mesh-cfgclient
appkey-add 0
```
正常に完了すると下記の表示のようになります。
![画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/d0f7beb5-3353-66c3-6420-cfe8bd7b05a5.png)
次にAppKeyをモデルへバインドするため下記のコマンドを入力します。
  *bind*コマンドで指定しているパラメータは左から順番に、unicast address、AppKey、Model IDとなります。

```:mesh-cfgclient
composition-get
bind 00aa 0 1000
```

*composition-get*コマンドを実行すると下記の表示のように評価ボードの情報が表示されます。
![画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/16804f93-c185-deac-2bec-8da5526bbcfe.png)
*bind*コマンドまで完了すると下記の表示のようになります。
![画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/84a1a779-1aa1-2a82-1a0c-f256e77f0cef.png)
ここまでで評価ボードのプロビジョニングが完了となります。

## BlueZ付属のサンプルプログラムをプロビジョニング

評価ボードのプロビジョニングで使用したコンソールとBlueZに付属のサンプルプログラムを動作させるコンソールを交互に使用していきます。
そのため、

- 評価ボードのプロビジョニングで使用したコンソール ：プロビジョニング用コンソール
- サンプルプログラムを動かすためのコンソール：サンプルプログラム用コンソール

と記載する事とします。

サンプルプログラム用コンソールから、*/bluez-5.62/test/test-mesh*を実行します。こちらはPythonで作成されています。
  ディレクトリ名は環境に応じて読み替えてください。

```:ターミナル
python3 ~/bluez-5.62/test/test-mesh
```

正常に起動すると下記のようなメニューが表示されます。
![image-20211113195148660.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/3c5016e1-9f10-6add-ec35-7546e84689ae.png)
プロビジョニングするために下記のコマンドを実行します。

```:サンプルプログラム
join
```

表示されたUUIDは後の作業で使用するのでメモします。
![image-20211113195441525.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/4424caa8-e7b5-ed19-a27b-ddfac8680ff8.png)
プロビジョニング用コンソールへ移って下記コマンドを実行してプロビジョニングを開始します。
UUIDはサンプルプログラム用コンソールで表示されたUUIDを指定します。
※*mesh-cfgclient*がconfigメニューになっている場合は*back*コマンドでメインメニューへ戻ってください。

```:mesh-cfgclient
provision <UUID>
```

プロビジョニングが開始されるとサンプルプログラム用コンソールに16進の文字列が表示されるので、評価ボードの時と同じくプロビジョニング用コンソールへ入力します。
![image-20211113200138364.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/50bc6657-e858-79f6-b38d-037fd31d102c.png)
Keyを入力するとプロビジョニングが完了します。今回はサンプルプログラムの先頭エレメント(parimary)が00abとなりました。
![image-20211113200300224.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/a6cd77b8-13d2-0e20-42bc-6e88f0485af8.png)
プロビジョニングが完了した後、サンプルプログラム用コンソールにtokenが表示されます。これは２回目以降にサンプルプログラムを起動して同じメッシュネットワークに参加する際に必要となりますのでメモしてください。
![image-20211113200538090.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/be5fb4e8-c08e-c71d-7f9f-8c484afecc72.png)
プロビジョニング用コンソールでconfigメニューへ移動して設定を行うため下記コマンドを実行します。

```:mesh-cfgclient
menu config
```

configメニューで下記のコマンドを実行していきます。ターゲットのアドレスが変わる以外は評価ボードの時と同じコマンドを使用します。

```:mesh-cfgclient
target 00ab
appkey-add 0
composition-get
```

*composition-get*コマンドを実行するとサンプルプログラムはモデルが２つ存在していることが分かります。
  今回使用したい"Generic OnOff Client"は２つ目のエレメントに存在しています。
![image-20211113201005523.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/a3ae9f83-f7c9-6bb6-6e85-20045f79e0f5.png)
モデルのバインドを行っていきます。プロビジョニングの際に先頭エレメント(primary)は00abと表示されていたので、"Generic OnOff Client"が存在するエレメントは00acとなります。従って*bind*コマンドの指定は下記のようになります。

```:mesh-cfgclient
bind 00ac 0 1001
```

正常に完了すると下記の表示のようになります。
![画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/861f0651-388e-77dd-0c8d-041c34f3dd4e.png)
以上でサンプルプログラムのプロビジョニングが完了です。
  これでようやくRaspberry Piのサンプルプログラムと評価ボードのOnOff Serverが通信出来るようになりました。

# Raspberry Piと評価ボードの通信

ここからはサンプルプログラム用コンソールを使用していきます。*test-mesh*が起動中の前提で説明を行います。

## 評価ボードのLEDを制御してみる

サンプルプログラムを*bluetooth-meshd daemon*へアタッチするため下記のコマンドを実行します。

```:サンプルプログラム
attach
```

通信先として評価ボードを指定するため下記のコマンドを実行します。

```:サンプルプログラム
dest
```

"Enter 4-digit hex destination address:"と表示されるので、評価ボードのunicast addressである00aaを指定します。

次に、下記のコマンドでOnOff Client Model の動作を指定するためのメニューへ移行します。

```:サンプルプログラム
client-menu
```

下記コマンドを実行すると評価ボードのLEDが点灯することを確認できます。

```:サンプルプログラム
on
```

LEDが点灯していることを確認できます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/fca22cbe-99fa-6c0d-c2e6-d93e753e33af.jpeg" width="50%">
RTT ViewerでもRaspberry Pi側のサンプルプログラムから評価ボードへメッセージが届いていることが確認できます。
![image-20211113210647113.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262995/0481b89c-eb1b-b926-d4b9-55f5da09ed2a.png)


# 参考にした資料など

- エレキジャックIoT No.4 CQ出版
- [Nordic社製BLEの開発環境構築と動作確認](https://qiita.com/Kosuke_Matsui/items/5d61ce77e928b9f117cc) [@Kosuke_Matsui](https://qiita.com/Kosuke_Matsui) さん
- [Developer Study Guide Using BlueZ as a Bluetooth Mesh Provisioner](https://www.bluetooth.com/wp-content/uploads/2020/04/Developer-Study-Guide-How-to-Deploy-BlueZ-on-a-Raspberry-Pi-Board-as-a-Bluetooth-Mesh-Provisioner.pdf) bluetooth.com

# 最後に

私が試行錯誤で動作させた手順を振り返って記載しました。
今回のLEDをOn/OffするモデルはGeneric ModeのためSDK等に付属のサンプルプログラムのみで試すことが出来ましたが、まだBluetooth Meshの仕様をよく理解していないため実際に通信を行うまで苦労しました。
次の目標は、既に評価ボード(nRF52833)同士はVendor Modelでの通信を試していますのでBlueZに付属の*test-mesh*プログラムをベースにVendor ModelでRaspberry Piと評価ボードの通信を行ってみようと考えております。

ご覧いただき、ありがとうございました。