ArmボードでBluetoothを動かすためにRustのBlueZ公式クレート"BlueR"を使って自作アプリをクロスコンパイルする ①サンプルアプリのビルドまで

　Qiita初投稿です。独学のため用語や説明が間違っているかもしれませんが、ググっても初心者の私が理解できる情報が少なくて悲しくなったので記事にしていこうと思います。

## はじめに
　LinuxでBluetoothを使用する開発をする上で、Bluetoothスタックの選択肢はほぼBlueZ一択なのが現状です。しかし最近のBlueZはhcitool等のツール群が廃止され、少し難易度の高いbluetoothctlしか提供されていないようです。使いづらかったのか、現在でもディストリビューションによってはhcitool等が配布されていることもあるようです。
　私も最初はbluetoothctlで動かしていましたが、動作確認以上のことをしようとすると途端に何もわからなくなってしまったため、BlueZがD-busで提供しているAPIを叩いて自分で開発することにしました。

## BlueR
　BlueZ公式クレートとしてGitHubに公開されています。


https://github.com/bluez/bluer

　BlueRからBluetoothモジュールまでの流れです。カーネルは登場してないですがBlueZはLinuxカーネルにも含まれており、実装したい機能によってはカーネルコンフィグを触って有効にする必要が出てきます。

Bluetoothモジュール
　　　｜ USB接続とかUART接続とか
デバイスドライバ
　　　｜ hciインターフェース
　　BlueZ
　　　｜ D-bus通信
　　BlueR

　D-Bus通信から先を実装する方法は何を使用してもよかったのですが、Rustに興味があったのとサンプルコードが良さそうに見えたこと、BlueZ公式の提供という安心感があったので選択しました。参考までにD-Bus通信の実装方法を比較した感想を書いておきます。

#### dbus-send
　コマンドラインツール。動作確認ぐらいはできそう。

```
dbus-send --print-reply --system --dest=org.bluez /org/bluez/hci0 --type=method_call org.freedesktop.DBus.Properties.GetAll string:org.bluez.Adapter1
```

#### C/C++
　D-Bus通信の利便性が弱く、PythonやRustでは隠蔽できる部分まで実装する必要がありそう。仮に選んだ場合はsdbus-cppを使用すると良さそう。

https://www.silex.jp/blog/wireless/2017/01/d-bus.html

　sdbus-cppを使用した例
https://github.com/weareaudiofile/bluez-dbus-cpp

#### Python
　検索すると一番情報が出てくる。d-busを使用する例はBlueZのexampleに入っているが、私の環境では動かなかった。bluezeroというライブラリが良さそう。

https://github.com/ukBaz/python-bluezero

## ビルド環境の作成
　今回のビルド環境とターゲット環境です。

　　ビルド環境：Debian 11 (amd64)
　　ターゲット環境：Debian 11 (aarch64)

　Debian 11で揃える必要はありませんが、glibcのバージョン問題があるので揃えたほうが楽です。基本的にはターゲット環境と同じか古い環境にしておけば大丈夫のはずです。私は古いPCが余っていたので直接Debianをインストールしていますが、仮想環境で構築してもよかったと思います。
　また、Rustのクロスコンパイルで検索するとCrossを使用する方法が出てきますが、Crossは用意してくれているDockerのイメージをダウンロードしてビルドに使用します。BlueRのビルドにはsysrootにD-busのライブラリを入れる必要があるので、ビルド用のイメージを編集、もしくは新しく用意する必要があります（私が手段を知らないだけかも）。Dockerイメージを編集するより手元の環境で作ってしまったほうが早そうなので、Cargoでクロスコンパイルしています。

### コンパイラ等のインストール
　ビルド環境に導入します。
~~~
sudo apt install -y build-essential g++-aarch64-linux-gnu
~~~

### Rust環境のインストール
　ビルド環境にRustをインストールします。公式のスクリプトを使用することをお勧めします。aptで入れたら後で詰みました。途中に出てくる選択肢は"1"のdefaultを選んでます。

~~~
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
~~~

### Cargoのコンフィグ編集
　configファイルを新規作成してください。
　Proxy環境の人はコメントアウトしているproxy設定を使用してください。
~~~$HOME/.cargo/config
#[http]
#proxy = "http://(ip addr):(port)"

[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
~~~

### Rust環境にaarch64環境を追加
　rustupを使用してインストールします。おそらく要インターネット接続。
~~~
rustup target add aarch64-unknown-linux-gnu
~~~

### Hello Worldで動作確認
　cargoで新規プロジェクトを作成すると初期状態でHello Worldが動くようになっているので、これをクロスコンパイルしてターゲット環境で実行してみます。
~~~
cargo new --bin hello
cd hello
cargo build --target=aarch64-unknown-linux-gnu
~~~
　ビルドされた実行ファイルは./target/aarch64-unknown-linux-gnu/debug/helloに配置されているはずです。
　参考まで、---releaseオプションを付けるとリリース用のビルドになり、./target/aarch64-unknown-linux-gnu/release/helloに配置されます。

　実行ファイルをターゲット環境に転送したら、実行権限を付けて動かしてみましょう。
~~~
chmod +x hello
./hello
~~~
　Hello, Worldが表示されたら成功です。

## BlueRのクロスコンパイル
　それではBlueRを持ってきて、同梱のサンプルアプリをクロスコンパイルします。
　まず、依存ライブラリをsysrootに追加します。
~~~
sudo dpkg --add-architecture arm64
sudo apt update
sudo apt install libdbus-1-dev:arm64
~~~
　これで/usr/aarch64-linux-gnuにd-busのライブラリがダウンロードされます。

　次にBlueRのダウンロードは公式の指示に従って、gitに--recursiveオプションを付けてダウンロードします。
~~~
git clone --recursive https://github.com/bluez/bluer.git
~~~
　ダウンロードが終わったら、bluerフォルダに移動して、cargoでビルドしていきます。まず環境変数にsysrootの設定を行います。cargoのコマンドは--targetでターゲット環境を指定してクロスコンパイル、--all-featuresはBlueRの指示、--exampleでビルドするサンプルアプリを指定しています。今回ビルドするle_advertiseはBluetooth Low Energyのビーコンを送信するアプリです。送信しているビーコンはスマートフォンのアプリ等で確認できます。
~~~
export PKG_CONFIG_PATH=/usr/aarch64-linux-gnu/pkgconfig
export PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu  
cargo build --target=aarch64-unknown-linux-gnu --all-features --example le_advertise
~~~
　ビルドされたサンプルアプリの配置先は./target/aarch64-unknown-linux-gnu/debug/examples/le_advertiseです。Hellow Worldの時と同じように転送して実行権限を付与して実行してみましょう。
~~~
chmod +x le_advertise
./le_advertise
~~~

　実行に成功すると下記のようなログが出てBluetoothビーコンが送信されます。XX:XX:XX:XX:XX:XXにはBluetoothモジュールのMACアドレスが入ります。サンプルアプリはEnterを押して実行を終了するとビーコンの送信も止まります。ビーコンには、固定のUUIDと"le_advertise"というデバイス名のデータが入るようです。
~~~
Advertising on Bluetooth adapter hci0 with address XX:XX:XX:XX:XX:XX
Advertisement { advertisement_type: Peripheral, service_uuids: {123e4567-e89b-12d3-a456-426614174000}, manufacturer_data: {}, solicit_uuids: {}, service_data: {}, advertising_data: {}, discoverable: Some(true), discoverable_timeout: None, system_includes: {}, local_name: Some("le_advertise"), appearance: None, duration: None, timeout: None, secondary_channel: None, min_interval: None, max_interval: None, tx_power: None, _non_exhaustive: () }
Press enter to quit
~~~

### スマートフォンで確認する場合
　スマートフォンでビーコンを簡単に確認したいときはnRFのツールとかルネサスのGATT Browserとかのアプリが使えるはずです。

https://play.google.com/store/apps/details?id=com.renesas.ble.gattbrowser&hl=en_US

　2回目とか何回か実行していると、ビーコンの挙動がおかしくなることがあるようです。その際はbluetoothctl等からアダプタを手動でpower offしてから再度実行するとうまくいきます。
~~~
bluetoothctl
[bluetooth]# power off
Changing power off succeeded
[bluetooth]# exit
~~~

　以上でBlueRのサンプルアプリをクロスコンパイルするとこまで終了です。
　次の記事はle_advertiseをもとに好きなビーコンを出せるアプリを作成してみたいと思います。

## おわりに
　ググった情報をかき集めながら、何とかサンプルアプリをビルドするのに1週間もかかりました。
　Rustを初めて触りましたが、CやPythonよりも一度動けばすんなり動いてくれる印象です。また、ビルドエラーのメッセージが情報満載で感動しました。
　BlueZはとにかくとっつきにくくて、公式のドキュメントは少なく、わからなかったらコードを読めの文化のようです。ただ公式がBlueRという使いやすいものを提供し始めたので、今後はもっとBluetooth開発が流行るといいな。
### 
### 　
### 　
### 　
　記事書くのって疲れるね。Markdownでこんなに書いたのも初めてかも。プログラムの作成を進めてますが、Rustが想像よりも良かったので本を買って勉強中です。

## 参考記事
https://qiita.com/tajihiro/items/8bc012fa2a91b7f82da2

https://qiita.com/reoring/items/700942f979ccf2aae334