プログラミング言語Rustは、低レベルのシステム開発向けに作られたぬるぽが発生しないメモリーセーフなプログラミング言語です。その利用範囲はシステム開発に留まらず、バックエンド開発やフロントエンド開発などにも広く使われはじめています。
人気急上昇ですね。
最近「実践Rustプログラミング入門」を入手して勉強していまして、Chapter8「組込みシステム」でLチカのお題(Cortex-M4)が出てくるのですが、ArduinoでもRustできないかなと思い調べてみました。
MAC環境(Docker未使用)で、Rust+ArduinoでLチカする方法について纏めています。
初心者向けにサクっと実行できる様に書いたつもりです。
「そういえばArduino押入れで眠ってるわ」という方は是非お試し下さい。
ArduinoでLチカ
まずはRust無しで、Arduinoを使ってふつーにLチカしてみましょう。
(MACとArduinoが接続されているポート名を調べる為なので、不要な場合は飛ばして下さい)
Arduino IDE
Arduino公式からSOFTWARE->DOWNLOADを選び、MAX OS X 用をダウンロードします。
ダウンロードしたzipを解凍し、Arduino.appを起動しましょう。
この時、Arduino本体はUSBに接続しておいて下さい。
そして、左上から 開く->01.Basics->Blinkを選択し、Lチカのサンプルプログラムコードを読み込みます。
最後に、左上の「マイコンボードに書き込む」ボタンを押すと、Arduinoへの書込みが始まります。
(初回は、USBポートの選択画面が出ると思いますので、選択して接続を試して下さい。)
ここで、お使いのMACがどのポートでArduinoとつながっているのかを調べておきます。
Arduino-IDEの画面右下を見て下さい。接続ポート名が表示されている筈です。
ここでは「/dev/cu.usbmodem141401のArduino Uno」と書かれているので、Arduinoが繋がっているポートは「/dev/cu.usbmodem141401」である事が分かります。後ほど使いますので、メモしておきましょう。
Rust+ArduinoでLチカ
ここまででお腹いっぱいにならなかった方は、続いてRustを使ったLチカにトライしてみましょう。
Arduino-IDEはRustに対応しておらず、cargoを使ってビルドしてやる必要があります。
環境構築
Rust
まずはRust環境をMACにセットアップします(普段からRustを使っているなら不要だと思います)。
rustup公式にアクセスし、curlコマンドパラメータをコピペしてMAC上のターミナルで実行します。
インストールに成功すれば、以下のコマンドが使える様になります。
cargo
rustup
rustc
avr-gcc
Arduinoには、AVRファミリのマイクロコントローラーATmegaシリーズが搭載されています。
avr-gcc は、ATmega用のコンパイラです。後述のblinkでビルドする際に必要です。
(※このサイトを参考にしました)
以下のコマンドを実行すれば、avr-gccコマンドが使える様になります。
brew tap osx-cross/avr
brew install avr-gcc
クロスコンパイラ(Rust with AVR support)
AVRをRustでビルドできる様にする為、Rust with AVR supportをセットアップします。
MAC用の手順はコチラに記載されている通りです。
MACではビルド構成都合上、事前に「/usr/local/avr-rust」フォルダを手動で作成しパーミッション設定する必要があります。
以下のコマンドでそれらを実行します。
sudo mkdir /usr/local/avr-rust && sudo chown ${USER}:admin /usr/local/avr-rust
また、以下のコマンドで coreutils がインストールされている必要がある様です(私は不要でした)。
brew install coreutils
では、avr-toolchainをビルドしていきましょう。
avr-rust/rust.gitをcloneします。オプション(--recursive)が付いている点に注意。
# Grab the avr-rust sources
git clone https://github.com/avr-rust/rust.git --recursive
ビルド用フォルダを作成し、カレントディレクトリをbuildに変更します。
# Create a directory to place built files in
mkdir build && cd build
自作したフォルダbuildは、git cloneで生成されたフォルダ(rust)と同階層である点に注意して下さい。
参考:
avr-rust
├── build
└── rust
confiugreでMakefileを生成します。
# Generate Makefile using settings suitable for an experimental compiler
../rust/configure \
--enable-debug \
--disable-docs \
--enable-llvm-assertions \
--enable-debug-assertions \
--enable-optimize \
--enable-llvm-release-debuginfo \
--experimental-targets=AVR \
--prefix=/usr/local/avr-rust
makeします。ちなみに私の環境では小一時間かかりました。
# Build the compiler, install it to /usr/local/avr-rust
make
make install
これで、makeされたavr-toolchainが「/usr/local/avr-rust」に配置されました。
最後に、avr-toolchainを登録します。
# Register the toolchain with rustup
rustup toolchain link avr-toolchain /usr/local/avr-rust
# Optionally enable the avr toolchain globally
rustup default avr-toolchain
これで、avr用のRustプログラムをビルドできる様になりました。
avrdude
MACでビルドしたRust実行ファイル(.elf)をArduinoに書き込む為のツールです。
以下のコマンドで使える様になります。
brew install avrdude
ビルド(rust)
準備が整ったので、公式サンプル「blink」でLチカしてみましょう。
blinkサンプル(ruduino)
blink(点滅)は、ArduinoLでチカ用のRustサンプルプログラムです。
ATmega用モジュール(.elf)を生成する為の環境が整っていて便利です。
クローンします。
git clone https://github.com/avr-rust/blink.git
フォルダ「blink」が生成されます。
参考:私の環境では、以下の様なフォルダ構成にしました。
avr-rust
├── blink
├── build
└── rust
カレントディレクトリを「blink」に変更します。
cd ../blink
では、ビルドしましょう。
ビルドはnightlyで行う必要がある為、以下のコマンドを使います。
rustup override set nightly
# Compile the crate to an ELF executable.
cargo build -Z build-std=core --target avr-atmega328p.json --release
ビルドに失敗する場合は、以下のコマンドを実行した後、再びビルドしてみて下さい。
rustup component add rust-src
ビルドに成功すると、「target/avr-atmega328p/release/blink.elf」が生成されます。
これをArduinoに書き込みましょう。
Arduinoへ書き込む
MACからArduinoへ書き込むには、「avrdude」コマンドを使用します。
(参考サイト)
まず、MACとArduinoが繋がっているポート名を思い出してください。
ポート名の調べ方は、本記事の上記「ArduinoでLチカ」で説明しています。
ここでは、「/dev/cu.usbmodem141401」であるとして話を進めます。
Arduinoへ書き込む
ArduinoがMACと繋がっている状態で、コマンドを実行します。
「-P/dev/cu.usbmodem141401」の部分は、自分の環境用に適時修正して使って下さい。
avrdude -patmega328p -carduino -P/dev/cu.usbmodem141401 -b115200 -D -Uflash:w:target/avr-atmega328p/release/blink.elf:e
blinkを改造しよう
しかし、先程の「ArduinoでLチカ(非Rust)」と結果が同じで分かりにくいですよね。
少し改造してみましょう。
blink/src/main.rsを、以下の内容に上書きして下さい。
#![feature(llvm_asm)]
#![no_std]
#![no_main]
use ruduino::cores::atmega328 as avr_core;
use ruduino::Register;
use avr_core::{DDRB, PORTB};
#[no_mangle]
pub extern fn main() {
let mut param = 0;
// Set all PORTB pins up as outputs
DDRB::set_mask_raw(0xFFu8);
loop {
// Set all pins on PORTB to high.
PORTB::set_mask_raw(0xFF);
small_delay(param);
// Set all pins on PORTB to low.
PORTB::unset_mask_raw(0xFF);
small_delay(param / 2);
param += 50000;
if param >= 800000 {
param = 0;
}
}
}
/// A small busy loop.
fn small_delay(atai: u32) {
//for _ in 0..400000 {
for _ in 0..atai {
unsafe { llvm_asm!("" :::: "volatile")}
}
}
時間経過と共にLEDの点滅速度を遅く変化させる様に変更しました。
これを再びビルドし、Arduinoへ書き込んで見ましょう。
点滅の速度が変化しましたね!ちゃんとRustで動いている様です。
ちなみにこのgifアニメよりもっと遅く点滅すると思います(撮り直しが面倒だったので)
方法その2(avr-hal)
今度はこの例を試してみましょう。同じ様なLチカですが、実装方法の異なる例です。
こちらの方が、どちらかというと「実践Rustプログラミング入門」での紹介例(但しこちらはArduinoでは無くCortex-M4)に近いと思います。
この例の使い方はこのブログに纏められていますが、ビルド環境はMACではなく Arch Linux で書かれています。
ここでは、MACでビルドしてLチカできる様に解説します。
rust-arduino-blink
早速クローンしていきましょう。
git clone https://github.com/creativcoder/rust-arduino-blink.git
フォルダ「rust-arduino-blink」が生成されます。
※参考:この時点での私の環境は、以下の様なフォルダ構成です。
avr-rust
├── blink
├── build
├── rust
└── rust-arduino-blink
カレントディレクトリを「rust-arduino-blink」に変更します。
cd ../rust-arduino-blink
この構成の特徴は、フォルダ「.cargo」の中に「config.toml」が格納されている点です。
ビルド時の各種設定が書かれており、 cargo build 時に余計なオプションを付けずにビルド出来ます。
このブログに書かれている、ビルドに必要な「pacman -S avr-gcc」などはArchLinuxでの方法なのでここでは飛ばしていきます(MACでのインストール方法(brew)は本記事の上の方で説明しています)。
さっそくビルドしてみましょう。
rustup override set nightly
cargo build
これで、target配下(今度はdebug)に.elfファイルが生成されました。
※追記:
ビルド時、下記エラーが出る場合がある様です。
error: language item required, but not found: `eh_personality`
これは、rustup toolchain 等の設定により起こる事がある様なのですが、具体的な発生理由は分かりませんでした。
ただ、発生した際は、Cargo.tomlに以下を追記する事で解消します。
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
※こちらの記事を参考にしました。
Arduinoに書き込む為、avrdudeコマンドを使いましょう。
※ポート名を自分の環境に合わせる事と、.elfファイルのファイル名・パスが違う点に注意して下さい。
avrdude -patmega328p -carduino -P/dev/cu.usbmodem141401 -b115200 -D -Uflash:w:target/avr-atmega328p/debug/rust-arduino-blink.elf:e
Arduinoへの書込みが成功すると、
ブログ記事と同じ様にLチカしました!
※こっちのLチカも、実装は違いますが点滅速度が徐々に遅くなる感じのやつです。点滅速度や動きで違いを感じて下さい。Lチカでは出来る事は限られますよね〜😷
環境を戻すには
avrでクロスコンパイルする為にrustupコマンドで色々変更を加えた為、もしかしたらArduino以外の普通のビルドが通らなくなっているかもしれません。以下のコマンドで元に戻しておきましょう(必要に応じて実行して下さい)。
rustup default stable
まとめ
Rustを使ってArduinoでLチカする方法を2つご紹介しました。
・1つめ(https://github.com/avr-rust/blink) → ruduino
・2つめ(https://github.com/creativcoder/rust-arduino-blink) → avr-hal
「実践Rustプログラミング入門」(github)では、2つめの手法に近いサンプルが掲載されています。
おそらく2つめの手法(avr-hal, .cargo/config.tomlを使用する例)が一般的(?)になるのかな、と思います。
「実践Rustプログラミング入門」のSection8-5「組込み向けクレート」では、組込み環境で使うクレート(alloc, heapless, panic等)の解説が纏められていて、Lチカから一歩先に進む為の情報が得られ、Arduino解説ではないですがとても参考になります。この本は入門書としての読みやすさもバツグンですので、興味ある方は入手してみて下さい。
Lチカ女子の参考になれば幸いです😁