PC-Arduino間 Python経由 シリアル通信備忘録

#目的
本稿の目的は, Python を用いて `Arduino` とシリアル通信を行い, 更に同通信により得た値を数値として処理できるようにすることである.

# 目標
* PC側から `Arduino` にデータを送信, その内容に応じて `Arduino` の動作を変化させる
* PCが, `Arduino` から受信したデータを数値的に処理できるようにする

## 具体的目標
* PCから文字データを送信し, `Arduino`のLEDの明滅を操作するプログラムを組む
* 超音波距離センサ `HC-SR04` から得たデータから数値を取り出すプログラムを組む
* 上記プログラム言語には Python 3.x を利用する

# 準備
* Arduino Uno Rev.3
* USBケーブル
* LED
* ジャンパワイヤ 4本
* HC-SR04
* Python 3.x , Arduino IDE
* Python モジュール : PySerial

# 環境
* Windowsだがおそらく汎用的なので省略

# PC → Arduino　シリアル通信
1. Arduino の13番ピンとGNDにLEDをつなげる
 * LEDの足が長いほうが13番, 短い方はGND
2. 以下のプログラム ```Serial_Com_LED.ino``` をArdino IDEで書いて, Arduino に書き込む  
一旦このプログラムをArduino IDE上で実行し, シリアルモニタを用いてLEDの明滅を制御できるか確認する
3. 以下のプログラム```Serial_Com_LED.py```を作成する  
4. ```Serial_Com_LED.py```を実行すると, Arduino IDEで実行した際と同様の動作が得られる

```Serial_Com_LED.ino
void setup(){
  pinMode(13,OUTPUT); //13番ピンを出力に設定
  
  Serial.begin(9600);//シリアル通信のレートを9600に設定
}

void loop(){

  int inputchar;      //入力状態の読み取りに使う

  inputchar = Serial.read();  //シリアル通信で送信された値を読み取る

  if(inputchar!=-1){
    switch(inputchar){
      case 's':
        digitalWrite(13,HIGH);
        break;

      case 'q':
        digitalWrite(13,LOW);
        break;
    }
  }
  else{
  }
}
```

```Serial_Com_LED.py
#coding:utf-8

#シリアル通信で文字をArduino側に送信
#aが押されたら通信を中止するプログラム

import serial   #モジュール名はpyserialだが, importする際はserialである

def main():
    with serial.Serial('COM3',9600,timeout=1) as ser:
        while True:
            flag=bytes(input(),'utf-8')
            
            #シリアル通信で文字を送信する際は, byte文字列に変換する
            #input()する際の文字列はutf-8
            
            ser.write(flag)
            
            #シリアル通信:送信
            
            if(flag==bytes('a','utf-8')):
                break;
        ser.close()

if __name__ == "__main__":
    main()
```

# Arduino → PC シリアル通信
1. ピン接続  
ジャンパワイヤとブレッドボードを用いてそれぞれ以下のように接続する

|   Arduino  |   HC-SR04   | 
|:-----------|------------:|
|     5V     |     Vcc     |
|     GND    |     GND     |
|    Pin-9   |     Trig    |
|    Pin-10  |     Echo    |

2. 以下の```.ino```, ```.py``` プログラムを書き込む

```Serial_Com_Sonic.ino
#define echoPin 10 // Echo Pin
#define trigPin 9 // Trigger Pin
 
double Duration = 0; //受信した間隔
double Distance = 0; //距離
void setup() {
Serial.begin( 9600 );
pinMode( echoPin, INPUT );
pinMode( trigPin, OUTPUT );
}
void loop() {
  digitalWrite(trigPin, LOW); 
  delayMicroseconds(2); 
  digitalWrite( trigPin, HIGH ); //超音波を出力
  delayMicroseconds( 10 ); //
  digitalWrite( trigPin, LOW );
  Duration = pulseIn( echoPin, HIGH ); //センサからの入力
  if (Duration > 0) {
    Duration = Duration/2; //往復距離を半分にする
    Distance = Duration*340*100/1000000; // 音速を340m/sに設定
    Serial.println(Distance);
  }
  delay(500);
}
```

```Serial_Com_Sonic.py
#coding utf-8
import serial
import re
def main():
    with serial.Serial('COM3',9600,timeout=1) as ser:

        while True:
            c = ser.readline()
            d = re.findall('[0-9]+\.+[0-9]',str(c),flags=0)
            d = [float(i) for i in d]
            for i in range(0, len(d)):  #要素を1つずつ順番に出力します
                print(d[i])
            print
        ser.close()

if __name__=="__main__":
    main()
```

# 実行
HC-SR04 についてのプログラムで, list型ではなく, float型を返させるのに苦労しました
~~原理はわかりませんがとりあえず動いたので良しとします~~

* 以下の手続きを踏んでいる
    * ```ser.readline()```で, ```serial.Serial...```の出力を一行分読み込む
    * ```d = re.findall...``` で正規表現によって所望の出力を得る
    * re.findallが返す値の型はlist(str)であるため
        * リストから文字列を得る
        * floatに変換


# 引用, 参考
* http://denshi.blog.jp/arduino/python%E3%82%B7%E3%83%AA%E3%82%A2%E3%83%AB%E9%80%9A%E4%BF%A1
* http://bi.biopapyrus.net/python/syntax/list.html