【背景】
手上有个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 areSTATE_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
是一样的:
所以上述代码解析后的信息,也是和之前一致的。