Bluetooth経由でArduino×Processing(PC)の無線通信をやってみました。まともに通信できるようになるまでいろいろと遠回りして、ようやく安定した通信ができるようになりました。結論が分かってしまうとたいした内容ではないのですが、そこにたどり着くまでに随分時間が掛かりました。
有線シリアル通信から順を追っていきたいと思います。Processing側から送信要求をして、Arduino側からデータを送信、Processing側で受信処理をしたらまた送信要求をするというよくある通信方法(ハンドシェイク)で行ってます。
Arduinoからは6軸センサーの情報(各軸の加速度、角加速度)と相補フィルターを掛けた角度値、Arduinoの処理内容などのデータを送信してます。
Processing側では受信したデータをもとにリアルタイムで視覚化してます。
まずは有線
作り掛けですが、Arduino側の6軸センサー情報をリアルタイムにモニタリングしてます。(有線であれば全く通信に問題無しです。)
通信部のソース抜粋です。
◇Arduino側ソース
if (Serial.available() > 0) { int val = Serial.read(); if (val == 's') { Serial.print(dt); Serial.print(","); : //省略// : Serial.print(roll, 4); Serial.print(","); Serial.print(pitch, 4);Serial.print(","); Serial.println(yaw, 4); }
普通のシリアル通信です。Processing側からデータ「s」を受信したら、送信する情報を文字列のカンマ区切りで送信。文字列最後に改行を送信してます。
◇Processing側
void serialEvent() { packet = myPort.readStringUntil('\n'); // read from port until '\n' if (packet != null) { data = split(packet, ","); dt = float(data[0]); ax = float(data[1]); ay = float(data[2]); : //省略// : } myPort.write("s"); }
serialEventはデータを受信したら呼ばれる関数です。改行コードがくるまでデータを読み込み、その後カンマ区切りで文字列に分けて、順次変数へ格納しています。処理後に”s”をArduino側へ送信、次のデータ送信を要求します。
有線シリアル通信であれば全く問題なかったです。レスポンスも良く、データの欠損も無かったです。
無線化
Bluetoothモジュールを使用して無線化してます。Arduinoとモジュール間はシリアル通信。無線規格もBluetooth SPP通信を使用するため、見た目のスケッチはそのまま有線シリアルで通信するときのように記述すればO.K。
Arduino×モジュール間はソフトウェアシリアルを使用。Serial.オブジェクトを、自分で定義したBluetooth.オブジェクトに変更しただけのスケッチで通信を試してみました。
結果、ほとんど通信できず・・・。少しは通信するのですが、ほんとに少しだけ・・。ものすごく不安定です。データは欠落するし、途中で切断?固まってしまうような状態になってしまうこともありました。運が良くて数秒~数十秒程度、まともに通信しただけです。
問題を切り分けるためにArduino・Processingともに処理内容は一切省き、通信のみ(適当な文字列のみ)の記述で試してみたところ、少しは通信状態が良くなったのですが、やはり途中でデータが欠落したり、切断されたりします。
どうも Processing の serial.Event が最初のバイトを受信した瞬間に呼び出されて、全データを受信する前に読込処理が進んでしまい、次の送信要求をしてしまっているようです。であればデータを読み込むまで受信を続ける命令をさせればうまくいくかと思い、
myPort.bufferUntil('\n');
改行コードを受信するまでシリアルデータを受け続ける命令を追加。だいぶ通信が安定したのですが、Arduino側で演算処理(センサー読込や相補フィルターなど)をすると途端に不安定となります。ノイズかな?と思い、ダメ元でパスコンやArduino×モジュール間の線を極力短くしてみたのですがほとんど変わらず・・・。
次に送信データを文字列からバイト(数値)に変えてみました。文字列送信ではセンサー値によって、その都度送信 byte 数が変わってしまいます。送信 byte 数を固定して、受信側できっちりカウントしてから読み込み処理を行ってみました。
◇Arduino側(送信側)では
if (Bluetooth.available()) { int val = Bluetooth.read(); if (val == 's') { Bluetooth.write(int(roll * 100) >> 8); Bluetooth.write(int(roll * 100) & 0xFF); : //省略// :
processing側からの送信要求(”s”)を受信したら、上記の要領でバイトで送信します。小数点が含まれているため一旦100倍した値を使用。また大きな値のため、2byte分を上位・下位 8bit ずつに分けて送信。これであれば、送信バイト数を固定できます。バイト送信は .print ではなく .write を使用します。
◇Processing側(受信)では
void readPacket() { if (myPort.available()==20) { byte highByte=byte(myPort.read()); byte lowByte=byte(myPort.read()); roll = (highByte<<8)+(lowByte&0x000000ff); roll /= 100; : //省略// : myPort.write("s"); } }
serialEventを使用した呼び出しは無駄に呼び出されるため止めました。単純にポーリングで受信確認してます。20byte 分のデータを受信していたら読込処理を行ってます。(ここでは20byte分のデータです)受信したデータ上位・下位 8bit を順に戻していきます。20byte分の処理が完了したら次の送信要求(”s”)を送信します。
文字列よりも送信データ量が随分減りました。今回のケースでは半分以下になってます。
この方法であれば取りこぼしが少なくうまくいくかと思ったのですが、これまでとあまり変わりませんでした。Arduio側で送信処理以外の処理を無くせばある程度通信するのですが、いろいろなことをさせると途端に不安定になります。
ソフトウェアシリアルは不安定と聞いていたので、最後にArduino×Bluetoothモジュール間をハードウェアシリアルに変えてみました。
・・・何故か全く通信しなくなりました・・・・・。
調べていたらこんなことが判明。
Arduino/Genuino101でハードウェアシリアルの RX/TX pin(0/1番pin)を使用する場合には、「serial.**」クラスではなくて「serial1.**」を使うみたいです。
「serial1.**」に記述を変えたら安定した無線通信があっさりできました。有線のときと遜色無しです。
ソフトウェアシリアルは不安定と聞いていたのですが、ほんとに不安定でした。外付け部品でシリアル通信する場合はハードウェアシリアルを使ったほうが無難です。
それにしてもArduino/Genuino101のハードウェアシリアルが serial1. クラスで定義されているとはなかなか気付けなかったです。ほとんどどこにもそんな情報ありませんでしたし・・。
これで Arduino の無線化もだいぶ目処がたったので次のステップに進んでいきたいと思います。
コメント
サーボモータとモーターの正転逆転停止を同時にスマホで制御したいのですが、この前のサーボモータのプログラムにモータの正転逆転停止を制御するプログラムを追加することはできますか。アプリ側にもモータの正転逆転停止などのボタンの追加などもよければ教えてくれませんか。簡単に言うとサーボモータを動かしながら、モーターを動かすことがしたいです。
よければお願いします。
関連する記事内容のページでコメント頂けると幸いです。
回路、ソフト、制御、通信内容、全てを紹介すると結構なボリュームとなってしまいます。要所だけでよければ時間を見つけて記事投稿で紹介したいと思います。ただボタン追加は基本です。モータやサーボの制御も基本です。Arduinoもそうですが特にMIT App Inventorは非常にお手軽な反面、言語の本質部分をだいぶスポイルしてしまっています。ハード(回路)、ソフト(言語)、制御、通信、など、まずは基本的な内容を理解することをお勧めします。
モータの制御のボタン追加やモータとサーボをbluetooth接続で同時に動かすプログラムの紹介をお願いします。
モータードライバはTOSHIBATのA7291Pを使用しようと考えています。
自分も言語や制御について勉強しているのですが、まだまだ勉強が足りなくすみません。
お手数をおかけしますがお願いします。