【背景】
手上有个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去检测是否支持蓝牙,且支持的话去打开:
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搜寻当前附近的蓝牙设备。
代码如下:
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失败
最终,用如下代码:
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; //http://www.guidgenerator.com/online-guid-generator.aspx //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猫是否成功,能否收到返回的数据。
最后所用代码为:
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; }
【总结】
至此,终于算是跑通了,可以通过:
平板上面的蓝牙,扫描找到蓝牙HART猫,然后通过HART猫发送command 0给HART设备,获得对应的信息:
注:
此处所用的HART设备和这里:
【记录】试用通过蓝牙操作HART设备的Android的程序:teknikol COMMANDER
是一样的:
所以上述代码解析后的信息,也是和之前一致的。
转载请注明:在路上 » 【记录】编写Android中的蓝牙模块驱动和底层HART设备