LeafonyをRustで動かせたので情報共有です

この記事の内容です。

* LeafonyのBasic KitがRustで動いた。

* Lチカできた

* シリアル通信できた

* I2C通信できた


最近、Rustの学習をArduinoではじめました。
もう少しで<b>書籍：基礎から学ぶ-組込みRust</b>も発売されますし、今、組み込みRustが熱い、と個人的に思っています。
[基礎から学ぶ-組込みRust](https://www.amazon.co.jp/基礎から学ぶ-組込みRust-中林-智之/dp/4863543379)

Rust、組み込みRustに興味ある方になにか参考になれば嬉しいです。
それではLet's Rust!!!


### 対象読者
* Rust、組み込みRust初学者

* Rustで動くIoTエッジデバイスはないかな?と思っている方


### Leafonyとは何か?
Leafonyについてはこちらを参照ください。
[Leafonyとは](https://docs.leafony.com/docs/overview/ "Leafonyとは")

小型の可愛らしい基板(リーフと呼ばれる)をスタックしていくことで機能を自由に組み合わせ可能なデバイスです。
これです。
![Lefofy-2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171866/9109feee-5204-b5c3-419e-33f10944cf61.jpeg)


Basic Kitはケース付きです。何気に嬉しい・痒いところに手が届く・重宝します!!!

IoTのエッジが簡単に出来ると謳われています。
制御マイコンはArduino Unoと同じATmega328PまたはESP32です。
この記事はATmega328PでRustを動かしました。


### なぜターゲットをLeafonyにしたのか?
1年前にelchikaさんのキャンペーンでLeafonyのキットが当選し手元にあったこと、最近ArduinoでRustを学び始めていたからです。

[Leafonyプレゼントキャンペーン](https://elchika.com/dic/Leafonyプレゼントキャンペーン)

LeafonyのBasic Kitでは次の基板から構成されており色々試行錯誤できるかも・・・と思ったことも理由です。
[Leafony Basic Kit](https://docs.leafony.com/docs/products/basic/)

Bluetooth、温度・湿度・照度・加速度センサなどのリーフで構成されています。

Basic Kitのサンプルプログラムはこちらでも紹介されています。
[Leafony Basic Kitサンプルプログラム](https://docs.leafony.com/docs/examples/)

### 開発環境
#### ベースにしたソースコード
ソースコードはこちらをベースにさせてもらいました。

[ベースにしたソースコード avr-hal](https://github.com/Rahix/avr-hal)

#### ホスト環境
WSL 1。Ubuntu-20.04で確認。

※かなり変則的ですがMacbook ParallelsでWindows10 64bitをゲストOSにしています。
ゲストOSのWindows10にWSL, Ubuntu-20.04を導入し、そこにRustをインストールしました。


#### Rustのバージョン
インストールしたバージョンは次の通りです。
<pre><code>k-abe@KOJIABEC31D:~/avr-rust/leafony_rust$ rustup show
Default host: x86_64-unknown-linux-gnu
rustup home:  /home/k-abe/.rustup

installed toolchains
--------------------

stable-x86_64-unknown-linux-gnu (default)
nightly-2021-01-07-x86_64-unknown-linux-gnu
nightly-x86_64-unknown-linux-gnu

active toolchain
----------------

stable-x86_64-unknown-linux-gnu (default)
rustc 1.50.0 (cb75ad5db 2021-02-10)</pre></code>

ただし、こちらのREADMEに記載あるようにnightly-2021-01-07をインストールしビルドしています。
[avr-hal](https://github.com/Rahix/avr-hal)

### 試したこと
ソースコードはこちらに置きました。
[Leafony Rustソースコード](https://github.com/grace2riku/avr-hal/tree/add_leafony)

今回、ベースのソースコードから変更したのは次の2つのソースコードです。

1. boards/arduino-uno/examples/Battery_Voltage.rs
次のLeafonyのバッテリ電圧読み取りサンプルプログラム相当をRustで書いてみました。
[Leafony バッテリ電圧読み取りサンプルプログラム](https://docs.leafony.com/docs/examples/beginner/1_p/basic/read_battery/)
I2Cでバッテリ電圧読み取り、バッテリ電圧をシリアル通信で送信、1秒間隔でLED点滅しています。


2. boards/arduino-uno/src/lib.rs
ベースのソースコードはArduino unoでクロックは16MHzです。
Leafonyの制御マイコンはArduino Unoと同じATmega328Pですがクロックは8MHzです。
シリアル送信、I2C、Delay関数は16MHz設定となっているので8MHzに変更します。

具体的な変更内容はこちらを参照ください。
[Leafony Rust変更内容](https://github.com/grace2riku/avr-hal/commit/d448326a16d5c5aa0f8b0be678017675d2cb5d11)

LeafonyオリジナルサンプルコードとRustで少し実装の違いがあります。

* i2Cのリード

　Leafony Basic Kitに同梱されている【AV01 CR2032】リーフのバッテリ電圧を読み取ります。
[電源リーフ【AV01 CR2032】](https://docs.leafony.com/docs/leaf/power/av01/)

　ソースコード先頭の
<pre><code>const BATT_ADC_ADDR: u8 = 0x50;</pre></code>
　はAV01 CR2032リーフに実装されているADコンバータADC081C027CIMKのI2Cアドレスです。ATmega328Pがマスター、ADコンバータがスレーブです。

　Rust版はi2Cのリードでデータ長を指定していません。あまりよく理解できていませんが引数にスライス(?)を指定しています。
<pre><code>		i2c.read(BATT_ADC_ADDR, &mut receive_buffer[0..2]);</pre></code>
　正しいのかどうか確信がないですが後述の理由で一応、このコードでバッテリ電圧を読めているようです。


　オリジナル版 i2Cリード処理の抜粋
<pre><code>
  Wire.requestFrom(BATT_ADC_ADDR,2);
  uint8_t adcVal1 = Wire.read();
  uint8_t adcVal2 = Wire.read();
</pre></code>


* キャスト

　Rust版は【as u32】キーワードでキャストしています。
<pre><code>		let temp_millivolt = ( ( (receive_buffer[0] << 4) | (receive_buffer[1] >> 4) ) as u32 * 3300 * 2) / 256; </pre></code>

　オリジナル版
<pre><code>double tempMillivolt = ((double)((adcVal1 << 4) | (adcVal2 >> 4)) * 3300 * 2) / 256;</pre></code>


* シリアル送信データのフォーマット

オリジナルのサンプルプログラムは浮動小数点のフォーマットでシリアルデータ送信していますが、Rust版では整数としています。

Rustでもオリジナルと同じようにしたかったのですがコンパイルエラーを解消することができなかったので整数にしました。


#### ビルド
ビルドはREADMEの通りにしました。

<pre><code>k-abe@KOJIABEC31D:~/avr-rust/avr-hal_fork_add_leafony/boards$ cd arduino-uno/
k-abe@KOJIABEC31D:~/avr-rust/avr-hal_fork_add_leafony/boards/arduino-uno$ cargo +nightly-2021-01-07 build --example Battery_Voltage
   Compiling arduino-uno v0.1.0 (/home/k-abe/avr-rust/avr-hal_fork_add_leafony/boards/arduino-uno)
warning: crate `Battery_Voltage` should have a snake case name
  |
  = note: `#[warn(non_snake_case)]` on by default
  = help: convert the identifier to snake case: `battery_voltage`

warning: unused `core::result::Result` that must be used
  --> boards/arduino-uno/examples/Battery_Voltage.rs:35:6
   |
35 |         i2c.write(BATT_ADC_ADDR, &adc_hedder);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(unused_must_use)]` on by default
   = note: this `Result` may be an `Err` variant, which should be handled

warning: unused `core::result::Result` that must be used
  --> boards/arduino-uno/examples/Battery_Voltage.rs:38:3
   |
38 |         i2c.read(BATT_ADC_ADDR, &mut receive_buffer[0..2]);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: this `Result` may be an `Err` variant, which should be handled

warning: 3 warnings emitted

    Finished dev [optimized + debuginfo] target(s) in 21.45s
</pre></code>

warningがでています。
良くないですが無視しました。
戻り値を処理してね、というwarningのようです。


#### elfからhexファイルを作成
WSLからLeafonyに書き込みできないよなと思ったのでWindowsから書き込みを行います。
書き込みは書き込みツールavrdudeの64bit版を使いました。
avrdudeはelfファイルでも書き込みできるだろうと思っていましたができませんでした。
そのため、ビルドでできたelfファイルからhexファイルを作成し、avrdudeで書き込みました。

次がelfからhexファイル作成コマンドです。
<pre><code>k-abe@KOJIABEC31D:~/avr-rust/avr-hal_fork_add_leafony/boards/arduino-uno$ avr-objcopy -S -j .text -j .data -O ihex ../../target/avr-atmega328p/debug/examples/Battery_Voltage.elf ../../target/avr-atmega328p/debug/examples/Battery_Voltage.hex</pre></code>


#### 書き込み
avrdudeでhexファイルを書き込みます。
Windowsのコマンドプロンプトを起動し、avrdudeがあるフォルダに移動します。
次のコマンドを実行し、hexファイルを書き込みます。

<pre><code>Z:\Documents\avr\avrdude-64>avrdude -carduino -PCOM5 -b 57600 -patmega328p -D -U flash:w:Battery_Voltage.hex:i

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.05s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: reading input file "Battery_Voltage.hex"
avrdude: writing flash (1848 bytes):

Writing | ################################################## | 100% 2.61s

avrdude: 1848 bytes of flash written
avrdude: verifying flash memory against Battery_Voltage.hex:
avrdude: load data flash data from input file Battery_Voltage.hex:
avrdude: input file Battery_Voltage.hex contains 1848 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 1.88s

avrdude: verifying ...
avrdude: 1848 bytes of flash verified

avrdude: safemode: Fuses OK (E:00, H:00, L:00)

avrdude done.  Thank you.</pre></code>


気をつけることはhexファイル書き込みのためコマンドラインの末尾が【i】になることです。
※elfファイルの場合は【f】。

次のサイトを参考にさせていただきました。
[avrdude参考](https://synapse.kyoto/tips/ArduinoISP_AVRWriter/page002.html)

#### 動作確認
書き込みが終了したら下図のようにLeafonyとPCをUSBケーブルで接続します。

![接続図.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171866/a74a23af-f1ab-2de4-578b-03be08bf7d90.jpeg)


Teratermなどのシリアルターミナルを起動し、通信条件を下図に設定します。

![lefony_teraterm.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171866/6d8911e6-eef8-00e7-148e-5416eeded43f.png)


そうするとシリアルターミナルにバッテリ電圧が表示されます。
電源リーフのスイッチをONにするとバッテリ電圧が読み取りできます。

![バッテリ電圧読み取り.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171866/522fd247-91fb-fbf6-17e3-eb2d7f5761cb.jpeg)


非Rust版のLeafonyオリジナルのバッテリ電圧読み取りサンプルプログラムを動かしたところ、バッテリ電圧のシリアルデータ表示は次のようになりました。

* 電源リーフのスイッチオフ時：0.05

* 電源リーフのスイッチオン時：2.91

Rust版では電源リーフのスイッチオン時：2964でした。1000で割り小数点形式だと2.964Vになります。

非Rust版のサンプルプログラムとRust版で近い値となっているのでI2Cでバッテリ電圧を読めていると判断できそうです。


### 最後に
長い文章を最後まで読んでいただきありがとうございました。
Rustをはじめようと思っている方、Rustで動くIoTエッジデバイスを探している方のヒントになれば嬉しいです。