Zephyr OSで開発したコードをBabbleSimでシミュレーションしよう


(株)東芝 研究開発センターの平井と申します。
この記事では、物理層シミュレータ [BabbleSim](https://babblesim.github.io/)の基本的な使用方法について説明します。

BabbleSimを利用すると、[RTOS](https://ja.wikipedia.org/wiki/%E3%83%AA%E3%82%A2%E3%83%AB%E3%82%BF%E3%82%A4%E3%83%A0%E3%82%AA%E3%83%9A%E3%83%AC%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0)である[Zephyr OS](https://zephyrproject.org/)で開発したコードの挙動をマイコン無しで確認することができ、大変便利です。
今回は、Bluetoothの1対1通信をBabbleSimでシミュレーションし、Wiresharkでどのような通信が行われたかをGUIで確認します。

## 環境構築

まず、BabbleSimの開発環境構築について説明します。

Zephyr OSの開発環境が整っていれば、BabbleSimを動かすことができると思いますが、
本記事ではZephyr OSを使うのに便利な[nRF Connect SDK](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/index.html)(NCS)と、
[そのVS Code用の拡張機能](https://marketplace.visualstudio.com/items?itemName=nordic-semiconductor.nrf-connect-extension-pack)
を使用して開発を進めていきます。
[Installing the nRF Connect SDK](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/installation/install_ncs.html)を参考に開発環境を整えることをお勧めいたします。

なお、[公式ドキュメント](https://babblesim.github.io/faq.html#can-i-run-babblesim-in-windows)に記載されている通りBabbleSimはLinux環境でのみ動作します。

参考までに、私の開発環境は以下です。

| | |
|----|--------------|
| OS | Ubuntu 22.04 |
| Zephyr OS  | v4.0.99 |
| nRF Connect SDK | v2.8.0 |

### BabbleSimの導入

[Fetching BabbleSim](https://babblesim.github.io/fetching.html)および[Building BabbleSim](https://babblesim.github.io/building.html)を参考に、BabbleSimを導入します。

NCSとVS Codeを導入している場合、まず、Command Paletteで`nRF Connect: Create Shell Terminal`を選択し、Zephyr OS用の環境が設定されたシェルを開きます。
その後、以下でBabbleSimをダウンロードします。

```bash
cd ${ZEPHYR_BASE}
west config manifest.group-filter -- +babblesim
west update
```

ドキュメントに記載されている通り、`BSIM_OUT_PATH=${ZEPHYR_BASE}/../tools/bsim/`と`BSIM_COMPONENTS_PATH=${BSIM_OUT_PATH}/components/`を環境変数に追加します。例えば以下を`.bashrc`に記載します。

```bash:~/.bashrc
export BSIM_OUT_PATH=${ZEPHYR_BASE}/../tools/bsim/
export BSIM_COMPONENTS_PATH=${BSIM_OUT_PATH}/components/
```

最後に、BabbleSimを以下でコンパイルします。

```bash
cd ${BSIM_OUT_PATH}
make everything -j 8
```

これでBabbleSimを使用する準備が整いました。

## BabbleSimでシミュレーションしてみる

ここでは、Zephyr OSのサンプルを使用して簡単なBluetoothの通信をシミュレーションします。
使用するサンプルは`Bluetooth Central HR`と`Bluetooth Peripheral HR`です。
これらは、定期的にペリフェラルが心拍数のダミーデータをセントラルに送信するアプリケーションです。

### シミュレーション用の実行ファイルの作成

Visual Studio CodeのnRF Connect拡張機能を起動し、Create a new applicationを押した後、Copy a sampleからBluetooth Central HRとBluetooth Peripheral HRを選択します。
ここでは、アプリケーションのディレクトリをそれぞれ`~/myproj/central_hr`と`~/myproj/peripheral_hr`に設定しました。

<!-- TODO: 適宜画像を入れたい -->

その後、Add Build ConfigurationのBuild target欄にて`nrf52_bsim/native`を選択して、ビルドします。
ビルド先のディレクトリは任意ですが、ここではデフォルトの`build`であるとします。

無事ビルドに成功すれば、`~/myproj/central_hr/build/zephyr/zephyr.exe`と`~/myproj/central_hr/build/zephyr/zephyr.exe`が作成されていると思います。

試しに、これらのファイルを実行してみると、以下のような結果になります。

```bash
$ ~/myproj/central_hr/build/zephyr/zephyr.exe
?_??: WARNING: Neither simulation id or the device number have been set. I assume you want to run without a BabbleSim phy (-nosim)
?_??: WARNING: If this is not what you wanted, check with --help how to set them
```

WARNINGにも書かれている通り、物理層(無線部分)の設定をしていないため、このままではBluetoothのシミュレーションを実行することはできません。
そこで次に、シミュレーションの設定を行います。

## シミュレーションの設定

シミュレーションを行うためには、`${BSIM_OUT_PATH}/bin`に移動する必要があります。
コマンドを実行しやすいよう、先ほど作成された実行ファイルを移動させておきます。

```bash
cp ~/myproj/central_hr/build/zephyr/zephyr.exe ${BSIM_OUT_PATH}/bin/central_hr
cp ~/myproj/peripheral_hr/build/zephyr/zephyr.exe ${BSIM_OUT_PATH}/bin/peripheral_hr
```

その後、`${BSIM_OUT_PATH}/bin`に移動します。

```bash
cd ${BSIM_OUT_PATH}/bin
```

シミュレーションは以下のコマンドで実行できます。

```bash
./central_hr -s=my_first_sim -d=0 -p=phy &
./peripheral_hr -s=my_first_sim -d=1 -p=phy &
./bs_2G4_phy_v1 -s=my_first_sim -p_id=phy -D=2 -sim_length=10e6 -defmodem=BLE_simple -argsdefmodem -PER=0.1 &
```

ヘルプメッセージをもとに、各オプションの設定を確認していきます。

```text
$ ./central_hr --help
 [-s=<s_id>]                  :String which uniquely identifies the simulation
 -d=<device_number>           :Device number (for this phy)
 [-p=<p_id>]                  :(2G4) String which uniquely identifies the phy inside the simulation
 ...
```

簡単な説明は以下の通りです。

* `-s`はシミュレーションのidです。同じシミュレーションに対しては、全て同一の文字列を指定する必要があります。
* `-d`はデバイスの番号です。今回は2台のデバイスがありますので、`-d=0`, `-d=1`という風にそれぞれ指定しています。
* `-phy`は物理層のidです。シミュレーションのidと同様、基本的に同じシミュレーションに対して同一の文字列を指定することになると思います。

残りの`./bs_2G4_phy_v1`のオプションについても説明します。

```text
$ ./bs_2G4_phy_v1 --help
 [-s=<s_id>]                  :String which uniquely identifies the simulation
 [-p_id=<phy_id>]             :(2G4) String which uniquely identifies the phy inside the simulation
  -D=<number_devices>          :Number of devices which will connect in this phy
 [-sim_length=<sim_length>]   :In us, length of the simulation
 [-defmodem=<modem>]          :Which modem will be used by default for all devices ( lib/lib_2G4Modem_<modem>.so ). By default Magic
 [-argsdefmodem <arg>...]     :Following arguments (until end or new -args*) will be passed to the modems set to be the default modem
 ...
```

* `-D`では、デバイスの数を指定します。今回は2台なので2を指定しています。
* `-sim_length`では、us単位でシミュレーション時間を指定します。今回は10e6を指定しているので、10秒間のシミュレーションが行われることになります。
* `-defmodem`では、各デバイスのデフォルトのモデムを指定します。本記事執筆時点では、`BLE_simple`と`Magic`が指定できます。モデムの説明は、例えば`${BSIM_OUT_PATH}/components/ext_2G4_modem_BLE_simple/docs`に記載されています。
* `-argsdefmodem`以降で、モデムに対する引数を指定します。今回は、PERを0.1に指定しました。

### 実行結果をWiresharkで確認する

実行結果は、`${BSIM_OUT_PATH}/results/${s_id}`に保存されています。
`s_id`は、先ほど`-s`オプションで指定した文字列になります。

```
$ ls ${BSIM_OUT_PATH}/results/my_first_sim/
d_phy_00.CCA.csv      d_phy_00.RSSI.csv  d_phy_00.Rxv2.csv  d_phy_00.Txv2.csv  d_phy_01.ModemRx.csv  d_phy_01.Rx.csv    d_phy_01.Tx.csv
d_phy_00.ModemRx.csv  d_phy_00.Rx.csv    d_phy_00.Tx.csv    d_phy_01.CCA.csv   d_phy_01.RSSI.csv     d_phy_01.Rxv2.csv  d_phy_01.Txv2.csv
```

ファイルを確認すると、全てCSVファイルとなっていることが分かります。
CSVファイルの状態だと結果を確認しづらいですが、ありがたいことにCSVファイルをpcapファイルに変換する方法が用意されています。
以下で、pcapファイルに変換しましょう。

```bash
${BSIM_COMPONENTS_PATH}/ext_2G4_phy_v1/dump_post_process/csv2pcap -o ./my_first_sim.pcap  ${BSIM_OUT_PATH}/results/my_first_sim/d_phy_0*.Tx.csv
```

変換されたファイルをWiresharkで確認します。

![wireshark.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940000/40e8675d-784c-7380-3dd3-8e4d30e55e49.png)

アドバタイジングが行われた後、接続を行い、Notificationによる通信を行っていることが確認できます。
MACアドレスはランダムな値が使用されており、例えば`./central_hr --random-seed=1234`などしてシード値を設定することで変化します。
なお、少なくとも私の環境では、アクセスアドレスがおかしなことになっています。
解決方法をご存じの方がいればコメント欄にご記載頂けますと幸いです。
とはいえ、通信内容を確認する上では十分有用な結果が得られていると思います。

## まとめ

本記事では、Zephyr OSで動作するコードを用いてBabbleSimで物理層のシミュレーションを行う方法を説明し、Wiresharkでシミュレーションの結果を確認しました。
Zephyr OSで開発したコードに対して直接シミュレーションを適用することができるのは、非常に便利だと思います。
また、今回は説明しきれませんでしたが、BabbleSimでは[屋内環境の無線伝搬のシミュレーション](https://github.com/BabbleSim/ext_2G4_channel_Indoorv1)など様々な機能が提供されています。
興味のある方は、[公式ドキュメント](https://babblesim.github.io/)をご参照ください。

※本記事に掲載の商品、機能等の名称は、それぞれ各社が商標として使用している場合があります。