質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Bluetooth

Bluetoothとは短距離の間でデータを交換するための無線通信規格である。固定・モバイル両方のデバイスから、短波の電波送信を行うことで、高いセキュリティをもつパーソナルエリアネットワーク(PAN)を構築する。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

シリアルポート

シリアルポートは一度に一ビットごと移行される物理的なインターフェイスです。一般的には、9ピンのd-subコネクタであるRS-232を指します。

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

Q&A

解決済

1回答

27043閲覧

シリアル通信で複数のセンサの値を送るには? Arduino→Android Bluetooth接続時

kt.tk.co

総合スコア27

Bluetooth

Bluetoothとは短距離の間でデータを交換するための無線通信規格である。固定・モバイル両方のデバイスから、短波の電波送信を行うことで、高いセキュリティをもつパーソナルエリアネットワーク(PAN)を構築する。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

シリアルポート

シリアルポートは一度に一ビットごと移行される物理的なインターフェイスです。一般的には、9ピンのd-subコネクタであるRS-232を指します。

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

0グッド

1クリップ

投稿2016/11/21 02:31

編集2016/11/21 18:55

■目的・課題

●目的
ArduinoからAndroidにBluetooth接続で複数センサの値を送りたいと考えております。

イメージ
イメージ

●手段
ArduinoとAndroidのBluetooth接続は参考サイトを見て、全く同じコードで問題なく動作させることができました。

そこで次に、Arduinoから複数のセンサの値をシリアル通信で送り、Androidで受信してセンサごとの変数へ代入を行いたいです。

●問題
ただし、調べたところシリアル通信は「データを1ビットずつ連続的に送受信する通信方式」のようです。
そのためAndroidでデータを受け取り画面に表示すると、センサの値の途中で途切れた中途半端な状態で表示されてしまいます。
(Arduinoから1ビットずつ値を送っているため、Arduinoが送られてきたシリアル通信を読み込むタイミングによってセンサの値の途中で表示してしまっているものと考えています)

・例 センサ12個の場合

  1. Arduinoから12個の値をシリアル通信で送り続ける。

(送る値を、「ランダム,0,0,0,0,0,0,0,0,0,0,0¥n」にした場合)
(一番最初をランダムな値、それ以外は0で、すべてカンマ区切り。最後に¥n)

  1. 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 }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kt.tk.co

2016/11/21 13:23

ご指摘ありがとうございます。スマートフォンから編集を行ったことで、何故か画像が表示されなくなってしまったようです。パソコンから「編集」を行ったことで画像が無事に表示されるようになりました。ありがとうございます。
guest

回答1

0

ベストアンサー

USB接続でもarduinoは基本的に9600bpsだから速度が原因ではないと思います。
また時系列を崩さず送信するには、いったん配列に格納し、それを順に送り出す方法が考えられます。(アナログデータなどのサンプリングを一気にやって、できる限りタイムラグを生じさせない)。これである程度の時系列のずれは無くなると思います。(全部読むのに何秒もかからない)
問題はbluetoothとのハンドシェークかなぁ。でもシリアル以上の速度での通信が可能ですから、まずはサンプルと通信を分けて実装を試してみて、bluetoothの通信と分けて検証される事を考えます。

投稿2016/11/21 15:27

MasahikoHirata

総合スコア3747

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kt.tk.co

2016/11/21 18:49

ご回答頂きありがとうございます。 とても参考にさせていただきました。 あれから色々と試したところ、よくわからない症状と新たな問題を見つけました。 まだ検証している最中であることと、現在の状況を整理できていないためもう少し自身で考えてみることに致します。 また、「ハンドシェーク」という単語をしらなかったため、ブルートゥースモジュールでの通信におけるハンドシェークを調べて見る予定です。 今月末まで他の作業を行う必要も出てきたため、来月以降に再度集中してArduinoとAndroidの実装に着手していく予定です。いままでよりも確認や検証が遅くなってしまうため、一度teratailでの質問を終了いたします。予定としては、来月の中盤には大きな問題のない状態で組み上げたいと考えているため、時間を見つけて少しずつ検証していきたいと思います。一つの問題や課題でずっと悩むのは非常に疲れますが、おかげさまで検討するアイデアが尽きずに実装の作業はとても楽しめています。 本日は夜も更けてきたため睡眠を取ろうと思います。 --- 私自身の覚え書きのために簡単に現状をまとめさせて頂きます。 ・arduinoとbluetoothのどちらも115200bpsへ変更して動作確認しました また、配列へ格納した後にシリアル通信で送ることを試してみました。 ご指摘の通り時間あたりに送信できる値の量が増えました。 ただし、Arduinoから送られてくるタイミングと、Androidで読み込む処理が非同期なのは変わらず、カンマ区切りで値を分離してあげる必要がありそうです。 ・通信の干渉?? Arduinoのシリアルポートもしくはシリアルプロッタへ値を書き出す処理(Serial.println( "" );など)を入れると、Androidへの送信が低速になっていることを発見しました。 実際の症状としては、Arduinoのシリアルポートもしくはシリアルプロッタの画面を開くとAndroidでの値の送信が高速に行われ、画面を開いていないとAndroidへの値の送信が非常に低速になるという症状です。Arduinoのシリアルポートもしくはシリアルプロッタへ値を書き出す処理を消すことで問題なく実行できますが、問題の発生条件が不思議で原因を理解できていません。 ・カンマ区切りでの値の分離を実装 結局のところArduinoからカンマ区切りの文字列型へセンサの値を集約した後Androidへを送り、Androidでカンマを排除してセンサごとの変数へ代入する処理をプログラミングしました。 質問欄の最後に載せましたが、場当たり的な例外の処理が多くきれいなものではありません。 また、bluetoothの速度が9600bps の状態では問題なく動いていましたが、115200bpsにしてから処理が途中で止まっているようでした。 恐らくAndroidの処理スピードが追いついていないようで、得られたデータが処理される前にガーベージコレクションにて破棄されているようでした。 ・シリアル通信処理によるサンプリング回数の低下 Arduinoの環境下において、「BluetoothでAndroidにシリアル通信で送る」という処理を入れているとArduinoの処理スピードが落ちているようでした。 そのため、analogReadによってセンサの値を取得する際に、単位時間あたりのサンプリング回数が落ちておりました。今後検討予定です。 ・bluetoothモジュールによるセンサの値へのノイズ 「analogReadによってセンサを取得する処理」をarduino microにて行っています。そのままだと問題ないのですが、arduinoにbluetoothモジュールを付け足すことでセンサの値にノイズが乗っていることを確認しました。そのため、bluetooth用の電源は別に取ることを検討中です。(あまり装置を大型化したくないですが・・・)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問