#概要
今までハードウェアから逃げ続けていた人生だったが,
暇だったので立ち向かってみた.
その過程で,Rust
とArduino
でシリアル通信もやってみた.
#準備
ハードウェアって結構お金かかるのね…
とりあえず以下を購入して,手を動かしながら勉強できるようにした.
Arduinoをはじめよう 第3版 (Make:PROJECTS)
Arduinoをはじめようキット
Arduino拡張キット- Physical Computing Lab
大体1万円くらいだった.
Arduino拡張キット- Physical Computing Labは全く使わなかったので,
買わなくて良いかも.
#感想
Arduinoをはじめよう 第3版 (Make:PROJECTS)では,基本的にLEDを軸にして,
実際に回路を組み立てながら各種センサの紹介や仕組みの紹介をしていた.
回路作成は,Arduinoをはじめようキットで大半を作成できるが,
一番複雑な回路に関しては,別途電磁バルブなどを購入する必要がある.
また,キットについてくるArduino
とは違うバージョンのものに関する章があり,
そちらも必要に応じて別途購入する必要がある.
実際の回路作成の流れとしては以下のような感じだった.
1,LEDチカチカ(通称,Lチカ)
2,LEDをプッシュボタンで制御
3,LEDを光センサで制御
4,シリアル通信の簡単なテスト
5,Arduino
+Processing
で通信してLEDを制御
6,(やる人は)電磁バルブやRTCを用いて潅水ツール作成
初めてハードウェアを触る人間としては,
簡単ではあるものの,自力で回路を作成して動かせたので,割りと感動した.
また恥ずかしながら,他言語を組み合わせて一つのシステムを動かす,
という経験が今までなかったので,そちらも感動した.
電子回路に関する詳細な知識などは取得できないが,
入りとしては充分な教材だと思われる.
#Rust
と通信
さて,ここからが本番である.
最終的には,Rust
上で直接Arduino
の処理を記述できるような状態にしたいが,
とりあえず一番最初ということで,シリアル通信をしてみた.
(人のライブラリに乗っかっただけだが)
##環境
OS:Windows7
Rust:rustc 1.10.0 (cfcb716cf 2016-07-03)
Cargo:cargo 0.11.0-nightly (259324c 2016-05-20)
##準備
serial
というcrateが実は存在している.
仕組みはWinAPIで指定したポートと接続して,あとはデータを流し込んでいるだけのようだが,
自分で組もうとすると,シリアル通信周りの規格をしっかり調べる必要があるので,
今回はこちらを使用させていただくことにした.
cargo-edit
をインストールしている場合は,
該当するCargo.toml
がある場所でcmdを開いて,
以下を入力すればOK.
$cargo add serial
インストールしていない場合は,
該当するCargo.toml
を開いて,以下を記入すればOK.
crateのバージョンは適宜変えていただければ.
[dependencies]
serial = "0.3.4"
##コード
実装したコードは以下の通り.
送信用データの構成は,1行に16進数のRGB値が1つだけ渡される,という超簡易的なもの.
(例:白 -> #FFFFFF 的な)
#![allow(non_snake_case)]
#![allow(while_true)]
extern crate serial;
use std::thread;
use std::time::Duration;
use std::io::prelude::*;
use serial::prelude::*;
fn main()
{
//Arduinoが繋がっているCOMポートを指定.
let mut port = serial::open("COM3").unwrap();
//通信関連のオプション設定.
let optionFunc = &|settings: &mut SerialPortSettings|{
settings.set_baud_rate( serial::Baud9600 );
settings.set_char_size(serial::Bits8);
settings.set_parity(serial::ParityNone);
settings.set_stop_bits(serial::Stop1);
settings.set_flow_control(serial::FlowNone);
Ok(())
};
//コールバックで設定関数を呼び出す.
port.reconfigure( optionFunc );
//通信時のタイムアウト時間を設定.
port.set_timeout( Duration::from_millis( 1000 ) );
let mut hrgb: Vec<u8> = vec!( '#' as u8, 0, 0, 0, 0, 0, 0 );
let mut r: u8 = 0;
let mut g: u8 = 0;
let mut b: u8 = 0;
const U8_MAX: u8 = 255;
const HEX_VALUE: u8 = 16;
while( true ){
r = ( r + 1 ) % U8_MAX;
g = ( g + 1 ) % U8_MAX;
b = ( b + 1 ) % U8_MAX;
hrgb[1] = ConvertD2H( r / HEX_VALUE );
hrgb[2] = ConvertD2H( r % HEX_VALUE );
hrgb[3] = ConvertD2H( g / HEX_VALUE );
hrgb[4] = ConvertD2H( g % HEX_VALUE );
hrgb[5] = ConvertD2H( b / HEX_VALUE );
hrgb[6] = ConvertD2H( b % HEX_VALUE );
port.write( &hrgb );
port.flush();
thread::sleep( Duration::from_millis( 5 ) );
}
}
fn ConvertD2H( x: u8 ) -> u8
{
let mut tmp: u8 = 0;
if( 0 <= x && x <= 9 ){
let base: u8 = '0' as u8;
tmp = base + x;
}
else if( 10 <= x && x <= 15 ){
let base: u8 = 'A' as u8;
tmp = base + ( x - 10 );
}
tmp
}
serial
では書き込みがu8
の配列でしかできないようなので,
char
をu8
に変換して渡している.
バンドレートはArduino
側とRust
側で合わせる必要がある.今回は9600.
以下にArduino
側のコードも載せておく.
//入力.
const int PUSH_BTN = 12; //デジタル.
const int LIGHT_SENSOR = 0; //アナログ.
//出力.アナログ.
const int LED_1 = 11;
const int LED_2 = 10;
const int LED_3 = 9;
//ONOFF状態.
static bool g_bOnOff = false;
//RGB情報.
struct RGB
{
float r;
float g;
float b;
};
void setup()
{
Serial.begin( 9600 );
pinMode( PUSH_BTN, INPUT );
}
void loop()
{
//スイッチ入力確認.
if( GetTrigger() ){
g_bOnOff ^= true;
delay( 50 );
}
//色情報取得.
RGB color = GetColor();
if( !g_bOnOff ){
//OFF状態なら色情報を消す.
color.r = color.g = color.b = 0.0f;
}
//書き込み.
analogWrite( LED_1, color.r );
analogWrite( LED_2, color.g );
analogWrite( LED_3, color.b );
//光センサーで取得した明るさを送り返す.
Serial.println( (float)analogRead( LIGHT_SENSOR ) / 1024.0f * 255.0f );
}
bool GetTrigger()
{
static bool prev = false;
static bool now = false;
prev = now;
now = ( digitalRead( PUSH_BTN ) == HIGH );
return ( !prev && now );
}
RGB GetColor()
{
static RGB tmpRGB;
//一番最初だけ初期化する.
static bool bInit = false;
if( !bInit ){
tmpRGB.r = 0.0f;
tmpRGB.g = 0.0f;
tmpRGB.b = 0.0f;
bInit = true;
}
//通信があれば情報を更新.
if( Serial.available() > 0 ){
char c = Serial.read();
if( c == '#' ){
tmpRGB.r = ConvertF2D( Serial.read() ) * 16.0f + ConvertF2D( Serial.read() );
tmpRGB.g = ConvertF2D( Serial.read() ) * 16.0f + ConvertF2D( Serial.read() );
tmpRGB.b = ConvertF2D( Serial.read() ) * 16.0f + ConvertF2D( Serial.read() );
}
}
return tmpRGB;
}
float ConvertF2D( char c )
{
float tmp = 0.0f;
if( '0' <= c && c <= '9' ){
tmp = ( float )( c - '0' );
}
else if( 'A' <= c && c <= 'F' ){
tmp = ( float )( ( c - 'A' ) + 10.0f );
}
else{
Serial.print( c );
Serial.println( "is not compatible" );
}
return tmp;
}
上記のコードを実行すれば,LEDが徐々に明るくなっていく回路が動くはず.
(回路自体は,Arduinoをはじめよう 第3版 (Make:PROJECTS)の7章のものを作れば動く.)
(光センサはチェック用.上記コードでは何の役にも立っていない.)
##注意
理由は調査中だが,Rust
側からデータを送信すると,
一定時間経ったあとにデータが送信できなくなる,というバグがある.
Arduino
側の処理が間に合っていないとかなんだろうか?
もやもやする…