LoginSignup
11
10

More than 5 years have passed since last update.

Arduino始めてみた+ArduinoとRustでシリアル通信してみた

Last updated at Posted at 2016-08-15

概要

今までハードウェアから逃げ続けていた人生だったが,
暇だったので立ち向かってみた.

その過程で,RustArduinoでシリアル通信もやってみた.

準備

ハードウェアって結構お金かかるのね…
とりあえず以下を購入して,手を動かしながら勉強できるようにした.
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,ArduinoProcessingで通信して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 的な)

main.rs
#![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の配列でしかできないようなので,
charu8に変換して渡している.

バンドレートは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側の処理が間に合っていないとかなんだろうか?
もやもやする…

11
10
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
10