Rustでシリアル通信する

2023.06.16

Introduction

諸事情によりRustでシリアル通信をしようと思い、
serialportクレートを使ってみた記録です。

Environment

今回試した環境は以下のとおりです。

  • MacBook Pro (13-inch, M1, 2020)
  • OS : MacOS 12.4
  • Rust : 1.70.0

Setup

私のつかっているPCにはシリアルポートがないので、
仮想的なシリアルポートを用意する必要があります。
socatを使えば用意できるので、Homebrewでインストールします。

% brew install socat

socatコマンドで仮想シリアルポートを作成します。

% socat -d -d pty,raw,echo=0 pty,raw,echo=0

2023/06/09 12:39:00 socat[18488] N PTY is /dev/ttys006
2023/06/09 12:39:00 socat[18488] N PTY is /dev/ttys007
2023/06/09 12:39:00 socat[18488] N starting data transfer loop with FDs [5,5] and [7,7]

「-d -d」でデバッグ出力用オプションを指定して2つの仮想シリアルポートを作成します。
ここでは/dev/ttys006と/dev/ttys007の2つのポートが作成されました。

Create Rust Program

ではCargoでRustプロジェクトを作成します。

% cargo new rust-serial

Cargo.tomlにserialportクレートを追記します。
(cargo add serialportでもOK)

[dependencies]
serialport = "4.2.1"

main.rsに、さきほど作成した仮想シリアルポートにするコードを記述します。
作成したポートのうち、1つを読み取り側、もう1つを書き込み側として使います。

use std::error::Error;
use std::io::prelude::*;
use std::time::Duration;

fn main() -> Result<(), Box<dyn Error>> {
    let port_name_read = "/dev/ttys006";//socatで作成したポート  
    let port_name_read2 = "/dev/ttys007";//socatで作成したポート 


    let mut port1 = serialport::new(port_name_read, 0)
    .timeout(Duration::from_millis(100))
    .open()?;

    let mut port2 = serialport::new(port_name_read2, 0)
    .timeout(Duration::from_millis(10))
    .open().expect("Failed to open port");

    let mut buf: Vec<u8> = vec![0; 1000];
    let output = "hello".as_bytes();
    loop {
        println!("Write Buffer.");
        match port1.write(output) {
            Ok(_) => std::io::stdout().flush()?,
            Err(e) => eprintln!("{:?}", e),
        }

        println!("Read Buffer.");
        match port2.read(buf.as_mut_slice()) {
            Ok(t) => {
                let bytes = &buf[..t];
                let string = String::from_utf8(bytes.to_vec())?;
                //println!("bytes: {:?}", bytes);
                println!("string: {:?}", string);
            }
            Err(e) => eprintln!("{:?}", e),
        }
        std::thread::sleep(Duration::from_millis(1000));
    }
}

実行してみます。
シリアルポートへの送受信ができていることがわかります。

% cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.08s
     Running `target/debug/serial`
Write Buffer.
Read Buffer.
string: "hello"
Write Buffer.
Read Buffer.
string: "hello"
・
・

Summary

Rustでシリアルポート通信ができました。
しかし今回、通信時にbaud_rate(シリアル通信時に送受信速度を表すパラメータ)を
指定するとエラーになってしまいました。(なので0を指定している)
※serialport::newの第2引数

これが環境の問題なのかsocatで作成したからなのか不明なので
実際にシリアルポートがあるPCで試してみたいところです。

References