第八項 PCとの連携・シリアル通信基礎
電子工作創作表現(2019/6/14)
スライドPDF
パソコン<->Arduinoの通信
- 色々なソフトと連携する
- Processing, oF, Max/MSP, TouchDesigner...
Arduinoとパソコンの間で通信ができると、色々なソフトウェアと連携をとることができます。
今回はいくつかの通信手段を使って、パソコンと連携する方法を解説していきます。
USBシリアル通信
- 1バイトずつデータを送る、シンプルな方法
- 親戚にRS-232CやRS-485がいる
今までArduinoでデータをやり取りしていたUSBシリアル通信は、1対1の双方向通信をするための、シンプルでポピュラーな方法です。USBでシリアル通信をするのでUSBシリアルと言って、親戚のようなものにRS-232Cや、DMXのベースとなっているRS-485という規格のものがありますが、ここでやりとりするデータも基本的には同じ内容です。
プログラム上においてで複雑なルールは無く、1バイトずつデータをやりとりします。
「バイト」とは
- 2進数の0と1を8個並べたものが「1バイト」
- 0~255までの要素を表現できる
- 16進数では0x00~0xFFのように2桁で表現できる
バイトとはコンピュータ上で扱う数字の単位で、0と1だけで数を表す「2進数」の数字8桁を1バイトとしています。
この1バイトで表せる数字は10進数の0~255までです。Arduinoでは2進数を0b00001111のように0bを付けて表現することもできます。
ちなみに16進数も0x5Aのように0xを頭に付けて記述できますが、16進数が使われるのは1バイトを表記するのに2桁で収まって見やすいという理由があります。
2/8/10/16進数の相互変換
https://hogehoge.tk/tool/number.html
バイトの列を送る
- 0-255までの数字を1個ずつ送るのがシリアル通信
- データの意味や区切りなど、自分でルール(仕様)を決めてやり取りする
この1まとまりの数字を1個ずつ送れるというのが、シリアル通信の基本的な機能です。
1バイトずつデータを送るので、この255までの数字にどういう意味を持たせるのかを決めながらやり取りしていく必要があります。
PCとの連携・基本編
- Arduino側と、PCソフト側のプログラムでルール(仕様)を決めながら書く
- max/msp oF Processing touchdesigner
シリアル通信では決まったデータのルールが特に無いため、やり取りの決め事をArduinoとソフトウェア側で決めながら実装していく必要があります。
基本的なArduinoでのシリアル通信の記述方法と、PCとの連携についていくつか具体的な方法を紹介していきます。
Arduinoシリアル送受信(基礎編)
- 指定したピン番号のアナログ値を返すサンプル
まずは、とてもシンプルなArduinoのシリアル通信サンプルを用意します。これはPCから0~5までの数字を受け取って、その番号のアナログピンの値を返すというサンプルです。
アナログの値は0~1023まで取ることができますが、返す値は1バイトだけなので、0~255までしか送れません。大きい数字を送る方法は色々あるのですが、ここはひとまずアナログの値を255までに圧縮します。
void setup() {
Serial.begin(9600);
}
void loop() {
while (Serial.available())//データが来ているか確認
{
//1バイト分のデータを変数に入れる
byte data = Serial.read();
//アナログピンの番号の入力値を返す
if (data < 6)
Serial.write(analogRead(data) / 4); //1バイトでしか送れないので、4で割る
}
}
max/mspとの連携
max/mspでシリアル通信をするときは、serialパッチを使います。1つ目の引数にポート名、2つ目には通信速度を記載します。ポート名はprintを送信すると右のアウトレットから出力されます。
数字をメッセージで送るとその数字が1バイトだけ送信されます。1バイトなので、255を超える数字を送ろうとするとオーバーフローが起きて、256で割った余りの数字になってしまいます。
ここではアナログ5番のデータが欲しいので5を一定間隔で送りました。
Arduinoからは返事が返ってきますが、返事はserialオブジェクトの中に格納されたままになっていて、bangを送る事でアウトレットから出力されます。
touchdesignerとの連携
- serialDATを使う
- TDはアスキーコードとして使うのがデフォルト(後述)
続いてTouchdesignerでの連携です。touchdesignerではSerialDATというオペレータを使って送受信をします。ポート名と通信速度を同じように設定して、データを出力するタイミングのRow/Callback FormatをOne par byte、Byte ColumnをONに設定します。byte ColumnをONにしたことで、1バイト分のデータが数字で表示されるようになります。使うデータは最新のものだけなので、Maximum Linesは1にしておきます。
execute DATからデータ送信
毎フレームアナログ番号の5を送りたいので、Execute DATのFrame StartをONにし、onFrameStart内でserial1オペレータのsendByteメソッドを呼び出します。これで毎フレーム5が送られるので、アナログ入力値がserialDATに送られます。
CHOPで受け取る
DATの値をConstant CHOPに入れ込みます。直接DATから数字をもらってきても良いですが、これで使いやすくなりました。
グラフィックのパラメータにしてみる
これをNoise TOPのZパラメータに入れてみます。
データの範囲が0~255なので、これをexpressionで調整してnullに入れておいた値をNoise TOPのtzに入れます。アナログの値をぐりぐり動かすとNoiseの模様がリニアに変化していきます。
processingとの連携
- processing.serialライブラリを使用
- serialEventメソッドが呼び出される
続いてprocessingの場合です。processing.serialからライブラリをインポートして、Serialクラスを使います。writeメソッドで1バイト分のデータを送る事ができます。Processingではデータを受信するとserialEventが呼び出されるので、そこでデータを受信することができます。
import processing.serial.*;
int data = 0;
Serial serial;
void setup()
{
serial = new Serial(this, "COM3", 9600);
}
void draw()
{
background(0);
serial.write(5);
rect(width / 2, height / 2, 10 + data, 10 + data);
}
void serialEvent(Serial s)
{
data = serial.read();
}
openFrameworksとの連携
- C++なので、Arduino側と読み書きの構造は近い
- 今日紹介した中で処理速度は最速
openFrameworksでは、ofSerialというクラスが用意されています。
C++ということもあって、書き方はArduinoとかなり近くなります。
//====header====
ofSerial serial;
int analog = 0;
//====header====
ofApp::setup()
{
serial.listDevices();
serial.setup("COM3", 9600);
}
ofApp::update()
{
seiral.writeByte(5);
while (serial.available())
{
analog = serial.readByte();
}
}
ofApp::draw()
{
ofDrawRectangle(ofGetWidth() / 2.0, ofGetHeight() / 2.0, 10 + analog, 10 + analog);
}
USBシリアルでの「決めごと」
- 通信速度(9600/38400/115200bpsなど)
- データ長・パリティビット・ストップビット
- 8ビット長・パリティ無し・1ストップビット(SERIAL_8N1)がArduinoデフォルト
最後に、USBシリアルではいくつかルールがあってこれをArduino側とソフトウェア側でそろえなくてはいけません。通信速度はノイズ等でデータの取りこぼしがあるので早すぎない程度に高い数字を使いましょう。
データ長・パリティ・ストップビットについては通信のタイミングを決めるルールですが、基本的には8ビット・パリティ無し・1ストップビットが今スタンダードなようなので、特に意識する事はないと思います。メーカー製の装置等と通信する時に違った設定があることがあるので、なぜかちゃんと通信できないという時に思い出す程度で大丈夫です。各項目の意味については検索するとたくさん情報が出てきますが、これも自分で組み込みデバイスを設計しない限りあまり使うことはないかなと思います。
補足:通信速度
- 9600だと60fpsの世界では1フレーム160バイト
- 115200で1フレーム1920バイトなので無難?
自分の感覚としてはUSBシリアルなら9600未満は遅いかなという感じで、115200当たりが無難に思います。
映像的に考えると、9600bpsで1フレームに送れるデータ量は9600÷60=160バイトなのでちょっと遅いかも、115200bpsなら1フレーム1920バイトなので十分かなという感じです。
補足の補足:クロックとの組み合わせ
- Arduinoのクロック数(UNOでは16MHz)によってエラーレートが変わる
- 高いほどエラーが起きやすいという事でもない
その後、ツイッターで情報をいただいたのですが、実はクロック数と通信速度でデータを取りこぼしやすい組み合わせがあるということでした。
https://cache.amobbs.com/bbs_upload782111/files_22/ourdev_508497.html
ここによると16MHzでエラー率が低く、速度が速いのは76800bpsだそうです。115200だと3.7でやや高く、57600は少し遅いけど位相がずれていくのでエラー率が高くなっています。
https://ja-support.renesas.com/knowledgeBase/17794551
理論上は4.375%までが許容範囲ということなので、115200はギリギリですね…16MHzのArduinoでは76800くらいが丁度良さそうです。
ここまでがシリアル通信基本形
- 生のバイナリ(数字)データでシンプルにやりとり
- 複数種類のデータや大きい数字など複雑にしづらい
以上が、各ソフトウェアで扱える簡単なシリアル通信の方法です。単純なデータのやりとりはできますが、これではちょっと機能に物足りなさを感じます。
アナログのデータも欲しいけどデジタルの入出力もしたい、さらに複雑な処理をArduinoに指示したいとなるともう少し発展的な実装をする必要があるのですが、それはまた次回!
前期課題
- デバイスを使った作品プランを提出
- 「1種類のインプットと、それに対応した1種類のアウトプットを持つもの」
- その組み合わせがどういう意味を持つか、どういう意味を持たせられるか考える
6月も半分を超えたので、ここで前期課題を出そうと思います。デバイスを使った作品プランを考えてみてください。後期は実際に作品を作ってもらいたいと考えているので、ここで少し作品についてや実現可能性についてディスカッションをできればと思っています。聴講生の提出も歓迎です。
どうしても作りたいものがある場合は、Arduinoかその他デバイスを使っていれば自由に考えてもらっても構いませんが、一応トライしてみてほしいテーマを「1種類のインプットと、それによって変化する1種類のアウトプットを持つもの」とします。
何かセンサーや入力を1種類と、それによって制御される出力を1種類決めて、その現象がどういう意味を持つか、どんな意味を持たせることができるか考えてみてください。
7/19に発表してもらいたいので、7/19のお昼12時を提出期限とします。また課題用のページを作りますが、Googleのドキュメントかスライド、PDFなどWebで共有できる形式をいくつか指定します。