最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

【记录】编写Android中的蓝牙模块驱动和底层HART设备

Bluetooth crifan 4926浏览 0评论

【背景】

手上有个android设备:

现在希望可以在PAD上通过蓝牙去连接蓝牙的HART猫,然后再去操作HART的设备。

现在就是去android中写对应的蓝牙模块的程序,此处暂且叫做蓝牙驱动吧。

其中此处的开发环境是ADT。

【折腾过程】

1.参考:

Bluetooth | Android Developers

去添加权限:

add user permission android.permission.BLUETOOTH

添加好了:

added android.permission.BLUETOOTH and BLUETOOTH_ADMIN

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-100­0-8000-00805F9B34FB";
    //uuidValue = "00000003-0000-100­0-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系统打开配对窗口,输入密码:

pair with MACTekViator75FE bluetooth hart modem

后,是可以配对成功的:

use uuid 00001101-0000-1000-8000-00805F9B34FB then bluetooth connect will ok

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设备,获得对应的信息:

command 0 can response data bytes from mactek viator bluetooth hart modem

 

注:

此处所用的HART设备和这里:

【记录】试用通过蓝牙操作HART设备的Android的程序:teknikol COMMANDER

是一样的:

所以上述代码解析后的信息,也是和之前一致的。

转载请注明:在路上 » 【记录】编写Android中的蓝牙模块驱动和底层HART设备

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
83 queries in 0.194 seconds, using 22.20MB memory