■目的・課題
●目的
ArduinoからAndroidにBluetooth接続で複数センサの値を送りたいと考えております。
●手段
ArduinoとAndroidのBluetooth接続は参考サイトを見て、全く同じコードで問題なく動作させることができました。
そこで次に、Arduinoから複数のセンサの値をシリアル通信で送り、Androidで受信してセンサごとの変数へ代入を行いたいです。
●問題
ただし、調べたところシリアル通信は「データを1ビットずつ連続的に送受信する通信方式」のようです。
そのためAndroidでデータを受け取り画面に表示すると、センサの値の途中で途切れた中途半端な状態で表示されてしまいます。
(Arduinoから1ビットずつ値を送っているため、Arduinoが送られてきたシリアル通信を読み込むタイミングによってセンサの値の途中で表示してしまっているものと考えています)
・例 センサ12個の場合
- Arduinoから12個の値をシリアル通信で送り続ける。
(送る値を、「ランダム,0,0,0,0,0,0,0,0,0,0,0¥n」にした場合)
(一番最初をランダムな値、それ以外は0で、すべてカンマ区切り。最後に¥n)
- Androidに表示される値は次のようになります。
(上の写真の順番は時系列に添っていませんが、更新される度に前の値の続きから表示されるので、データの欠損などはなさそうです。)
対処療法としてAndroid側にてカンマ区切りで分離しようと考えていますが、処理が複雑になってしまいうまく実装できていない状態です。(カンマ区切りに分割した後、必要な箇所は変数に代入して、¥nより後の値は次のデータ取得時に持ち越す。 といった処理がうまくできていないです。)
そもそも、USBホストケーブルで行っていたとき(下にまとめました)には、センサごとにArduinoからデータを送ってもらい、Androidでセンサごとの変数へ代入できていました。(シリアル通信ではない?)
そういった処理を最初からできたら根本から解決できるかと考え調べていますが、なかなか良い方法が分からずに困っています。
●質問内容
Bluetoothの通信時に、Arduinoで読み取ったセンサの値(アナログ値の0~1024)を複数個まとめて、Androidに送る適切な方法をお教えいただけますと幸いです。
●考え
Bluetooth時にも、USBホストケーブルと同様の「Arduinoからの送信処理」を行えば実現できそうと考えていました。
Arduino
1acc.write(&pack, sizeof(Pack));
しかし、Arduinoのacc.writeコマンドはUSBホストケーブルでの接続時でしか実行できないようでした。
そのため、シリアル通信で同様の送信方法がないかと探しております。
このサイトより
「1回のSerial.write()で送れるデータは1バイト、すなわち数値として表現できる範囲は0~255です。」
とあることから、センサごとに値を送るのは難しいのかもしれません。
現在試行錯誤の真っ最中です。
■Bluetooth接続時のコード(簡略化のため、センサを2つだけに減らしたコードを載せました。)
Arduinoの送信部分
C
1 2 3//文字を送るために、文字のバッファを定義 4char buff[255]; 5 6//////////// 7//初期設定// 8//////////// 9 10void setup(){ 11 12 // arduinoのシリアルモニタ用 13 Serial.begin(19200); 14 15 // Bluetooth用のシリアルのポートを設定 16 Serial1.begin(9600); 17 18} 19 20void loop(){ 21 22 //バッファに値を代入 23 sprintf(buff, "%d,%d\n",analogRead(0),analogRead(1)); 24 25 //Arduinoにセンサの値を送る 26 Serial1.write(buff); 27 28} 29
Andrroidの受信部分
Java
1 2 InputStream mmInStream = null; 3 4 Message valueMsg = new Message(); //メッセージを入れる変数 5 valueMsg.what = VIEW_STATUS; //メッセージがステータスである とする 6 valueMsg.obj = "connecting..."; //ステータスに書き込むテキストを指定 7 mHandler.sendMessage(valueMsg); //テキスト書き換え関数を実行する 8 9 try{ 10 11 // 取得したデバイス名を使ってBluetoothでSocket接続 12 mSocket = mDevice.createRfcommSocketToServiceRecord(MY_UUID); 13 mSocket.connect(); 14 mmInStream = mSocket.getInputStream(); 15 mmOutputStream = mSocket.getOutputStream(); 16 17 // InputStreamのバッファを格納 18 byte[] buffer = new byte[1024]; 19 20 // 取得したバッファのサイズを格納 21 int bytes; 22 valueMsg = new Message(); 23 valueMsg.what = VIEW_STATUS; //メッセージがステータスである とする 24 valueMsg.obj = "connected."; //ステータスに書き込むテキストを指定 25 mHandler.sendMessage(valueMsg); //テキスト書き換え関数を実行する 26 27 //接続中である とする 28 connectFlg = true; 29 30 //フラグが立っていれば、Arduinoからシリアル通信のデータを読み込み 31 while(isRunning){ 32 33 // InputStreamの読み込み 34 bytes = mmInStream.read(buffer); //シリアル通信で送られてきたデータサイズを取得 35 Log.i(TAG,"bytes="+bytes); //データサイズをログに書き出し 36 // String型に変換 37 String readMsg = new String(buffer, 0, bytes); //データそのもの,?,データサイズ 38 39 // 「null以外」 かつ 「空欄ではない」 なら表示 40 if(readMsg.trim() != null && !readMsg.trim().equals("")){ 41 Log.i(TAG,"value="+readMsg.trim()); 42 43 valueMsg = new Message(); 44 valueMsg.what = VIEW_INPUT; //メッセージがシリアル通信のINPUTである とする 45 valueMsg.obj = readMsg; //Arduinoから取得したメッセージを代入 46 mHandler.sendMessage(valueMsg); //テキスト書き換え関数を実行する 47 } 48 else{ 49 // Log.i(TAG,"value=nodata"); 50 } 51 } 52 }catch(Exception e){ 53 54 } 55
■USBホストケーブルでの通信
Bluetoothで無線化をする前は、USBホストケーブルでセンサの値を送っていました。
処理は問題なく、Androidでセンサごとにデータを受け取ることができておりました。
■USBホストケーブル接続時のコード
Arduinoの送信部分
C
1//AndroidのUSBアクセサリ接続 2#include <Max3421e.h> 3#include <Usb.h> 4#include <AndroidAccessory.h> 5 6 7// AndroidAccessoryオブジェクト作成 8AndroidAccessory acc("MyManufacturer", // 製造者名 9 "MyModel", // モデル名 10 "テスト", // 説明文 11 "1.0", // バージョン名 12 "http://android.com", // URL 13 "0000000012345678"); // シリアル番号 14 15 16// Andloidにデータを送るためのパッケージを定義 17struct Pack { 18 long sensor1; /* 4バイト */ 19 long sensor2; /* 4バイト */ 20}; 21 22struct Pack pack; 23 24//////////// 25//初期設定// 26//////////// 27void setup() 28{ 29 Serial.begin(9600); 30 31 acc.powerOn(); //USB Host機能を有効にする。(Androidの通信用) 32} 33 34void loop(){ 35 36 //装置(Android)に接続していたら 37 if (acc.isConnected()) { 38 39 //メッセージ生成 (packの中に送りたいメッセージを詰め込む) 40 pack.sensor1 = analogRead(0); 41 pack.sensor2 = analogRead(1); 42 43 //データをAndroidに送る 44 acc.write(&pack, sizeof(Pack)); 45 } 46} 47
Andrroidの受信部分
Java
1 //センサの値を入れる変数 2 int SensorVal[] = new int[2]; 3 4 //受信データを入れる変数 5 byte [] buffer = new byte[100000];//受信した全てのデータ 6 7 //初めは必ず読み取りに行く 8 int ret = 0; //受信データの長さ「ret」を定義する 9 10 // Arduinoから受信 11 try { 12 //入力ストリームの読み込み 13 ret = mInputStream.read(buffer); 14 } catch (IOException e) { 15 e.printStackTrace(); 16 } 17 18 //取得したデータを、センサごとに分ける。 19 byte[] arr = new byte[ret]; 20 System.arraycopy(buffer,0,arr,0,ret); 21 22 ByteBuffer byteBuffer = ByteBuffer.wrap(arr); 23 byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 24 25 //得られた値を変数に入れる 26 SensorVal[0] = byteBuffer.getInt(); // 4バイト 27 SensorVal[1] = byteBuffer.getInt(); // 4バイト 28
カンマ区切りでの通信
どうしても場当たり的な例外の処理が多く、無駄にコンピューターリソースを使っています。
このような処理の要らないシリアル通信での送受信方法を知ることが最優先事項です。
Java
1 2 //前回以降の残りのデータを入れる文字列型の変数 3 String leftover = ""; 4 5 6 //カンマ区切りを分離して、表示する関数 7 public void DataSplit(String msgStr){ 8 9 boolean StartFlag = false; 10 boolean EndFlag = false; 11 12 //配列の添字用にセンサ番号をカウントする 13 int counter = 0; 14 15 //前回以降の残りのデータと、今回のデータを繋げる 16 msgStr = leftover + msgStr; 17 18 //シリアル通信から送られてくるデータを分割する 19 String[] SplitData = msgStr.split(",", 0); 20 21 22 //取得したデータの数だけ繰り返す 23 for (int i=0; i<SplitData.length; i++){ 24 25 //終了フラグが立っていなければ 26 if (!EndFlag){ 27 28 //前回までにスタートがあれば 29 if (StartFlag){ 30 31 //スタート以降にエンドがあるなら 32 if (SplitData[i].equals("E")){ 33 34 EndFlag = true; 35 36 //Eがあれば一度空にして、残ったものだけを引き次ぐ 37 leftover = ""; 38 39 }else{ 40 41 // 「null以外」 かつ 「空欄ではない」 なら (,区切りが先頭または最後に来ていると、残りデータとの結合時に,,となってしまう可能性があるため) 42 if(SplitData[i].trim() != null && !SplitData[i].trim().equals("")){ 43 //センサーデータの配列に入れる 44 SensorData[counter] = SplitData[i]; //スタートの次からデータ配列に入れ始める 45 counter++;//カウンターを増やす 46 } 47 48 } 49 50 //今がスタートであれば 51 }else if (SplitData[i].equals("S")){ 52 53 StartFlag = true; 54 55 } 56 57 }else{ 58 59 //カンマ区切りの最後の数値 かつ 文字の一番最後にカンマがない 60 if (i == SplitData.length - 1 && msgStr.charAt(msgStr.length()-1) !=',' ){ 61 62 //最後なら、一番最後に,を入れない! 63 64 //終了フラグが立っていれば、一つずつ残りデータに入れていく 65 leftover = leftover + "," +SplitData[i]; //前後にカンマを入れる。カンマは連続しても問題ない(カンマ区切りで抽出したものが空白になるため除外できる)が、カンマ無しで数値が繋がると問題なため。 66 67 }else{ 68 69 //終了フラグが立っていれば、一つずつ残りデータに入れていく 70 leftover = leftover + "," +SplitData[i] + ","; //前後にカンマを入れる。カンマは連続しても問題ない(カンマ区切りで抽出したものが空白になるため除外できる)が、カンマ無しで数値が繋がると問題なため。 71 72 } 73 74 } 75 76 } 77 78 79 //エンドがあれば、センサのデータ配列に入れ、あまりを繰り越す 80 if (EndFlag){ 81 82 //出力文字を定義 83 String OutputString = ""; 84 85 //出力用にセンサーの文字を結合する 86 for (int i=0; i<12; i++){ 87 OutputString = OutputString +"センサ " + String.valueOf(i) + " = " + SensorData[i] + "\n"; 88 } 89 90 //画面に出力する 91 Message valueMsg = new Message(); 92 valueMsg.what = VIEW_INPUT; //メッセージがシリアル通信のINPUTである とする 93 valueMsg.obj = OutputString; //Arduinoから取得したメッセージを代入 94 mHandler.sendMessage(valueMsg); //テキスト書き換え関数を実行する 95 96 97 }else{//エンドがなければ、何もせずにシリアル通信のデータを繰り越す 98 99 //Eがない場合はそのまま引き継ぐため代入。 100 leftover = msgStr; 101 102 } 103 104 }
回答1件
あなたの回答
tips
プレビュー