【背景】
手上有个android设备:
- 平板PAD:三星的Galaxy Tab 3 10.1 GT-P5210
- 其中带有蓝牙模块。
- 另外有个蓝牙的HART猫:
- 【记录】折腾蓝牙接口的HART猫:MACTek的Viator蓝牙HART猫
- 是对应的CSR8510 A10的芯片的。
- HART设备
- 蓝牙HART猫已连接好的
现在希望可以在PAD上通过蓝牙去连接蓝牙的HART猫,然后再去操作HART的设备。
现在就是去android中写对应的蓝牙模块的程序,此处暂且叫做蓝牙驱动吧。
其中此处的开发环境是ADT。
【折腾过程】
1.参考:
Bluetooth | Android Developers
去添加权限:
添加好了:
2.
参考代码期间,遇到:
【已解决】Android的蓝牙实例代码中找不到REQUEST_ENABLE_BT
然后再去用BluetoothAdapter去检测是否支持蓝牙,且支持的话去打开:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | private final int REQUEST_ENABLE_BT = 1 ; private BluetoothAdapter mBluetoothAdapter; private void testBluetooth() { mBluetoothAdapter= BluetoothAdapter.getDefaultAdapter(); if (mBluetoothAdapter == null ) { // Device does not support Bluetooth Toast.makeText(getApplicationContext(), "Device does not support Bluetooth" , Toast.LENGTH_LONG).show(); } else { //android.bluetooth.BluetoothAdapter@211120b0 Toast.makeText(getApplicationContext(), "Device support Bluetooth" , Toast.LENGTH_SHORT).show(); if (!mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); } else { scanOrDiscoverBtDevices(); } } } @Override protected void onActivityResult( int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_ENABLE_BT) { if (resultCode == RESULT_OK){ Toast.makeText(getApplicationContext(), "Enabled Bluetooth now" , Toast.LENGTH_LONG).show(); scanOrDiscoverBtDevices(); } else { Toast.makeText(getApplicationContext(), "Not enable Bluetooth !" , Toast.LENGTH_LONG).show(); } } } |
3.关于检测蓝牙状态变化,而去实现蓝牙被打开还是关闭了,可以参考:
Optionally, your application can also listen for the ACTION_STATE_CHANGED broadcast Intent, which the system will broadcast whenever the Bluetooth state has changed. This broadcast contains the extra fields EXTRA_STATE and EXTRA_PREVIOUS_STATE , containing the new and old Bluetooth states, respectively. Possible values for these extra fields are STATE_TURNING_ON , STATE_ON , STATE_TURNING_OFF , and STATE_OFF . Listening for this broadcast can be useful to detect changes made to the Bluetooth state while your app is running. |
4.另外,看到提示了:
Tip: Enabling discoverability will automatically enable Bluetooth. If you plan to consistently enable device discoverability before performing Bluetooth activity, you can skip step 2 above. Read about enabling discoverability, below. |
这也就是我之前疑惑的:
别的那个android的app,可以点击开启蓝牙,而无需弹出请求权限的对话框。
就是通过这个discovery功能实现的。
提示:
后来又遇到:
【已解决】Android中运行startActivityForResult后但是onActivityResult不执行
5.目前已经实现了,既可以scan那些paired,也可以discover搜寻当前附近的蓝牙设备。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | private final int REQUEST_ENABLE_BT = 1 ; private BluetoothAdapter mBluetoothAdapter; private void testBluetooth() { mBluetoothAdapter= BluetoothAdapter.getDefaultAdapter(); if (mBluetoothAdapter == null ) { // Device does not support Bluetooth Toast.makeText(getApplicationContext(), "Device does not support Bluetooth" , Toast.LENGTH_LONG).show(); } else { //android.bluetooth.BluetoothAdapter@211120b0 Toast.makeText(getApplicationContext(), "Device support Bluetooth" , Toast.LENGTH_SHORT).show(); if (!mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); } else { scanOrDiscoverBtDevices(); } } } private void scanOrDiscoverBtDevices(){ //scanBtDevices(); discoverBtDevices(); } // Create a BroadcastReceiver for ACTION_FOUND private final BroadcastReceiver mReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // When discovery finds a device if (BluetoothDevice.ACTION_FOUND.equals(action)) { // Get the BluetoothDevice object from the Intent BluetoothDevice btDev = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); //mArrayAdapter.add(btDev.getName() + "\n" + btDev.getAddress()); Toast.makeText(getApplicationContext(), btDev.getName() + "\n" + btDev.getAddress(), Toast.LENGTH_LONG).show(); } } }; private void discoverBtDevices(){ // Register the BroadcastReceiver IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy mBluetoothAdapter.startDiscovery(); } private void scanBtDevices(){ Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices(); //[BC:85:1F:96:99:C9, 00:06:66:4C:75:FE] // If there are paired devices if (pairedDevices.size() > 0 ) { //ArrayAdapter mArrayAdapter = new ArrayAdapter(); // Loop through paired devices for (BluetoothDevice btDev : pairedDevices) { //mArrayAdapter.add(btDev.getName() + "\n" + btDev.getAddress()); Toast.makeText(getApplicationContext(), btDev.getName() + "\n" + btDev.getAddress(), Toast.LENGTH_LONG).show(); } } } @Override protected void onActivityResult( int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_ENABLE_BT) { if (resultCode == RESULT_OK){ Toast.makeText(getApplicationContext(), "Enabled Bluetooth now" , Toast.LENGTH_LONG).show(); scanOrDiscoverBtDevices(); } else { Toast.makeText(getApplicationContext(), "Not enable Bluetooth !" , Toast.LENGTH_LONG).show(); } } } |
6.继续折腾。
对于可以被搜索“Enabling discoverability”暂时就不去关心了。毕竟当前android设备能搜到HART猫即可。暂时无需考虑被搜索到。
7.再去建立连接,期间出现UUID和connect失败方面的问题:
【已解决】Android中连接蓝牙设备时遇到createRfcommSocketToServiceRecord的UUID问题和BluetoothSocket的connect失败
最终,用如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | private String mactekHartModemName; private UUID mactekHartModemUuid; //void afterFoundBtHartModem(BluetoothDevice btDev, Parcelable[] btDevUuid){ void afterFoundBtHartModem(BluetoothDevice btDev, UUID btDevUuid){ if ( null != btDevUuid){ } //mactekHartModemName = btDev.getName(); //"MACTekViator75FE" //mactekHartModemUuid = UUID.fromString(mactekHartModemName); String uuidValue; //uuidValue = "e214d9ae-c3ba-4e25-abb5-299041353bc3"; //https://groups.google.com/forum/#!topic/android-developers/vyTEJOXELos //uuidValue = "00001101-0000-1000-8000-00805F9B34FB"; //uuidValue = "00000003-0000-1000-8000-00805F9B34FB"; uuidValue = "00001101-0000-1000-8000-00805F9B34FB" ; mactekHartModemUuid = UUID.fromString(uuidValue); ConnectThread connectBtThread = new ConnectThread(btDev); connectBtThread.start(); } private class ConnectThread extends Thread { private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; public ConnectThread(BluetoothDevice device) { // Use a temporary object that is later assigned to mmSocket, // because mmSocket is final BluetoothSocket tmp = null ; mmDevice = device; // Get a BluetoothSocket to connect with the given BluetoothDevice try { // MY_UUID is the app's UUID string, also used by the server code tmp = device.createRfcommSocketToServiceRecord(mactekHartModemUuid); //00001101-0000-1000-8000-00805F9B34FB } catch (IOException e) { } mmSocket = tmp; } public void run() { // Cancel discovery because it will slow down the connection mBluetoothAdapter.cancelDiscovery(); try { // Connect the device through the socket. This will block // until it succeeds or throws an exception mmSocket.connect(); } catch (IOException connectException) { // Unable to connect; close the socket and get out try { mmSocket.close(); } catch (IOException closeException) { } return ; } // Do work to manage the connection (in a separate thread) manageConnectedSocket(mmSocket); } /** Will cancel an in-progress connection, and close the socket */ public void cancel() { try { mmSocket.close(); } catch (IOException e) { } } } |
可以正常触发android系统打开配对窗口,输入密码:
后,是可以配对成功的:
8.然后接着再去试试发送数据给蓝牙HART猫是否成功,能否收到返回的数据。
最后所用代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 | private final int REQUEST_ENABLE_BT = 1 ; private BluetoothAdapter mBluetoothAdapter; private void testBluetooth() { mBluetoothAdapter= BluetoothAdapter.getDefaultAdapter(); if (mBluetoothAdapter == null ) { // Device does not support Bluetooth Toast.makeText(getApplicationContext(), "Device does not support Bluetooth" , Toast.LENGTH_LONG).show(); } else { //android.bluetooth.BluetoothAdapter@211120b0 Toast.makeText(getApplicationContext(), "Device support Bluetooth" , Toast.LENGTH_SHORT).show(); if (!mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); } else { scanOrDiscoverBtDevices(); } } } private void scanOrDiscoverBtDevices(){ //scanBtDevices(); discoverBtDevices(); } // Create a BroadcastReceiver for ACTION_FOUND private final BroadcastReceiver mReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // When discovery finds a device if (BluetoothDevice.ACTION_FOUND.equals(action)) { // Get the BluetoothDevice object from the Intent BluetoothDevice btDev = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); //mArrayAdapter.add(btDev.getName() + "\n" + btDev.getAddress()); String btDevName = btDev.getName(); String btDevMacAddr = btDev.getAddress(); Toast.makeText(getApplicationContext(), btDevName + "\n" + btDevMacAddr, Toast.LENGTH_LONG).show(); if (btDevName.contains( "MACTekViator" )){ //MACTekViator75FE afterFoundBtHartModem(btDev); } } } }; private void discoverBtDevices(){ // Register the BroadcastReceiver IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy mBluetoothAdapter.startDiscovery(); } private void scanBtDevices(){ Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices(); //[BC:85:1F:96:99:C9, 00:06:66:4C:75:FE] // If there are paired devices if (pairedDevices.size() > 0 ) { // Loop through paired devices for (BluetoothDevice btDev : pairedDevices) { //mArrayAdapter.add(btDev.getName() + "\n" + btDev.getAddress()); //Toast.makeText(getApplicationContext(), btDev.getName() + "\n" + btDev.getAddress(), Toast.LENGTH_LONG).show(); String btDevName = btDev.getName(); String btDevMacAddr = btDev.getAddress(); Toast.makeText(getApplicationContext(), btDevName + "\n" + btDevMacAddr, Toast.LENGTH_LONG).show(); if (btDevName.contains( "MACTekViator" )){ //found our concerned Bluetooth HART Modem afterFoundBtHartModem(btDev); break ; } } } } @Override protected void onActivityResult( int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_ENABLE_BT) { if (resultCode == RESULT_OK){ Toast.makeText(getApplicationContext(), "Enabled Bluetooth now" , Toast.LENGTH_LONG).show(); scanOrDiscoverBtDevices(); } else { Toast.makeText(getApplicationContext(), "Not enable Bluetooth !" , Toast.LENGTH_LONG).show(); } } } private UUID mactekHartModemUuid; void afterFoundBtHartModem(BluetoothDevice btDev){ String sspUuid; //http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createRfcommSocketToServiceRecord%28java.util.UUID%29 //Hint: If you are connecting to a Bluetooth serial board then try using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. //However if you are connecting to an Android peer then please generate your own unique UUID. sspUuid = "00001101-0000-1000-8000-00805F9B34FB" ; mactekHartModemUuid = UUID.fromString(sspUuid); ConnectThread connectBtThread = new ConnectThread(btDev); connectBtThread.start(); } private class ConnectThread extends Thread { private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; public ConnectThread(BluetoothDevice device) { // Use a temporary object that is later assigned to mmSocket, // because mmSocket is final BluetoothSocket tmp = null ; mmDevice = device; // Get a BluetoothSocket to connect with the given BluetoothDevice try { // MY_UUID is the app's UUID string, also used by the server code tmp = device.createRfcommSocketToServiceRecord(mactekHartModemUuid); //00001101-0000-1000-8000-00805F9B34FB } catch (IOException e) { } mmSocket = tmp; } public void run() { // Cancel discovery because it will slow down the connection mBluetoothAdapter.cancelDiscovery(); try { // Connect the device through the socket. This will block // until it succeeds or throws an exception mmSocket.connect(); } catch (IOException connectException) { // Unable to connect; close the socket and get out try { mmSocket.close(); } catch (IOException closeException) { } return ; } // Do work to manage the connection (in a separate thread) manageConnectedSocket(mmSocket); } /** Will cancel an in-progress connection, and close the socket */ public void cancel() { try { mmSocket.close(); } catch (IOException e) { } } } private void manageConnectedSocket(BluetoothSocket socket){ //Toast.makeText(getApplicationContext(), "Bluetooth HART Modem Connected", Toast.LENGTH_LONG).show(); ConnectedThread connectedThread = new ConnectedThread(socket); connectedThread.start(); //command 0 String command0Str = "FFFFFFFFFF0280000082" ; byte [] commnd0Bytes = HexString2Bytes(command0Str); connectedThread.write(commnd0Bytes); //read out command 0 response byte [] readoutBuffer = new byte [ 1024 ]; //Note: here use DEBUG, so will take some time, so follow can read out real response data //if no DEBUG, just run through, will only get -1 //readoutBuffer = connectedThread.read(); //[-1, readoutBuffer = connectedThread.read(); //[-1, -1, -1, -1, 6, -128, 0, 14, 0, 69, -2, 54, 2, 5, 5, 2, 13, 1, 3, 43, -11, -82, 122, 0, 0, 0, 0, parseHartCommand0Resp(readoutBuffer); //readoutBuffer = connectedThread.read(); } private void parseHartCommand0Resp( byte [] command0RespBytes){ //-1, -1, -1, -1, 6, -128, 0, 14, 0, 69, -2, 54, 2, 5, 5, 2, 13, 1, 3, 43, -11, -82, 122, //FF FF FF FF 06 80 00 0E 00 45 FE 36 02 05 02 0D 01 03 2B F5 AE 7A //(hex value) ---> //FF FF FF FF 06 80 00 0E 00 45 //FE ==254(expansion) //ManufactureIdentificationCode=36==Yamatake //ManufactureDeviceType=02 //PreampleNumber=5 //UniversalCommandRevision=5==HART5 //DeviceSpecificCommandRevision=2 //SoftwareRevision=13 //HardwareRevision=1 //DeviceFunctionFlag=3 //DeviceIdNumber=2B F5 AE //CommonPracticeCommandRevision=7A } private boolean isReadoutRespOk = false ; private class ConnectedThread extends Thread { private final BluetoothSocket mmSocket; private final InputStream mmInStream; private final OutputStream mmOutStream; public ConnectedThread(BluetoothSocket socket) { mmSocket = socket; InputStream tmpIn = null ; OutputStream tmpOut = null ; // Get the input and output streams, using temp objects because // member streams are final try { tmpIn = socket.getInputStream(); tmpOut = socket.getOutputStream(); } catch (IOException e) { } mmInStream = tmpIn; mmOutStream = tmpOut; } public void run() { // byte[] buffer = new byte[1024]; // buffer store for the stream // int bytes; // bytes returned from read() // // // Keep listening to the InputStream until an exception occurs // while (true) { // try { // // Read from the InputStream // bytes = mmInStream.read(buffer); // // Send the obtained bytes to the UI activity // //mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget(); // if(bytes > 0){ // //Toast.makeText(getApplicationContext(), "Got HART Command 0 resp data: " + buffer.toString(), Toast.LENGTH_LONG).show(); // isReadoutRespOk = true; // // //-1, -1, -1, -1, 6, -128, 0, 14, 0, 69, -2, 54, 2, 5, 5, 2, 13, 1, 3, 43, -11, -82, 122, // //parseHartCommand0ResponseData(); // } // else{ // isReadoutRespOk = false; // } // // } catch (IOException e) { // break; // } // } } /* Call this from the main activity to send data to the remote device */ public void write(byte[] bytes) { try { mmOutStream.write(bytes); } catch (IOException e) { } } public byte[] read() { byte[] buffer = new byte[1024]; // buffer store for the stream int bytes; try { bytes = mmInStream.read(buffer); } catch (IOException e) { } return buffer; } /* Call this from the main activity to shutdown the connection */ public void cancel() { try { mmSocket.close(); } catch (IOException e) { } } } //copy from other code // string to hex array public byte [] HexString2Bytes(String hexStr) { if ( null == hexStr || 0 == hexStr.length()) { return null ; } byte [] ret = new byte [hexStr.length() / 2 ]; byte [] tmp = hexStr.getBytes(); for ( int i = 0 ; i < (tmp.length / 2 ); i++) { ret[i] = uniteBytes(tmp[i * 2 ], tmp[i * 2 + 1 ]); } return ret; } private byte uniteBytes( byte src0, byte src1) { byte _b0 = Byte.decode( "0x" + new String( new byte [] { src0 })) .byteValue(); _b0 = ( byte ) (_b0 << 4 ); byte _b1 = Byte.decode( "0x" + new String( new byte [] { src1 })) .byteValue(); byte result = ( byte ) (_b0 | _b1); return result; } |
1 | |
【总结】
至此,终于算是跑通了,可以通过:
平板上面的蓝牙,扫描找到蓝牙HART猫,然后通过HART猫发送command 0给HART设备,获得对应的信息:
注:
此处所用的HART设备和这里:
【记录】试用通过蓝牙操作HART设备的Android的程序:teknikol COMMANDER
是一样的:
所以上述代码解析后的信息,也是和之前一致的。
转载请注明:在路上 » 【记录】编写Android中的蓝牙模块驱动和底层HART设备