ArduinoとPCの通信(シリアル通信)
この記事の目的
ArduinoとPC間でのシリアル通信についてまとめる
1.Arduinoのシリアル通信機能
ArduinoでPCと信号の送受信を行う場合、USBケーブルを使ったシリアル通信が最も便利な手法である。
Arduino標準のSerialライブラリを使い下記の関数で信号の受け渡しを行うことができる。
参考サイト https://garretlab.web.fc2.com/arduino_reference/language/functions/communication/serial/
API名 | リンク | 概要 |
---|---|---|
Serial.begin() | リンク | シリアル通信の初期化/bpsの指定 |
Serial.end() | リンク | シリアル通信の終了 |
Serial.setTimeout() | リンク | シリアル通信タイムアウト時間の指定 |
Serial.readStringUntil() | リンク | 指定した終端文字まで文字列を読み込む |
Serial.println() | リンク | 指定した文字列を出力する |
通信の速度はbpsで選択する。(300, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 115200 など。送受信に必要な周期とデータ量を考慮して設定する。)
上記の表で挙げたのは送受信するデータは文字列であることを想定している。数値データは文字列に変換しカンマ区切りのデータで一括して送信することができる。下記にサンプルコードを示す。下記はデータの桁数をあらかじめ定めておき、終端が区切り文字";"で出力されるデータの送受信をすることを想定している。
/* serial_com_test.cpp */ /* header file */ #include "stdafx.h" #include "serial_com_test.h" /* static variable */ static int apl_cyc_count = 0; #define READ_BUFSIZE (7) //受信バッファサイズ x.x,x.x; を想定(終端文字は含まない) #define SEND_BUFSIZE (50) //送信用バッファサイズ #define SEND_DATA_SIZE (6) //整数桁3 小数点1 小数点以下2 を想定 //デバッグ用変数 static float deb_val1 = 0.0; static float deb_val2 = 0.0; static int keta = 0; //通信の初期化用関数 void com_init (void) { //Serial Communication initialization Serial.begin(115200); //boudrate 115200 Serial.setTimeout(50); //read timeout for serial communication Serial.print("Serial Communication Start!"); return; } //シリアル通信読み込み用関数 void com_read (void) { char buff_A[4]; char buff_B[4]; if(Serial.available()) //読み込むデータが存在する場合 { //終端データ";"までのすべてのデータを読み込む String str_buf = Serial.readStringUntil(';'); keta = str_buf.length(); if (str_buf.length() == READ_BUFSIZE ) { str_buf.toCharArray(buff_A,4); deb_val1 = atoi(buff_A); Serial.println(deb_val1); str_buf.toCharArray(buff_B,4,4); //先頭バイトの指定 deb_val2 = atof(buff_B); Serial.println(deb_val2); } Serial.println(buff_A); Serial.println(buff_B); } return; } //シリアル通信読み込み用関数 void com_send (void) { float deb_send_val1; float deb_send_val2; char test_str[SEND_BUFSIZE]={'\0'}; //配列を初期化用データで埋める char *ptr = test_str; deb_send_val1 = deb_val1 + 10.0; deb_send_val2 = deb_val2 - deb_val1; /*Send Acceralation information*/ dtostrf(deb_send_val1,SEND_DATA_SIZE,2,ptr); ptr = ptr + SEND_DATA_SIZE; *ptr=','; //区切り文字_token ptr = ptr + 1; dtostrf(deb_send_val2,SEND_DATA_SIZE,2,ptr); ptr = ptr + SEND_DATA_SIZE; *ptr=';'; //end_token Serial.println(test_str); return; } /*arduino 初期化処理*/ void setup() { /*Initialize Communication */ com_init(); } /*arduinoメインループ*/ void loop() { //50msec 周期実行 interval<5000>::run([]{ switch (apl_cyc_count) { case 0: com_read(); break; case 1: //アクチュエータの制御などを実施(仮) break; case 2: com_send();; break; default: //no action //Serial.println("Default"); break; } if (apl_cyc_count>=2) { apl_cyc_count = 0; } else { apl_cyc_count++; } }); }
上記スクリプトで送受信した結果はArduino IDE / Teraterm などで確認することができる。
ここで、 PCのPythonスクリプトとArduinoを通信させたい場合はライブラリ『Pyserial』を利用すると便利である。
Pyserialは下記コマンドでインストールすることができる。
pip install pyserial
Pyserialではポート名とボーレート、タイムアウト時間を指定して関数readlineでバッファ読み込み、writeでバッファ書き込みを行う。
読み込みについては、通信状態によって不安定になることがあるため区切り文字までの文字数を明示的にしておくほうが良い。
#!/usr/bin/env python # -*- coding: utf-8 -*- import serial import threading import time import re ########################################### ### Serial Socket Communication Class ### ########################################### class Class_Serial_Socket(threading.Thread): ################################### #Connection information ########## COM_PORT_NAME = 'COM4' #for Windows #COM_PORT_NAME = '/dev/ttyACM0' #for Ubuntu BAURATE = 115200 ############################################# ### Serial Communication Initialization ### ############################################# def __init__(self): #Initialize value for threading threading.Thread.__init__(self) self.terminate_request = False #Vehicle Control Signals sending to Arduino self.debval1 = 0.0 #initialize debag value 1 self.debval2 = 0.0 #initialize debag value 2 self.counter = 1.0; #initialize serial communication socket information self.ser_sock = serial.Serial(self.COM_PORT_NAME,self.BAURATE, timeout=0.01, writeTimeout=0.01) self.send_flg = True self.RECEIVE_LENGTH = 16; time.sleep(3) #################################### ### Update RX data from Arduino ### #################################### def update_RX_data_from_arduino(self,str_b): try: str = str_b.decode() #Arduinoから受信した文字列のデコード #str_splitted =str.split(',') if (len(str) == self.RECEIVE_LENGTH): str_splitted = re.split('[,;]', str) self.debval1 = float(str_splitted[0]) #1つ目の数字の取得 print('debval1',self.debval1) self.debval2 = float(str_splitted[1]) #2つ目の数字の取得 print('debval2',self.debval2) except: pass ################################## ### Update TX data to Arduino ### ################################## def update_TX_buffer_to_arduino(self): #Generate sending buffer to Arduino #debug send_buffer = str(self.counter + 2.0) tempbuf = str(self.counter) send_buffer = send_buffer + "," + tempbuf send_buffer = send_buffer + ";" #for charactor end token self.counter = self.counter + 1.0 if self.counter > 5.0: self.counter = 1.0 try: self.ser_sock.write(str.encode(send_buffer)) except Exception as e: print("例外args:", e.args) # pass #print("write_error\n") ############################### ### Main Periodic Function ### ############################### def run(self): while(1): if self.terminate_request == True: break; #Finish (User request) if self.send_flg == True: #try: # self.update_TX_buffer_to_arduino() #except: # print("Serial Communication Error (PC->Arduino)") self.update_TX_buffer_to_arduino() self.send_flg = False else: str = "" try: str = self.ser_sock.readline() self.update_RX_data_from_arduino(str) except: print("Serial Communication Error (Arduino->PC)") self.send_flg = True time.sleep(0.050) if __name__ == '__main__': sock_controler = Class_Serial_Socket() #Initialize Serial Communication sock_controler.start()
サンプルコードはgithubで公開している。
次回はROS TopicをArduinoと送受信する方法について説明する。