Arduinoを使ったデータのやり取りで使われる通信方式には、I2C通信・SPI通信・UART通信などがあります。
この3つはArduinoでもよく使われる通信方式となります。
前回、シリアル通信の1つであるUART通信についてご紹介しました。
Arduinoを使っていると一番よく使う通信方式だと思います。
Arduinoのスケッチの書き込みはPCとArduinoをUSBケーブルを接続して行いますが、普段意識することなくこのスケッチの書き込みを行っているわけですが、これもUARTによるシリアルでのデータのやり取りにより行われています。
また、デバッグ等でスケッチで使われている変数の値をシリアルモニタに表示させることがありますが、これもPC←→Arduino間でUARTによるやり取りが行われています。
このようにUARTによるシリアル通信は手軽に使えるためよく使われる通信方式となります。
Arduino(こちらではArduino Unoを使います)でUARTによるシリアル通信をサポートしているピン(端子)は決まっています。
Arduino Unoの場合、デジタルピンD0とD1端子にはボード上にRX/TXと書かれていますが、これがシリアル通信で利用できる端子となっています。
この端子はArduinoのマイコンチップ(ATmega328P)と直接接続されているためハードウェアシリアルと呼ばれることもあります。
Arduino Unoではシリアル通信で使える端子(RX/TX)が1系統用意されているということですね。
しかし複数の機器やデバイスとシリアル通信させたい時にはArduino Unoではシリアル通信で使える端子が1系統しかないため、複数のハードウェアシリアルを搭載したArduino Megaなどを使う必要が出てきます。
これでは不便ですよね!
そこでArduinoには、空いている他の任意のデジタルピンをプログラムにより擬似的にシリアル通信で使える端子(TX/RX)に割り当てることが出来る『SoftwareSerial』というライブラリを使う方法もあります。
Arduinoに接続するデバイスが増えてくるとそれに応じてソフトウェア的に接続できるシリアルポートを増やしていくということです。
マイコンチップに搭載されたシリアルポートに直結されたピン(Arduino UnoではD0→RX/D1→TX)を使うハードウェアシリアルに対して、ライブラリを使いソフトウェア的にシリアルポートとして使えるようにしているものなのでこれをソフトウェアシリアルと呼びます。
前回ArduinoのUARTによるシリアル通信としてボード上のRX/TXを使うハードウェアシリアルについてご紹介しました。
今回はソフトウェアシリアルについて見ていきたいと思います。
目次
Arduinoのソフトウェアシリアルを使ってみる!
UARTシリアル通信とは?
まずUARTシリアル通信について簡単に見ておきます。
UART(ユーアート)とは、「Universal Asynchronous Receiver/Transmitter」の略となります。
I2C通信やSPI通信のようにクロック用の信号ラインがあり同期させながらデータのやり取りを行う同期式シリアル通信に対して、UARTでは同期用の信号ラインを持たない非同期式のシリアル通信となっていて仕組みが単純で比較的扱いやすくなっています。
UART通信ではクロック用の信号ラインがなく、2つの信号線を使って通信を行います。
UART通信で使われる信号線は、データ送信用の信号線TX(Transmitter)とデータ受信用の信号線RX(Receiver)の2つの信号線を使ってデータのやり取りが行われます。
- TX(Transmitter):データ送信用の信号線
- RX(Receiver):データ受信用の信号線
Arduinoとデバイスとの接続は、データ送信用のTXラインを対となる受信ラインRXに接続する形となります。
Arduino | 接続機器 |
TX(送信) | RX(受信) |
RX(受信) | TX(送信) |
Arduinoのハードウェアシリアルについて
まずハードウェアシリアルについて見ておきます。(前回の内容です)
Arduino Unoのボード上にはデジタルピンD0にRX、D1にTXと書かれています。
この端子はArduino Unoの心臓部であるマイコンチップ(ATmega328P)と直結されています。
また、USB-シリアル変換を行うためのATmega16U2チップにも繋がっています。
そのため、PCとArduinoをUSB端子で接続してスケッチの書き込みやシリアルモニタを使うことができ、またRX/TXに接続したデバイスとのシリアル通信で使うことが出来るようになっています。
マイコンチップに搭載されたシリアルポートを使っているのでこれをハードウェアシリアルと呼びます。
しかしArduino Unoの場合、ハードウェアシリアルで使える端子はこのD0(RX)/D1(TX)の1系統しかありません。
複数の機器やデバイスとシリアル通信をさせたい場合、シリアルポートが足りない・・・といったことになります。
UARTでのやり取りでは、I2CやSPIでの接続のようにマスターやスレーブといった関係がなく1対1でのやり取りとなるため、これでは複数のデバイスとのやり取りが出来ません。
そこでArduinoには、空いている任意のデジタルピンをRX/TXに割り当ててソフトウェア的にシリアル通信を可能にするライブラリが用意されています。
これはソフトウェア的にシリアル通信を可能にしているものなので『ソフトウェアシリアル』と呼ばれます。
Arduinoのソフトウェアシリアルについて
それではここからが今回の本題となるソフトウェアシリアルについてです。
先述のようにArduino(こちらではArduino Unoで説明していきます)には、シリアル通信で使えるための端子が1系統しかありません。
複数のデバイスや機器等と接続しシリアル通信させたい場合、これでは困ってしまいます!
そしてマイコンチップと直結されたハードウェアシリアルでは、USB-シリアル変換器(ATmega16U2)チップとも繋がっていてスケッチの書き込みにも利用されています。
そのため、ArduinoのD0/D1端子に何かしらの機器が接続された状態でスケッチを書き込もうとすると書き込みエラーが発生するのもこのためです。
また、シリアルモニタを利用する場合にもハードウェアシリアルを使いPCとのやり取りが行われています。
Arduino Unoにはハードウェアシリアルとして使える端子が1系統しかありませんが、普段あまり意識することはありませんが何かとこのハードウェアシリアルは使われているんですね!
それでは複数のデバイスを接続してそれぞれの機器でシリアルでのやり取りを行いたい場合はどうしたらいいか?
Arduinoには任意のデジタルピンをソフトウェア的にシリアル通信で使える端子(RX/TX)に割り振ることが出来る便利なライブラリ『SoftwareSerial』というものが用意されています。
これを使えば、Arduinoの空いているデジタルピンを使いシリアルポートに割り当てることにより複数のデバイスとのシリアル通信などが可能となります。(通信速度などハードウェアシリアルと比べると制限もあります)
ソフトウェアシリアル使用例
例えばこちらの動画は、以前製作した4足歩行ロボくんをシリアルモニタからBluetoothモジュールを使って動かしているものとなります。
Bluetoothモジュールはシリアルでの接続で使えるHC-05を使っているのですが、Arduino Uno(このロボくんはNanoを使っています)ではシリアルポートが1つしかないのでBluetoothモジュールをハードウェアシリアル(ArduinoのD0/D1端子)を使って接続すると動作確認後のスケッチの修正作業のたびに一旦Bluetoothモジュールを外してスケッチを書き込む必要が出てきます。
また、デバッグ等でシリアルモニタを使いたい場合も同様の問題が出てきます。
動作用のスケッチが完成してしまえば問題ないのですが、ハードウェアシリアルを使ってしまうとテスト段階では面倒となる場面が出てきます。
そこでBluetoothモジュールは他の空いているデジタルピンをソフトウェアシリアルでRX/TXを割り当てて接続しテスト動作などをさせています。
このようにArduinoのボードに搭載されているハードウェアシリアルだけでは足りない場合や意図的にハードウェアシリアルを使いたくない場合などでソフトウェア的に接続できるシリアルポートを増やしていくことが出来るので便利となります。
ソフトウェアシリアルについて雰囲気伝わったかと思います。
それではソフトウェアシリアルを使った簡単な接続例を見ていきます。
ソフトウェアシリアルを使い接続できるシリアルポートを増やし複数のArduinoと接続し簡単な動作テストをしてみたいと思います。
2台のArduinoを接続して双方向でのやり取りを確認する
Arduino Unoのハードウェアシリアルで使えるデジタルピンは、D0端子がRX・D1端子がTXの1系統しかないことは先に説明しました。
I2C通信やSPI通信のようにマスターに対して複数のスレーブ端末を接続できるわけではなく、UARTによるシリアル通信では1対1の接続関係になるのが特徴となります。
そのため接続は簡単なのですが、複数の端末(デバイス)と接続してやり取りを行いたい場合はハードウェアシリアルだけでは不足する場合が出てきます。
そこで先述のようにソフトウェアシリアルを使う方法が出てきます。
Arduinoには複数のデバイスとシリアル通信をする場合、「SoftwareSerial」というライブラリ(Arduino IDEにある標準ライブラリです)を使うことにより空いている任意のデジタルピンをRX/TXに割り当ててソフトウェア的にシリアル通信を可能にすることが出来ます。
Arduinoのハードウェアシリアルとして割り当てられているD0(RX)/D1(TX)端子は、ArduinoボードのUSB-シリアル変換器(ATmega16U2)チップとも接続されていてスケッチの書き込みにも使われています。
これら端子に何かしらのデバイスが接続された状態でスケッチの書き込みを行おうとした場合、書き込みエラーが発生するためソフトウェアシリアルを使い任意のデジタルピンをシリアル通信で使用できるRXやTXに割り当てることによりそれらを回避する事も出来ます。
それでは、「SoftwareSerial」ライブラリを使い簡単な接続例(使用例)をいくつか試してみます。
「SoftwareSerial」ライブラリはArduino IDEに標準で入っているライブラリとなります。
SoftwareSerialライブラリの使い方は、通常のシリアル通信とほぼ同じです。
詳しくはArduino公式リファレンスサイトをご覧下さい。
こちらは前回やったものですが、2台のArduinoをソフトウェアシリアルにより接続し双方向での簡単なデータのやり取りを行うものです。
SoftwareSerialライブラリを使い、それぞれのArduinoで空いているデジタルピンD2をRX、D3をTXに割り当てています。
シリアルでのやり取りなので、RX→TX/TX→RXという接続ですね。
一方のArduinoに繋いだタクトスイッチが押されると他のArduinoのLED(オンボードLED)が点灯するという簡単なものとなります。
Arduino1 | Arduino2 |
RX(D2) | TX(D3) |
TX(D3) | RX(D2) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | // Arduino入門編㉘ ソフトウェアシリアルについて! // https://burariweb.info #include <SoftwareSerial.h> SoftwareSerial mySerial(2, 3); // RX,TXの割り当て const int LED_PIN = 13; const int SW_PIN = 8; void setup(){ Serial.begin(9600); mySerial.begin(9600); // ソフトウェアシリアル通信の開始(ボーレート9600bps) pinMode(LED_PIN, OUTPUT); pinMode(SW_PIN, INPUT_PULLUP); // スイッチに接続したピンをプルアップ digitalWrite(LED_PIN, LOW); } void loop(){ while(mySerial.available()>0){ int val = mySerial.read(); // 受信したデータを読み込む if(val == '1'){ // "1"ならLEDを消灯、"0"ならLEDを点灯 digitalWrite(LED_PIN, LOW); } else if(val == '0'){ digitalWrite(LED_PIN, HIGH); } } int SW = digitalRead(SW_PIN); // スイッチの状態を判定 if(SW == HIGH){ // "HIGH"なら”1”を送信 mySerial.write('1'); } else if(SW == LOW){ // "LOW"なら"0"を送信 mySerial.write('0'); } delay(100); } |
スケッチ解説
通常のシリアル通信とほぼ同じですが、異なる部分だけ見ておきます。
まずライブラリのインクルードです。
他のライブラリを使うときと同様にSoftwareSerialライブラリをインクルードしておきます。
5 | #include <SoftwareSerial.h> |
ハードウェアシリアルではD0がRX・D1がTXと決められていましたが、ソフトウェアシリアル(SoftwareSerial)では使いたいピンを任意に選ぶことが出来ます。
こちらでは、RXをD2・TXをD3に設定しmySerialという名前にしています。
6 | SoftwareSerial mySerial(2, 3); // RX,TXの割り当て |
ソフトウェアシリアルでの通信を開始します。
通信速度(ボーレート)は9600bpsとしています。
ハードウェアシリアルとなるSerial.begin(9600)が入っていますが、このスケッチではハードウェアシリアルでのやり取りは行っていないのでこの行は書かなくても問題ありません。
13 14 | Serial.begin(9600); mySerial.begin(9600); // ソフトウェアシリアル通信の開始(ボーレート9600bps) |
あとはソフトウェアシリアルで設定したRX・TXラインを使いデータの送信及び受信をさせています。
タクトスイッチが押されると”0″を送信し、押されていない時は”1″を送信させています。(プルアップさせているので反転していて分かりづらいですね)
同時に受信も行い、”1″を受信するとLEDを消灯、”0″を受信するとLEDを点灯させています。
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | while(mySerial.available()>0){ int val = mySerial.read(); // 受信したデータを読み込む if(val == '1'){ // "1"ならLEDを消灯、"0"ならLEDを点灯 digitalWrite(LED_PIN, LOW); } else if(val == '0'){ digitalWrite(LED_PIN, HIGH); } } int SW = digitalRead(SW_PIN); // スイッチの状態を判定 if(SW == HIGH){ // "HIGH"なら”1”を送信 mySerial.write('1'); } else if(SW == LOW){ // "LOW"なら"0"を送信 mySerial.write('0'); } |
2台のArduinoを接続してシリアルモニタで確認
次に2台のArduinoをソフトウェアシリアルで接続し、同時にシリアルモニタ(ハードウェアシリアル)も使ってみたいと思います。
今回、送信側Arduinoと受信側Arduinoと割り当てました。
送信側Arduinoから送られた数字(0・1・2・3・・・)を受信側Arduinoで受け取り、その数字を受信側Arduinoに接続したPCのシリアルモニタに表示させるというものです。
この時、数字が偶数ならそれぞれArduinoのオンボードLED(D13)が点灯します。
【送信側スケッチ】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | // Arduino入門編㉘ ソフトウェアシリアルについて! // https://burariweb.info // 送信側スケッチ #include <SoftwareSerial.h> SoftwareSerial mySerial(2, 3); // RX,TXの割り当て int LED_PIN = 13; int counter = 0; void setup(){ mySerial.begin(9600); // ソフトウェアシリアル通信の開始(ボーレート9600bps) pinMode(LED_PIN, OUTPUT); } void loop(){ mySerial.write(counter); // counterの数値を送信する if((counter % 2) == 0){ // counterの値が偶数ならLEDを点灯 digitalWrite(13, HIGH); } else { digitalWrite(13, LOW); } counter++; delay(500); } |
【受信側スケッチ】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | // Arduino入門編㉘ ソフトウェアシリアルについて! // https://burariweb.info // 受信側スケッチ #include <SoftwareSerial.h> SoftwareSerial mySerial(2, 3); // RX,TXの割り当て int LED_PIN = 13; void setup(){ Serial.begin(9600); // シリアル通信の開始(ハードウェアシリアル) mySerial.begin(9600); // ソフトウェアシリアル通信の開始(ボーレート9600bps) pinMode(LED_PIN, OUTPUT); } void loop(){ while(mySerial.available()>0){ // 受信データがあれば読み込む int val = mySerial.read(); Serial.print("Number = "); // シリアルモニタに受信データを表示 Serial.println(val); if((val % 2) == 0){ // 受信データが偶数ならLEDを点灯 digitalWrite(13, HIGH); } else { digitalWrite(13, LOW); } delay(10); } } |
動作確認
送信側Arduinoのcounter変数はloop関数内で1ずつ加算され0,1,2,3・・・のように受信側Arduinoに常に送り続けます。
それぞれのArduinoは数字が偶数の時オンボードLEDが点灯するスケッチとなるので、上手くデータのやり取りが行われていれば2台のArduinoのLEDが同期しながら点灯/消灯を繰り返します。(0.5秒間隔)
そして受信側Arduinoで受け取った数字をシリアルモニタにも表示させています。
シリアルモニタはArduinoのハードウェアシリアルでのやり取りとなります。
このようにソフトウェアシリアルを使うことにより複数のシリアルでのやり取りが可能になるということですね!
Arduino同士はソフトウェアシリアルを使い接続しているので、スケッチの書き込みもそのまま行うことが出来ます。
通信速度を上げると動作が不安定に!
上記スケッチではソフトウェアシリアルを使った通信速度(ボーレート)は9600bpsとしています。
ここで通信速度を115200bpsやさらに高い数値に上げてみるとどうなるか?
上記の送信側・受信側スケッチのmySerial.begin(9600);の数値を115200と変えて動作を確認してみます。
所々、受け取った数値がおかしくなっていることが分かります。
このようにソフトウェアシリアルでは早い通信速度になると動作が不安定やデータの取りこぼしなどが出てきます。
Arduinoのリファレンスによれば、ソフトウェアシリアルでは最大115200bpsまでの通信速度となるようです。
ハードウェアシリアルとソフトウェアシリアルの特徴
ハードウェアシリアルではArduinoのマイコンチップ(UnoではATmega328P)に内蔵されているUART端子(RX/TX)に直結して使われるので高速な通信が可能となりますが、ソフトウェアシリアルではライブラリを使ってソフトウェア的にこれを処理しているため高い通信速度では動作が不安定になってきます。
またハードウェアシリアルではマイコンチップ(CPU)が他の処理をしている場合も正常な通信ができますが、ソフトウェアシリアルでは通信が終了するまで他の処理が出来ない場合やデータの取りこぼしなど出てくる場合があります。
簡単にまとめるとこのようになります。
- 高速なやり取りが可能
- 他の処理をしている場合も正常な通信ができる
- 使用できる端子が限られていくる(Arduino Unoの場合、D0/D1のみ)
- 空いている任意のデジタルピンを使うことが可能
- 通信速度が上がると動作が不安定になってくる
- 通信が終わるまで他の処理ができない場合がある
ソフトウェアシリアルで複数ポートを使う場合の受信の切り替え
それでは最後にソフトウェアシリアルを使い複数のポートを割り振ってやり取りしてみたいと思います。
複数のデバイスを同時に受信状態にしたい場合は、Arduino Megaなどのハードウェアシリアルのポートを複数持つArduinoを使う必要があります。
ソフトウェアシリアルでは複数同時にデータを受信する状態には出来ません。
しかしlisten()関数を使い単一のシリアルポートを受信出来る状態にしこれを高速に繰り返すことにより同時に通信出来ているかのように振る舞わせることは可能なようです。
簡単な例としてこのようにArduino Unoを3台接続しソフトウェアシリアルでのやり取りをさせてみたいと思ったんですが・・・これがなかなか難しく!
常に送信用Arduino①と②から数値が送られ、これを受信側Arduinoに割り当てられた2つのソフトウェアシリアルポートをlisten()関数で切り替えながら受信させるというものです。
ソフトウェアシリアルでは複数同時にデータを受信する事ができないため、listen()関数を使い割り当てたソフトウェアシリアルポートmySerial1やmySerial2などを切り替えながら受信させるわけですが・・・この方法、実は簡単にはいきませんでした。
常に送られてくるデータを高速でポートを切り替えながら受信させるわけですが、当然送信側との同期などはさせていないのでスケッチを走らせていると徐々に受信タイミングが合わなくなりデータの取りこぼし等が発生してしまいます。
Arduinoくんの複数ソフトウェアシリアルでの受信、取りこぼしや変な値拾ったり・・・😬(前々からの課題)
素直にMega使えってな話かな😛 pic.twitter.com/3BFZNfqpqw
— ガジェット大好き!! (@smartphone_jp1) March 12, 2022
SoftwareSerialのサンプルスケッチを使っても同様な事が起こるようですね!
送信終了時やバッファが空の時など(ソフトウェアシリアルではlistenされていないポートのデータは破棄されますが)、ポートを切り替えるタイミングをうまく取る必要があるようです。
このあたりスケッチを工夫しないと難しいようですね。
このようにソフトウェアシリアルでは同時に複数のデータを受信状態にする事ができないなど制約も出てきます。
上記接続で受信側に割り当てられたArduinoから他の2台のArduinoにデータを送信するといった逆の動作では比較的簡単に実現できるのですが・・・このあたりいろいろと試してみると面白いかと思います。(私は上手くいきませんでした!)
参考 Two Port ReceiveArduinoまた、複数のUARTデバイスからの受信データを同時にバッファ出来るMultiUARTというライブラリもあるようですね!
参考 MultiUARTGitHub今回使ったアイテム
Arduino UNO
Arduinoはオープンソースのハードウェアなので正規品以外にも互換品が多数販売されています。
互換品でも正規品と比べて特に問題なく使用でき安価なためArduino学習用としていいと思います。
Elegoo製のArduinoボードは、互換ボードの中でも非常にクオリティーが高いのでおすすめです!
Arduino スターターキット
これからArduino学習を進めていくにあたりArduino UNO(互換品)やブレッドボード、ジャンパーピンなどがセットになったスターターキットが販売されています。
私はGeekcreit製のスターターキットを使っていますが、ELEGOO製のものは国内Amazonなどでも購入可能で人気があるようです。(セット内容はほぼ同じです!)
そしてELEGOOのサイトからスターターキット用サンプルスケッチのダウンロードも可能です。(Geekcreitのキットでも使えます)
参考 チュートリアルダウンロードELEGOO基本的にこれからこのセットで出来るものから紹介していこうと考えていますが、かなり多くのことが出来ます。
電子工作を始めるにはまずブレッドボードやジャンパーピン、メインとなるArduino UNOやサーボ、LEDなどの基本的なパーツがないと実際に動かすことが出来ませんが、個々にパーツを購入して回路を組んでとなるとかなりの手間がかかります。
スターターキットがあればArduinoの初歩的なことはかなりの数こなすことが出来るのでオススメです!
そこからスキルアップに伴い個別でセンサーやモジュールなど必要なものを増やしていくのがいいと思います。
最後に!
ソフトウェアシリアルを使った方法では複数のシリアルポートを割り当てる場合、複数ポートを同時に受信状態に出来ないためlisten()関数を使いうまく受信するタイミング等を合わすなどスケッチを工夫しないとデータの取りこぼし等いろいろと問題が出て来ました。(1対1のやり取りは比較的簡単でしたが)
しかし、テスト環境などスケッチ修正作業を頻繁に行う必要がある場合やシリアルモニタを併用する場合などで便利に使える場面は多いと思います。
コメントを残す