MochiuWiki : SUSE, EC, PCB
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
Arduinoの基礎 - Bluetoothのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
Arduinoの基礎 - Bluetooth
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == <br><br> == Bluetoothモジュール HC-06 == このセクションでは、Bluetoothの使用前に実施するBluetoothモジュールHC-06の設定方法について記載する。<br> <br> HC-06は、Arduinoとシリアルインターフェイスで接続する。<br> ホストPCとHC-06の接続にはFT232RLを使用して、ATコマンドを送信することにより、デバイス名やボーレート等の設定ができる。<br> WindowsでHC-06の設定を行う場合は、RealTermを使用する。<br> LinuxでHC-06の設定を行う場合は、GNOME Bluetooth、Bluedevil(KDE)、Blueberry、Bluemanを使用する。<br> <br> まず、HC-06のジャンパを3.3[V]にする。<br> <br> COMポートの設定は、RealTermの[Port]タブ -[Port]プルダウンから、<code><数字> = \VCP0</code>を選択する。(Linuxの場合、/dev/ttyUSB0等)<br> ボーレートの設定は、RealTermの[Port]タブ -[Baud]プルダウンから選択する。<br> ここでは、ボーレートを38400[bps]に設定しているが、既定の設定では9600[bps]である。<br> <br> 下図左のように、<code>AT</code>という文字を送信した後、HC-06から<code>OK</code>を受信すれば正常に接続されている。<br> 下図右のように、HC-06のバージョンを確認する場合は、<code>AT+VERSION</code>を送信する。<br> このように、<code>AT+<コマンド></code>という形式のデータを送信することで、モジュールの設定を行うことができる。<br> <br> まず、デバイス名をMYBTに設定するため、以下のコマンドを送信する。<br> HC-06からOKsetnameを受信すれば正常に設定されている。<br> AT+NAMEMYBT <br> 次に、ボーレートを38400[bps]に変更するため、以下のコマンドを送信する。(6は38400を表す)<br> AT+BAUD6 <br> これらの設定は、HC-06の再起動後に有効になる。<br> <u>また、Bluetoothの既定の接続PINは1234である。</u><br> <br><br> == Bluetoothモジュール HC-06と温度センサ == このセクションでは、Arduino、BluetoothモジュールHC-06、温度センサTMP36を使用して、温度データをBluetoothで送信する。<br> <br> TMP36は、アナログ出力温度測定ICである。<br> 詳細は、Arduinoの基礎 - 温度センサを参照すること。<br> <br> ここでは、HC-06のデバイス名をMYBT、ボーレートを38400[bps]に設定している。<br> HC-06の設定については、上記のセクションを参照すること。<br> <br> 下図に、回路図および配線図を示す。<br> 下図右では、ArduinoからのTXを分圧している抵抗器が接続されている。<br> <br> 以下に、Arduinoのサンプルコードを記述する。<br> HC-06がGET:TEMPという文字を受信した時、温度データを送信している。<br> ここでは簡易的な通信のみを行っているが、クライアント側とサーバ側でデータフォーマットの取り決めをする方がよい。<br> <br> このままでは、データを受信するクライアントが無いため、<br> 次のセクションでは、スマートフォンとHC-06を接続して、遠隔通信で温度データを受信する。<br> <syntaxhighlight lang="c++"> #include <SoftwareSerial.h> const int PIN_TMP36 = 1; SoftwareSerial bt = SoftwareSerial(2,3); boolean bt_found = false; void setup() { Serial.begin(9600); bt.begin(38400); delay(3000); String s = bt_sendCommand("AT"); Serial.println("AT --> " + s); if( s == "OK" ) { bt_found = true; } else { return; } } void loop() { if(!bt_found) { return; } if(bt.available() == 0) { return; } String r = ""; while(bt.available() > 0) { char ch = bt.read(); r += ch; } Serial.println("App Command Received: " + r); process_command( r ); } float get_temperature() { int i = analogRead( PIN_TMP36 ); float f = i * 5.0 / 1023.0; // TMP36 // C = 100V - 50 return 100 * f - 50; } void process_command(String r) { if( r == "GET:TEMP" ) { float temp = get_temperature(); String s = String(temp, 1); bt.print(s); bt.flush(); } else { Serial.println("Unknown command."); } } String bt_sendCommand(String cmd) { bt.print(cmd); bt.flush(); Serial.println("Waiting [" + cmd + "]"); while(bt.available() == 0) { delay(300); } Serial.println("OK [" + cmd + "]"); String r = ""; while(bt.available() > 0) { char ch = bt.read(); r += ch; } Serial.println("Response [" + r + "]"); return r; } </syntaxhighlight> <br><br> == HC-06とクライアント == 上記のセクションに記載したArduinoで温度データを送信するデバイス・ソフトウェアにおいて、<br> Bluetoothを使用してPCまたはスマートフォンと接続して、温度データを受信するPCまたはスマートフォンのソフトウェアの作成手順を記載する。<br> <br> ==== PCのソフトウェア ==== ソフトウェアの開発は、Visual Studio 2019のコンソールを用いている。<br> 予め、HC-06とペアリングする必要がある。PINは、0000または1234のいずれかである。<br> <br> 標準ライブラリにあるBluetoothのAPIはUWP専用であるため、NuGetからコンソールでも使用できる<code>UwpDesktop</code>をインストールする。<br> Visual Studioの[プロジェクト]メニューバー - [NuGet パッケージの管理]を選択して、UwpDesktopと入力してインストールする。<br> <br> 以下に、Bluetooth通信の手順を大まかに記載する。<br> * 周囲のBluetoothデバイスをスキャンする。 * 見つかったデバイスから、接続・通信を行う端末かどうかを確認して、データの受信を行う。 <br> まず、周囲のBluetoothデバイスをスキャンする。<br> Bluetoothデバイスは、アドバタイズパケットという自身の場所を伝えるためのデータを発信するため、<br> そのパケットをスキャンすることでBluetoothデバイスを認識して接続することができる。<br> <br> 以下の例では、<code>BluetoothLEAdvertisementWatcher</code>クラスのプロパティの値を設定した後、Startメソッドを実行してスキャンしている。<br> <syntaxhighlight lang="c#"> using System; using System.Threading; using Windows.Devices.Bluetooth.Advertisement; class Program { private static BluetoothLEAdvertisementWatcher advWatcher; static async void Main(string[] args) { Console.WriteLine("Start"); advWatcher = new BluetoothLEAdvertisementWatcher(); advWatcher.SignalStrengthFilter.SamplingInterval = TimeSpan.FromMilliseconds(1000); advWatcher.ScanningMode = BluetoothLEScanningMode.Passive; advWatcher.Received += WatcherReceived; advWatcher.Start(); await Task.Delay(60000); advWatcher.Stop(); Console.WriteLine("Stop"); } } </syntaxhighlight> <br> 次に、スキャンして見つかったデバイスにおいて、接続・通信を行うデバイスかどうか確認する。<br> <code>WatcherReceived</code>メソッドからスキャンされたデバイスの情報を受信するので、その情報を元に確認する。<br> # スキャンして見つかったデバイスが持つUUIDと、接続・通信を行うデバイスのUUIDを比較する。 # 同じなら、スキャンを停止してそのデバイスと接続する。 # 接続したデバイスから、サービスのUUIDとキャラクタリスティックのUUIDを用いて目的のデータにアクセスする。 # アクセスに成功した後、コールバックを設定して、目的のデータの受信する。<br>以下のサンプルコードにおいて、<code>CharacteristicChangedVroomController</code>メソッド(ユーザ自身が作成したメソッド)は、<br>受信したデータ(バイト列)に対して、任意の処理を行うメソッドである。 <br> UUIDを確認する理由は、Bluetooth通信で使用されるデータフォーマットGATTがあるためである。<br> GATTは、<u>サービス</u>と<u>キャラクタリスティック</u>の2つから構成されており、各々がUUIDで設定されている。<br> サービスがフォルダ、キャラクタリスティックがファイルに相当する。<br> <br> GATTに則っているBluetoothデバイスは、必ずこの形式でデータを送受信するため、UUIDを確認することで目的のデバイスを判別できる。<br> UUIDを知る方法は、販売元が公開している場合や自身で全パケットを確認する方法がある。<br> <syntaxhighlight lang="c#"> private GattDeviceService gattService; private GattCharacteristic vroomService; private readonly String serviceGuid = "00001101-0000-1000-8000-00805f9b34fb"; private async Task WatcherReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args) { bool find = false; var bleServiceUUIDs = args.Advertisement.ServiceUuids; foreach (var uuidone in bleServiceUUIDs) { // serviceGuid : デバイス固有のGUID if (uuidone == serviceGuid) { // 発見 find = true; break; } } if (find) { try { // スキャンの停止 advWatcher.Stop(); // デバイスと接続 BluetoothLEDevice device = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress); // UUIDを用いて目的のデバイスを取得 gattService = device.GetGattService(serviceGuid); // キャラクタリスティックUUIDを用いて目的のキャラクタリスティックを取得 var characteristics = gattService.GetCharacteristics(new Guid("キャラクタリスティックのGuid")); // 戻り値の配列が空かどうか確認 if (characteristics.Count > 0) { vroomService = characteristics.First(); if (vroomService == null) { throw new Exception("Not Exist Service"); } // 読み取り可能な場合、キャラクタリスティックにアクセスしてコールバックを設定 if (vroomService.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Read)) { vroomService.ValueChanged += CharacteristicChangedVroomController; Console.WriteLine("Connect"); await vroomService.ReadClientCharacteristicConfigurationDescriptorAsync(); } } } catch (Exception ex) { Console.WriteLine($"Exception...{ex.Message})"); } } } </syntaxhighlight> <br> ==== スマートフォン(Android)のソフトウェア ==== クライアントからサーバに接続する時、RFCOMMチャネルを作成してBluetoothSocketを作成する必要があるが、<br> この時に指定するUUIDは、SPP(シリアルポートプロファイル)のUUIDである00001101-0000-1000-8000-00805f9b34fbを指定する。<br> <br> また、デバイス名はMYBTとしているため、BondedリストからBluetoothDeviceを探す際の名前も変更する。<br> <br> Bluetooth通信を使用するパーミッションを設定する。<br> ディスカバリは行わないため、LOCATIONのパーミッションは設定しない。<br> <syntaxhighlight lang="xml"> <!-- AndroidManifest.xmlファイル --> <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.keicode.android.testapps.bttest1c"> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permission android:name="android.permission.BLUETOOTH"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:configChanges="orientation"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> </syntaxhighlight> <br> 状態を表示するためのTextViewとArduinoおよびHC-06から送信される文字を表示するためのTextViewの計2つのTextViewを並べている。<br> <syntaxhighlight lang="xml"> <!-- res/layout/activity_main.xmlファイル --> <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.keicode.android.testapps.bttest1c.MainActivity"> <TextView android:id="@+id/btStatusTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Not connected" /> <TextView android:id="@+id/tempTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="(unknown)" android:layout_below="@id/btStatusTextView" android:textSize="24sp"/> </RelativeLayout> </syntaxhighlight> <br> 以下のサンプルコードでは、ワーカースレッド用に1つのクラスを定義しているだけである。<br> 例外が発生した場合、その状況をHandlerを介してUI側にフィードバックしている。<br> <syntaxhighlight lang="java"> // MainActivity.java package com.keicode.android.testapps.bttest1c; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.widget.TextView; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.Set; public class MainActivity extends AppCompatActivity { static final String TAG = "BTTEST1"; BluetoothAdapter bluetoothAdapter; TextView btStatusTextView; TextView tempTextView; BTClientThread btClientThread; final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { String s; switch(msg.what) { case Constants.MESSAGE_BT: s = (String) msg.obj; if(s != null) { btStatusTextView.setText(s); } break; case Constants.MESSAGE_TEMP: s = (String) msg.obj; if(s != null) { tempTextView.setText(s); } break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { Log.d(TAG, "onCreate"); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Find Views btStatusTextView = (TextView) findViewById(R.id.btStatusTextView); tempTextView = (TextView) findViewById(R.id.tempTextView); if(savedInstanceState != null) { String temp = savedInstanceState.getString(Constants.STATE_TEMP); tempTextView.setText(temp); } // Initialize Bluetooth bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if( bluetoothAdapter == null ) { Log.d(TAG, "This device doesn't support Bluetooth."); } } @Override protected void onResume() { super.onResume(); btClientThread = new BTClientThread(); btClientThread.start(); } @Override protected void onPause() { super.onPause(); if(btClientThread != null) { btClientThread.interrupt(); btClientThread = null; } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString(Constants.STATE_TEMP, tempTextView.getText().toString()); } public class BTClientThread extends Thread { InputStream inputStream; OutputStream outputStrem; BluetoothSocket bluetoothSocket; public void run() { byte[] incomingBuff = new byte[64]; BluetoothDevice bluetoothDevice = null; Set<BluetoothDevice> devices = bluetoothAdapter.getBondedDevices(); for(BluetoothDevice device : devices) { if(device.getName().equals(Constants.BT_DEVICE)) { bluetoothDevice = device; break; } } if(bluetoothDevice == null) { Log.d(TAG, "No device found."); return; } try { bluetoothSocket = bluetoothDevice.createRfcommSocketToServiceRecord(Constants.BT_UUID); while(true) { if(Thread.interrupted()) { break; } try { bluetoothSocket.connect(); handler.obtainMessage(Constants.MESSAGE_BT, "CONNECTED " + bluetoothDevice.getName()).sendToTarget(); inputStream = bluetoothSocket.getInputStream(); outputStrem = bluetoothSocket.getOutputStream(); while(true) { if(Thread.interrupted()) { break; } // Send Command String command = "GET:TEMP"; outputStrem.write(command.getBytes()); // Read Response int incomingBytes = inputStream.read(incomingBuff); byte[] buff = new byte[incomingBytes]; System.arraycopy(incomingBuff, 0, buff, 0, incomingBytes); String s = new String(buff, StandardCharsets.UTF_8); // Show Result to UI handler.obtainMessage(Constants.MESSAGE_TEMP, s).sendToTarget(); // Update again in a few seconds Thread.sleep(3000); } } catch(IOException e) { // connect will throw IOException immediately // when it's disconnected. Log.d(TAG, e.getMessage()); } handler.obtainMessage(Constants.MESSAGE_BT, "DISCONNECTED").sendToTarget(); // Re-try after 3 sec Thread.sleep(3 * 1000); } } catch(InterruptedException e) { e.printStackTrace(); } catch(IOException e) { e.printStackTrace(); } if(bluetoothSocket != null) { try { bluetoothSocket.close(); } catch(IOException e) { } bluetoothSocket = null; } handler.obtainMessage(Constants.MESSAGE_BT, "DISCONNECTED - Exit BTClientThread").sendToTarget(); } } } </syntaxhighlight> <br> <syntaxhighlight lang="java"> // Constants.java package com.keicode.android.testapps.bttest1c; import java.util.UUID; public interface Constants { public static final String BT_DEVICE = "MYBT"; public static final UUID BT_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); public static final String STATE_TEMP = "STATE_TEMP"; public static final int MESSAGE_BT = 0; public static final int MESSAGE_TEMP = 2; } </syntaxhighlight> <br> 次に、環境設定を行う。<br> ここでは、デバイス名はMYBTとしているが、これをあらかじめペアリングする。<br> 以上の変更を行い、Arduinoとスマートフォンの両方を起動することで自動的にBluetooth接続して、スマートフォンの画面に温度が表示される。<br> <br><br> __FORCETOC__ [[カテゴリ:Arduino]]
Arduinoの基礎 - Bluetooth
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse