今回はセンサーの話ではなく、センサーで読んだ値をどうするかという話になります。ここまでは、SDカードに記録したり(第5回)、PCやLCDに表示したり(第3・18回)することでArduinoに接続したセンサーの出力値を読んできました。しかし、例えば三軸試験装置などのように、Windows PC上に記録・制御ソフトウェアをつくり、そこにセンサーの値を読み込みたい場合があります。当然、Windows PCにSPIやI2C接続用のデジタルセンサーを直接つなぐことはできません(やろうにも、そもそもどこにつなぐ?って話です)。この場合、以下の図のように、Arduinoなどのマイコン(ボード)を介して、センサーとWindows PCをつなぐことができます。つまり、まずArduinoでデジタル値としてセンサー出力を読み込み、その数値をPCに転送します。

 

 

ここで、マイコンとPCの通信にはUART(Universal Asynchronous Receiver/Transmitter)という非同期シリアル通信を使い、USBに変換してPCに入力します。実は第3回でやったことと同じなのですが、第3回では、Arduino IDEの「シリアルモニタ」に数字が表れるだけで、ただ文字が現れるのを見るだけでした。第30回では、Windows Visual Studioを使って、Arduinoを介してデジタルセンサーの値をPCで読んだり、PCから値を入力してArduinoを介してDAC(デジタル-アナログ変換:第9回)から任意の電圧を出力する方法を紹介します。この第29回では、その前準備としてUART通信に慣れるため、Arduino同士のUART通信を説明をします。

 

ハードウェアシリアルとソフトウェアシリアル

Arduino UNOのデジタルピン0と1を見ると、製品によってはそれぞれRXD・TXD(RX・TXと意味は同じ)と書いてあります。これらのピンはチップ内でUART通信を行うハードウェアに接続されており、「ハードウェアシリアル」と呼ばれます。ArduinoをUSBケーブルでPCと接続したとき、実はこれで通信が行われています。一方、他のデジタルピンを、あたかもUARTハードウェアにつながっているかのようにRXやTXとしてふるまうようにさせるライブラリがArduinoには標準で存在し(SoftwareSerial)、これはソフトウェアによりUARTを可能にしているので、このようなUARTはソフトウェアシリアルといいます。Arduinoのモデルによって、RXとして割り当てられるデジタルピンに制約がある(ピン13はダメ、など)ようで、これについては他のリファレンスを見て下さい。

 

2台のArduino間の通信

先に述べたように、Arduinoを介してWindows PCでデジタルセンサーのデータを読むという本題に入る前に、2台のArduino間でUART通信によりデータを交換することを考えます。このことにそれほどの実用性はないのですが、この前置きには二つの意味があります。一つは、これまでI2CやSPIといったシリアル通信を使うセンサーを紹介してきましたが、UARTによりデータを交換することで機能するモジュール(後の回で紹介するLPWAのSigfox通信モジュールなど)もあるので、その模擬試験としての意味で、もう一つは、Arduino – PCの通信は途中にUSB変換が介在するため、UARTの基本がわかりづらいことです。まあ、面倒ならここは読まなくてもいいです。

 

ここでは、Arduino AのアナログピンA0にかかる電圧値をArduino Aで読み、UARTでArduino Bに送り、Arduino Bに接続したLCDに表示します。もちろん、LCDをArduino Aに直接接続すればArduino Bは要らないわけですが、ここは練習なので。UARTはGNDに加え、送信線TXと受信線RXを使います。ここでポイントなのが、第12回で説明したように、Arduino AのTXとRXは、Arduino BのRXとTXにそれぞれ接続することです。糸電話で、二人とも同じ糸でつながれたコップを口に当てていては、話ができませんよね。TXとRXのイメージは、以下の絵(西村紫6歳に加筆)です。

 

 

 まずは、デジタルピン0と1を使った「ハードウェアシリアル版」です。以下のように接続します。

 

 

スケッチはArduino A・Bそれぞれ以下のようにします。UART通信はバイト単位でデータを送信し、受信もバイト単位となります。つまり、00~FFの256段階の値をやりとりするわけですが、これを送る・読むにはSerial.write()・Serial.read()という関数を使います。これは、例えば「3,056」mVという値を送る場合、値として”3” (0x03), “0” (0x00), “5” (0x05), “6” (0x06)を送ることになります(3056は256より大きいので、それ自体を一つの数値として送ることはできません)。一方、文字列として”3” (ASCIIコード表で51番、つまり0x33), “0” (0x30), “5” (0x35), “6” (0x36)をまとめてやりとりすることがソフトウェア上可能です。この場合、Serial.print()・Serial.readString()という関数を使います(最後に改行文字を加える場合はSerial.println()を使うことは以前にも説明済みです)。どちらのやり方がよいかは場合によるのでしょうが、文字を送りたい場合もあるので、後者のほうが汎用性があるかもしれません。ここでは文字列として通信を行います。なお、シリアルでデータを送る場合、受信バファー(64バイト)に蓄積され、そこから1バイトずつ読まれて消えていくのですが、そこに残っているバイト数はSerial.available()で読めます。つまり、while(!Serial.available())というのは「もう読み込むものがないうちは」という条件で、逆にwhile(Serial.available())というのは「まだ受信バファーに読まれていないデータが残っているうちは」という条件です。

 

Arduino A:

 



void setup()

{

  Serial.begin(9600); // opens serial port, sets data rate to 9600 bps

}

 

void loop()

{

  int ainput; //読み取ったbit数

  float vinput; //bit数を電圧に変換したもの

 

  while(!Serial.available()){} // 受信バファーにArduino Bから送信要求がないうちは何もせずに待つ

 

  String dummy;

  dummy=Serial.readString(); //受信バファーから送信要求を読み捨てる

 

  ainput=analogRead(A0); //ピンA0から電圧をbitとして読む

  vinput=5000.0*ainput/1023; //上記を電圧mVに変換

  Serial.println(vinput); //Arduino Bに送信する

}

 

 

Arduino B:

 



#include <LiquidCrystal_I2C.h>

 

LiquidCrystal_I2C lcd(0x3F,16,2);

 

void setup()

{

  Serial.begin(9600); // opens serial port, sets data rate to 9600 bps

  lcd.init();

  lcd.backlight();

}

 

void loop()

{

  Serial.println(); // Arduino Aに文字(改行のみ)を送る

 

  delay(1000);

 

  lcd.clear();

  lcd.setCursor(0, 0);

  lcd.print(Serial.readString()); // 文字列を受信バファーから読み込み、LCDに表示

}

 

 

 次に、デジタルピン2と3を使った「ソフトウェアシリアル版」です。

 

 

 

スケッチはArduino A・Bそれぞれ以下のようにします。SoftwareSerialというライブラリはArduino IDEに標準でインストールされているはずです。スケッチ内で、どのピンをTX・RXとするのか指定しなくてはならないことに注意が必要です。ハードウェアシリアル版のスケッチからの変更箇所を赤で示します。

 

Arduino A:

 



#include <SoftwareSerial.h>

SoftwareSerial mySerial(2, 3); // RX, TX

 

void setup()

{

  mySerial.begin(9600); // opens serial port, sets data rate to 9600 bps

}

 

void loop()

{

  int ainput; //読み取ったbit数

  float vinput; //bit数を電圧に変換したもの

 

  while(!mySerial.available()){} // 受信バファーにArduino Bから送信要求がないうちは何もせずに待つ

 

  String dummy;

  dummy=mySerial.readString(); //受信バファーから送信要求を読み捨てる

 

  ainput=analogRead(A0); //ピンA0から電圧をbitとして読む

  vinput=5000.0*ainput/1023; //上記を電圧mVに変換

  mySerial.println(vinput); //Arduino Bに送信する

}

 

 

Arduino B:

 



#include <LiquidCrystal_I2C.h>

#include <SoftwareSerial.h>

 

LiquidCrystal_I2C lcd(0x3F,16,2);

SoftwareSerial mySerial(2, 3); // RX, TX

 

void setup()

{

  mySerial.begin(9600); // opens serial port, sets data rate to 9600 bps

  lcd.init();

  lcd.backlight();

}

 

void loop()

{

  mySerial.println(); // Arduino Aに文字(改行のみ)を送る

 

  delay(1000);

 

  lcd.clear();

  lcd.setCursor(0, 0);

  lcd.print(mySerial.readString()); // 文字列を受信バファーから読み込み、LCDに表示

}

 

 

どちらでやっても、LCDに電圧値が表示されると思います。値だけでなく、メッセージなどを文字列として通信することももちろん可能です。