Arduino UnoとPython3で通信する方法

Mac / PC とArduino Unoを通信させる方法です.たくさんの情報がネット上にはありますが,ここでも紹介しておきます.

Arduino側からPCへ

Serialを使ってArduinoは文字列を出力し,Python側はそれを読み取ります.

Arduino側

void setup() {
  Serial.begin(9600);
}

int data = 0;
void loop() {
  data++;
  Serial.println(data); 
  delay(1000);
}

コンパイルしてシリアルモニタで表示させると以下のように数字が増えながら表示されます.

1
2
3
4
5
6

PC / Mac

まずpython3の環境にpyserialをインストールします.

$ pip3 install pyserial

Windowsだったら単純に pip install pyyaml

コードは以下の通りです.

import serial

ser = serial.Serial('/dev/cu.usbmodem1101', 9600,timeout=None)
while True:
    line = ser.readline() # ここで一行データを取得するがbyte型
    stripped_str = str(line, 'ascii').strip() # byte型を文字列に変換して前後の空白改行除去
    data = int(stripped_str) # 文字列を数値に解釈し直す
    print(data)

/dev/cu.usbmodem1101の部分は自分のarduinoがつながっているポート名にする.WinならCOM1とか.Arduinoのシリアルモニタがつながっているとできません.

これのポイントは,データとしては「文字列」として送られているものを,python側で解釈してデータに変換していることです.文字列のままでは加減乗除のような計算が不能なので数値に解釈することが必要です.

たくさんのデータを送りたい

ADコンバータ等をたくさん使ってデータを送る場合は以下の通り

Arduino側

コンマで区切って一行に送りたいデータをすべて送ります.

void setup() {
  Serial.begin(9600);
}

void loop() {
  int data1 = analogRead(A1);
  int data2 = analogRead(A2);
  int data3 = analogRead(A3);
  int data4 = analogRead(A4);
  Serial.print(data1);
  Serial.print(',');
  Serial.print(data2);
  Serial.print(',');
  Serial.print(data3);
  Serial.print(',');
  Serial.println(data4);
  delay(1000);
}

シリアルモニタで確認すると以下のような表示です.

311,301,292,305
364,336,316,315
287,283,278,286
274,274,272,275

Python側

import serial

ser = serial.Serial('/dev/cu.usbmodem1101', 9600,timeout=None)
while True:
    line = ser.readline()
    stripped_str = str(line, 'ascii').strip()
    datas = [int(d) for d in stripped_str.split(',')]
    print(datas)

ポイントとしては [int(d) for d in stripped_str.split(',')]の部分ですね.これはリスト内包表記を使ってstripped_str.split(‘,’)で返ってきたリスト型データに繰り返し処理をしてリストに変換しています.要するにここでは「 ‘,’で区切られている文字列を’,’で分割したのち,それら一つ一つに対してint()関数を使って数値に解釈し,それらの結果をリストとしてdatasに代入します」という処理でした.出力は以下のようになります.

[365, 337, 317, 291]
[270, 272, 271, 267]
[260, 264, 265, 260]
[258, 263, 265, 258]
[257, 262, 264, 257]
[257, 262, 264, 257]
[365, 337, 317, 291]

PCからArduinoへ

ここはあまり難しい処理をしないでONとOFFの文字列をそれぞれ送ります.

Python側

import time
import serial

ser = serial.Serial('/dev/cu.usbmodem1101', 9600,timeout=None)
while True:
    ser.write('ON\n'.encode())
    time.sleep(1.0)
    ser.write('OFF\n'.encode())
    time.sleep(1.0)

Arduino側

こちらは文字列がONだったら,OFFだったら・・・という処理を記述します.

void setup() {
  // put your setup code here, to run once:
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  Serial.begin(9600);
  Serial.setTimeout(3000); /// 3 sec
}

void loop() {
  // put your main code here, to run repeatedly:
  String message = Serial.readStringUntil('\n');
  message.trim();
  if (message == "ON") {
    digitalWrite(13, HIGH);
  } else if (message == "OFF") {
    digitalWrite(13, LOW);
  }
}

ONの文字列だったらArduino基板上のLED (Pin 13) がひかり,OFFなら消える,という処理です.文字列を増やせばいろんな処理を送れます.

送受信が絡まり合う処理はお勧めしません

おすすめは,PCが送ったらArduinoが返す,という処理を繰り返すことです.こういう処理を「同期処理」と言います.基本的には同期処理でなんとかできるように考えてみてください.

これ以上の処理,たとえばPCが送らなくてもArduinoがデータを送るけど,時々受け取るデータに対してArduinoは処理を行なってほしい,などの要求がある場合は,たとえばSerial.available()関数などを使います.これ以上は詰め込みすぎだと思うのでこの辺で.