はじめに
Arduinoでモノを作っていると、パソコンから操作したくなったり、パソコンを操作したくなったりしてきます。
その方法の一つが シリアル通信で、Arduinoの場合はUSBを使ったりして通信することができます。
今回はこのシリアル通信について触れます。
ArduinoのSerialライブラリ
Serialライブラリ
Arduinoの公式チュートリアルのSerialライブラリに関する項目はこちら。
Arduino Reference : https://www.arduino.cc/reference/en/language/functions/communication/serial/
これを見ると、Arduinoではシリアル通信は1系統のみ。上位機種のMegaでは3系統とることができます。UnoではUSBでPCと接続している場合は他の機器とシリアル通信ができない(ピンが足りない)ということですね。USBのハブのようなモノを作りたいときは、Unoではできないということです。
Arduino Unoでシリアル通信をする場合はRX,TXピンを使います(USBの場合はすでに結線されている)ので、対応する 0番ピンと1番ピンはシリアル通信中は使えないということですね。PCとやりとりするような機器を作る場合は基本的にこの2つのピンはフリーにしておきましょう。
基本的に公式チュートリアルを見ると各関数の役割や使用方法は書いてありますので、そちらを参照してもらえれば良いかと思います。
Serial.begin() / Serial.end()
シリアル通信を行いたい場合は、このコードを書きます。Serial.begin()
によりシリアル通信が始まります。逆にSerial.end()
により終了します。
シリアル通信はバックグラウンドで行われるので、loop()
の処理が中断されることは基本的にありません。
通常はPCとやりとりし続けるようなプログラムを書くことが多いので、setup()
内にSerial.begin()
を一度行い、Serial.end()
を書かずにloop()
をさせる、という方法がよく取られています。
Serial.begin()
の引数はシリアル通信とのデータ転送レートを指定します。単位はbps。
Arduinoのシリアルモニターの右下にある数値と合わさなければ綺麗に通信することができないので、きちんと合わせないといけません。
300, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 115200 のどれかでやると良いですね。
if(Serial)
シリアルポートがきちんと接続されているかどうかの判断をするのに使われます。
setup()内にSerial.begin()
と共に使われます。
//シリアルポートの接続
void setup() {
Serial.begin(9600); //シリアル通信のポート接続を試みる
while (!Serial) {
; //シリアル通信ポートが正常に接続されるまで抜け出さない
}
}
これを書くとシリアル通信しない限りsetup()
が終わらなくなるので、正確にポート接続してからloop()
処理を始めたい場合は、このようにかけば良いですね。
Serial.available()
シリアル通信が行われて、Arduinoシリアルポートのパケットに何バイトのデータが到着しているかを返します。
パケットに何もない場合は、-1を返します。
なので、シリアル通信が行われてArduinoに受け取るべきデータがあるかどうかで処理を変えることができます。
if (Serial.available() > 0) { // 受信したデータが存在する
...
}
このようにかけばシリアル通信をしたときにだけ処理を行うことができますね。
Serial.write() / Serial.read()
シリアル通信によって、データを受信したり送信したりする関数です。Serial.readBytes()
などはこれらの拡張ですので、とりあえずこの2つから。Serial.write()
でArduinoからシリアル通信によってデータを1byte分送信します。Serial.read()
で逆に1byte分受信します。
Serial.print() / Serial.readString()
シリアル通信によって、データを受信したり送信したりする関数です。
Serial.write()
とSerial.print()
で何が違うの? となりそうですが、Serial.write()
はバイト文字、Serial.print()
はASCII文字で送られます。
ASCII文字に関しては、Wikipediaなどで調べてもらえればわかります。
Arduinoでシリアル通信をしてみる
Exampleコード
とりあえず公式チュートリアルを見ながら実践してみましょう。
Serial.read()のページにあるExampleコードがこちら。
int incomingByte = 0; // for incoming serial data
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}
void loop() {
// send data only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
// say what you got:
Serial.print("I received: ");
Serial.println(incomingByte, DEC);
}
ArduinoのI/Oピンには何も繋がずに、このコードをコンパイル、書き込みしてみましょう。
Arduino IDEの右上にあるシリアルモニターのボタンを押すと、別窓が開きます。
シリアルモニターの右下にある転送レートをSerial.begin()
の値に揃えます。今回は9600bpsで通信しているので、「9600 bps」にします。
Arduinoと接続が完了していれば、上のテキストボックスにテキストを入力して送信してやれば返ってきますね。
たとえば、「1」を入力してEnterを押すと、
I received: 49 I received: 10
と返ってきます。
なんで「1」なのに49とか10なんだ??となりますが、これがASCII文字とバイト文字の違いです。
wikipediaやその他いろんなサイトでASCIIを調べてみると、数字や大文字小文字のアルファベットや各種制御機能が別の数字で変換されています。この対応をASCIIコードとよびます。
つまり、「1」はバイト文字の「1 (b0000 0001)」ではなく、ASCII文字として「(49 , BIN)」と送られるということです。
ついでにASCIIコードの「10」は「改行(Line Feed / LF)」なので、Enterを押すと「10」が返ってくるというわけですね。
さらに I receive: が2回続いているのは Serial.read()
が1バイト分しか読まないためで、「1」と「Enter」で2回loop()
が処理されているということですね。
コードの修正 (文字列の送信)
String incomingStrings; // for incoming serial data
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}
void loop() {
// send data only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
incomingStrings = Serial.readString();
// say what you got:
Serial.print("I received: ");
Serial.println(incomingStrings);
}
}
文字列を扱いたい場合、Serial.read()
の代わりにSerial.readString()
を使えばいけますね。
Serial.readString()は文字列として受け取るので、データ型はint
ではなくString
にします。
文字列は何文字届いているかわからないので、Serial.readString()
は一定時間の間入力を待ちます。初期状態では1000 msです。この時間を変えたい場合Serial.setTimeout()
で待ち時間を変えることができます。短くしすぎると、大量のシリアル通信が行われていると最後まで読み取る前に次の処理に行ってしまいます。
また、Serial.readString()
の代わりに、Serial.readStringUntil()
を使ってやれば、引数にとった値が入力されればタイムアウトを待たずに次の処理に行きます。たとえば、Serial.readStringUntil(10)
としてやれば、Enterを押せば次の処理に行きますね。
コードの修正 (ASCII変換)
int incomingByte = 0; // for incoming serial data
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}
void loop() {
// send data only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
// say what you got:
Serial.print("I received: ");
Serial.write(incomingByte);
Serial.println(); //改行
}
}
たとえば、Serial.print()
やSerial.println()
の代わりにSerial.write()
を使ってみましょう。
Serial.write()
を使うと、結果はキチンと表示されるようになりましたね (1バイト分ずつですが)。
Serial.read()
で受け取った「1」は、実際には「1 (b0000 0001)」ではなく、「(49, BIN)」と送られるということですから、incomingByteには49が格納されています。
これをSerial.print()
で送ったために、ASCII文字として「49」として表示されてしまっていました。Serial.write()
で送った場合、シリアル通信ではバイトとして「(49, BIN)」が送られましたよ、という状態になります。PC側のシリアルモニターで「受け取った(49,BIN)は「1」だったな」と自動で変換してくれるので、シリアルモニターには「1」と表示されるようになりました。
2行目に空白の「I received: 」は改行(10) を読み取ったためで、何も表示されていないというわけですね。
Arduinoでフェーダーの値をモニター
はじめに
では、実践です。
前回Arduinoで半固定抵抗を使うことで多段階の数値を入力できるようになりました (Arduinoでフェーダー操作)。
今回はこのフェーダーで読み取った値をLEDのPWMに渡さずに、シリアル通信でモニターしてみましょう。
用意するもの
Arduino本体 (今回はArduino Uno互換機)
Akizuki : http://akizukidenshi.com/catalog/g/gM-07385/
スライドボリューム (10kΩ, B)
Akizuki : http://akizukidenshi.com/catalog/g/gP-09238/
ブレッドボードとケーブル
Akizuki : http://akizukidenshi.com/catalog/g/gP-00315/
Akizuki : http://akizukidenshi.com/catalog/g/gC-05371/
Arduinoボード
今回はLEDでの読み取りはないので、半固定抵抗をつなげるだけです。 I/OピンはA0ピンに繋げます。
Arduino IDE
//Fader and Serial
int const FADER = A0;
int val;
void setup() {
Serial.begin(9600);
pinMode(FADER, INPUT);
}
void loop() {
val = analogRead(FADER);
Serial.println(val);
delay(10);
}
今までやってきたことだけでほぼ説明は不要ですね。
setup()
にはフェーダーのINPUTとシリアル通信の開始を、loop()
ではフェーダーの値を読み取ったのちにそれをSerial.println()
で表示させてるだけです。
delay(10)
は表示する頻度を減らすためにいれています。
今回はシリアルモニターで値を読むので、Serial.println()
を使いましたが、実際に値をデータとしてやりとりする場合はSerial.write()
を使わないといけません。Serial.write()
は1バイトずつ送られるので、int型のような2バイト以上のデータを扱うときには注意が必要です。
まとめ
PCとの通信ができるようになりました。
MIDIコントローラーを作りたいので、PCとの接続は必須ですね。