esp32からシリアル通信でarduinoへデータを送る
esp32からシリアル通信でarduino megaへデータを送ってみました。
シリアルモニタでそのデータを見るのは簡単ですが、いざ使えるように整数型に変換しようとするといろいろ困ったことが起きるようです。
グローバル変数とローカル変数
グローバル変数はすべての関数から見えます。ローカル変数は、それが宣言された関数の中でのみ見ることができます。Arduinoではsetupやloopといった関数の外側で宣言された変数はグローバル変数となります。
[php]void setup(){
}
void loop(){
int i; // loop関数の中でだけ見える
float f; // loop関数の中でだけ見える
for (int j = 0; j <100; j++){
// 変数jはこの波カッコ内でだけアクセス可能
}
}[/php]
グローバル変数とローカル変数の最大の違いは変数の初期化のようです。ローカル変数は波カッコを抜けると変数が初期化されるのに対し、グローバル変数は初期化されません。
その結果、グローバル変数ではメモリの容量が問題になって来ます。
esp32から常時データが送られると、変数をグローバル変数として宣言すると、カズのスケッチでは19回ほどデータを受信するとフリーズ してしまいました。
arduinoにはスピリッツ関数がないので当初ライブラリを利用したのですが、メモリの問題で実用にはなりませんでした。
そこでシリアル通信で取り込んだ変数をローカル変数とし、波カッコを抜けることでローカル変数を空にすることにします。
シリアル通信
esp32からシリアルで通信すると、受信側ではSerial.read( ) でデータを受けます。
しかし、Serial.readでは1文字ずつの受信になり、受信後のデータ再編が難しくなります。
そこで、何かいい方法がないか探したところ、Serial.readStringUntil( )という関数がありました。
Serial.read( ) 関数はchar型であるのに対しSerial.readStringUntil( ) 関数はString型で、()の中で指定した文字を受信するまで読み込みを続けます。
Serial.readStringUntil()は、シリアルバッファから文字を読み、文字列に書き込む。この関数は、終端文字を検出するかタイムアウトすれば終了する
そこでカズはesp32側ではすべての文字に終端文字を連ねて送信し、arduino mega側ではSerial.readStringUntil( ) 関数で読み込むという方法を採用することにします。
終端文字は”&”にしました。
arduino側ではSerial.readStringUntil(“&")とすることで & を受信するまでデータを取り込みます。
また、データとデータの区切りにはCSVファイルで使われるカンマ区切りとしました。
時刻,温度,気圧,……,&
このような一連のデータ形式になります。
受信した文字列をカンマで分ける
送信ではデータをカンマで区切っていますので、カンマ毎にデータを分けるのにindexOf( )関数を使うことにしました。
構文はmyString.indexOf(val, from)
valには char 、 String型を用いてfromには検索する位置を指定します。
またfromは省略することが出来て、その場合には文字列の最初の位置から検索するということです。
そこで、スケッチでは
int check=(str.indexOf(“,"));
として整数型 check にカンマの位置を代入させます。
文字列を取り出す
str.indexOf( ) 関数でカンマの位置が判りましたので次にデータを取り出します。
文字列を取り出す関数にはsubstring( ) 関数が用意されています。
構文 string.substring(from, to)
fromには開始位置を、to にはカンマの位置を当てます。
data=(str.substring(0,check));
このようにすることでカンマまでの文字をdataに代入出来ました。
繰り返して文字列を取り出す
substring( ) 関数はtoを省略すると最後まで読み込みます。
そこで、開始位置だけ指定して最後まで読み込ませます。
str=(str.substring(check+1));
プラス1はカンマです。(^^;
文字列strに最初のデータとカンマを除いて代入しています。
これをfor文で繰り返すことにしました。
またif文で取り出したデータをそれぞれの変数に代入します。
データは西暦,月,日,時刻、…というように送信していますので
if(i==0){int 西暦=data.toInt();}
if(i==1){int 月=data.toInt();}
if(i==2){int 時刻=data.toInt();}
このような条件式で振り分けました。(プログラムの中では全角文字は使うことが出来ません。 (^^; )
String型をint型に変換する
String型から整数型に変換するには toInt 関数を使います。
取り出した数値をそれぞれ代入します。
ここではグローバル変数で定義したyyに入れ込んでいます。
yy=data.toInt();
String型をfloat型に変換する
String型を小数点のあるフロート型に変換するには toFloat( ) 関数を利用します。
グローバル変数でondoをフロート型で定義し、
ondo=data.toFloat();
とすることで取り出した温度のデータをondoに代入しています。
受信データ取り出しスケッチ
[php]void DATAreading(){
if(Serial.available()){
String str =Serial.readStringUntil('&’);
delay(10);
Serial.println(str);
for( int i = 0; i < 23; i++){
int check=(str.indexOf(","));
String data=(str.substring(0,check));
str=(str.substring(check+1));
if(i==0 && data.toInt()>0){yy=data.toInt();}
if(i==1 && data.toInt()>0){ mo=data.toInt();}
if(i==2 && data.toInt()>0 ){dd=data.toInt();}
…..
….[/php]
esp32でシリアル送信されたデータをarduino megaで受信、データを取り出しシリアルモニタで見てみます。
WIFIとSerial 通信
esp32にはwifi機能が装備されています。ですからデータをgoogleクラウドやカイエンシステムに送信したり受信したり出来ます。
このような送受信時にはSerial通信に乱れが生じることがあるようです。どこが原因なのか特定出来ていませんがarduino mega側で受信データの文字数に異常があった場合には受信文字列を無視することで対応しました
また、esp32とarduinoのシリアル通信にはSerial2を使いデフォルトのSerialと分けることにしました。
現実の運用ではarduinoやesp32はパソコンとは接続していません。
カズはデータの確認や正常に作動しているかを知るためにarduino mega側にLCDモニターを接続し、データの確認を行います。
そのためLCDデータとSerial通信データを分離することにしました。
Serial2やSerial3などをarduinoやesp32で使うための情報がネットには氾濫していますが実際には何も特別な操作は必要ありません。
これについては別記事LCDでまとめる予定です。
カズはLCDをデフォルトのシリアルで、esp32とarduino mega のシリアル通信にはSerial2を利用することにします。
googleの送信開始時には1,通常状態を0として、またヌル文字やデータ異常を勘案したスケッチはこちらです。
修正スケッチ
[php]if(Serial2.available()>2 ){
String str2 =Serial2.readStringUntil('&’);
str=str2;
ch_1=str.length(); //文字数
ch_2=(str.substring(0, 1)).toInt(); //google check
if(ch_1 > 3 && ch_1 < 90 && ch_2 == 0 ){ //文字数とgoogleTXで更新停止
for( int i = 0; i < 24; i++){
int check=(str.indexOf(","));
String data=(str.substring(0,check));
str=(str.substring(check+1));
if(i==0 ){google_tx=data.toInt();}
if(i==1 ){yy=data.toInt();}
………..[/php]
文字数が3文字以上、90文字以下でgoogle送信中でない場合に受信データを取り込むようにしたスケッチです。
送信開始前と送信開始時にデータが乱れ、通常時は正常なシリアル通信ですのでこのようなスケッチにしました。
シリアル通信の文字数
実際の送信文字数は80程あるのに受信側では63文字しか受信できず、ちょっとハマってしまいましたが何のことはなく、デフォルト設定では64バイトに制限されていました。
ということで設定変更して2倍の128バイトにしました。
設定変更はウインドウズの場合にはC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino内にあるHardwareSerial.hファイルを変更します。
HardwareSerial 0から沢山並んでいますがHardwareSerialのhファイルは一つだけです。
このファイルをTeraPadなどで開き
#define SERIAL_TX_BUFFER_SIZE 64
#define SERIAL_RX_BUFFER_SIZE 64
を次のように変更し、上書き保存します。
#define SERIAL_TX_BUFFER_SIZE 128
#define SERIAL_RX_BUFFER_SIZE 128
カズは全部変更してしまいました。 (^^;
他にも修正する必要があるかどうか判りませんが以上の変更だけで正しく受信出来るようになったので良しとしましょう。
シリアル通信 まとめ
arduinoにはスプリット関数が用意されてないので最初はStringSplitterというライブラリを利用しましたが、変数がグローバル宣言されていて19回の受信でフリーズしてしまいました。
最初は原因が判らずハマってしまいましたが、メモリの問題と気づきスプリット関数もどきを自作してみました。
水耕栽培のセンサデータをgooglesheetsに送ったりカイエンに送ったりするのにはesp32が便利なのですが、使えないGPIOピンも多く、これから色々と使うピンも増えてくるので、データをesp32で、実行はarduino megaで行うことにしました。
そのためにセンサデータをesp32とarduinoで共有する必要が出てきたことからシリアル通信をしてみようということでスケッチしてみました。
今年は水耕栽培をデータで攻めてみようと思います。