之前两篇都是在说与手机的连接,连接方法,和主动配对连接,都是手机与手机的操作,做起来还是没问题的,但是最终的目的是与单片机的蓝牙模块的通信。
下面是到目前为止尝试的与单片机的通信方法,没有成功,但是从思路上来说没有问题,最大的问题是与单片机配对的时候,单片机的蓝牙模块的PIN配对码是写死的,固定为1234,
而手机这边连接配对都是自动生成的PIN配对码,这种方式在手机与手机配对的时候是极为方便的,但是在这里与单片机连接却成了最大的问题,因为手机自动生成而且每次都不一样,所以没法与单片机蓝牙模块的1234相同也就没法陪对了。下面只是介绍的到目前为止我们的大题思路,具体代码很多,而且涉及到项目也就没有贴。
如果关于上面的问题哪位同学有思路或者做过类似的项目还请指点。
首先,如何开启蓝牙设备和设置可见时间:
[java] view plaincopyprint?
1 private void search() {
2 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
3 if (!adapter.isEnabled()) {
4 adapter.enable();
5 }
6 Intent enable = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
7 enable.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 3600); //3600为蓝牙设备可见时间
8 startActivity(enable);
9 Intent searchIntent = new Intent(this, ComminuteActivity.class);
10 startActivity(searchIntent);
11 }
正式开始与蓝牙模块进行通信
[java] view plaincopyprint?
12 public class ComminuteActivity extends Activity {
13 private BluetoothReceiver receiver;
14 private BluetoothAdapter bluetoothAdapter;
15 private List<String> devices;
16 private List<BluetoothDevice> deviceList;
17 private Bluetooth client;
18 private final String lockName = "YESYOU";
19 private String message = "000001";
20 private ListView listView;
21
22 @Override
23 public void onCreate(Bundle savedInstanceState) {
24 super.onCreate(savedInstanceState);
25 setContentView(R.layout.search_layout);
26
27 listView = (ListView) this.findViewById(R.id.list);
28 deviceList = new ArrayList<BluetoothDevice>();
29 devices = new ArrayList<String>();
30 bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
31 bluetoothAdapter.startDiscovery();
32 IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
33 receiver = new BluetoothReceiver();
34 registerReceiver(receiver, filter);
35
36 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
37 @Override
38 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
39 setContentView(R.layout.connect_layout);
40 BluetoothDevice device = deviceList.get(position);
41 client = new Bluetooth(device, handler);
42 try {
43 client.connect(message);
44 } catch (Exception e) {
45 Log.e("TAG", e.toString());
46 }
47 }
48 });
49 }
50
51 @Override
52 protected void onDestroy() {
53 unregisterReceiver(receiver);
54 super.onDestroy();
55 }
56
57 private final Handler handler = new Handler() {
58 @Override
59 public void handleMessage(Message msg) {
60 switch (msg.what) {
61 case Bluetooth.CONNECT_FAILED:
62 Toast.makeText(ComminuteActivity.this, "连接失败", Toast.LENGTH_LONG).show();
63 try {
64 client.connect(message);
65 } catch (Exception e) {
66 Log.e("TAG", e.toString());
67 }
68 break;
69 case Bluetooth.CONNECT_SUCCESS:
70 Toast.makeText(ComminuteActivity.this, "连接成功", Toast.LENGTH_LONG).show();
71 break;
72 case Bluetooth.READ_FAILED:
73 Toast.makeText(ComminuteActivity.this, "读取失败", Toast.LENGTH_LONG).show();
74 break;
75 case Bluetooth.WRITE_FAILED:
76 Toast.makeText(ComminuteActivity.this, "写入失败", Toast.LENGTH_LONG).show();
77 break;
78 case Bluetooth.DATA:
79 Toast.makeText(ComminuteActivity.this, msg.arg1 + "", Toast.LENGTH_LONG).show();
80 break;
81 }
82 }
83 };
84
85 private class BluetoothReceiver extends BroadcastReceiver {
86 @Override
87 public void onReceive(Context context, Intent intent) {
88 String action = intent.getAction();
89 if (BluetoothDevice.ACTION_FOUND.equals(action)) {
90 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
91 if (isLock(device)) {
92 devices.add(device.getName());
93 }
94 deviceList.add(device);
95 }
96 showDevices();
97 }
98 }
99
100 private boolean isLock(BluetoothDevice device) {
101 boolean isLockName = (device.getName()).equals(lockName);
102 boolean isSingleDevice = devices.indexOf(device.getName()) == -1;
103 return isLockName && isSingleDevice;
104 }
105
106 private void showDevices() {
107 ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
108 devices);
109 listView.setAdapter(adapter);
110 }
111 }
这里需要提一下的是,startDiscovery()这个方法和它的返回值,它是一个异步方法,会对其他蓝牙设备进行搜索,持续时间为12秒。
搜索过程其实是在System Service中进行,我们可以通过cancelDiscovery()方法来停止这个搜索。在系统搜索蓝牙设备的过程中,系统可能会发送以下三个广播:ACTION_DISCOVERY_START(开始搜索),
ACTION_DISCOVERY_FINISHED(搜索结束)
和ACTION_FOUND(找到设备)。
ACTION_FOUND这个才是我们想要的,这个Intent中包含两个extra fields: EXTRA_DEVICE和EXTRA_CLASS,
包含的分别是BluetoothDevice和BluetoothClass,
EXTRA_DEVICE中的BluetoothDevice就是我们搜索到的设备对象,从中获得设备的名称和地址。
而EXTRA_CLASS中的BluetoothClass是搜索到的设备的类型,比如搜索到的是手机还是耳机或者其他,之后我会写一篇关于它的介绍。
在这个上面我现在在想,是否通过判断搜索到的设备类型来识别单片机蓝牙模块与手机蓝牙的不同,采取不一样的配对方式,从而不自动生成配对码。不知是否可行,一会尝试。
搜索到该设备后,我们就要对该设备进行连接和通信。
[java] view plaincopyprint?
112 public void connect(final String message) {
113 Thread thread = new Thread(new Runnable() {
114 public void run() {
115 BluetoothSocket tmp = null;
116 Method method;
117 try {
118 method = device.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
119 tmp = (BluetoothSocket) method.invoke(device, 1);
120 } catch (Exception e) {
121 setState(CONNECT_FAILED);
122 Log.e("TAG", e.toString());
123 }
124 socket = tmp;
125 try {
126 socket.connect();
127 isConnect = true;
128 } catch (Exception e) {
129 setState(CONNECT_FAILED);
130 Log.e("TAG", e.toString());
131 }
132 if (isConnect) {
133 try {
134 OutputStream outStream = socket.getOutputStream();
135 outStream.write(getHexBytes(message));
136 } catch (IOException e) {
137 setState(WRITE_FAILED);
138 Log.e("TAG", e.toString());
139 }
140 try {
141 InputStream inputStream = socket.getInputStream();
142 int data;
143 while (true) {
144 try {
145 data = inputStream.read();
146 Message msg = handler.obtainMessage();
147 msg.what = DATA;
148 msg.arg1 = data;
149 handler.sendMessage(msg);
150 } catch (IOException e) {
151 setState(READ_FAILED);
152 Log.e("TAG", e.toString());
153 break;
154 }
155 }
156 } catch (IOException e) {
157 setState(WRITE_FAILED);
158 Log.e("TAG", e.toString());
159 }
160 }
161
162 if (socket != null) {
163 try {
164 socket.close();
165 } catch (IOException e) {
166 Log.e("TAG", e.toString());
167 }
168 }
169 }
170 }
这里包括写入和读取,用法和基本的Socket是一样的,但是写入的时候,需要将字符串转化为16进制:
[java] view plaincopyprint?
171 private byte[] getHexBytes(String message) {
172 int len = message.length() / 2;
173 char[] chars = message.toCharArray();
174 String[] hexStr = new String[len];
175 byte[] bytes = new byte[len];
176 for (int i = 0, j = 0; j < len; i += 2, j++) {
177 hexStr[j] = "" + chars[i] + chars[i + 1];
178 bytes[j] = (byte) Integer.parseInt(hexStr[j], 16);
179 }
180 return bytes;
181 }
连接设备之前需要UUID,所谓的UUID,就是用来进行配对的,全称是Universally Unique Identifier,是一个128位的字符串ID,用于进行唯一标识。网上的例子,包括谷歌的例子提供的uuid,通用的"00001101-0000-1000-8000-00805F9B34FB"也试过了,在配对的时候都是自动生成了配对码,也无法正常与单片机的蓝牙模块连接,所以,我就利用反射的原理,让设备自己提供UUID尝试。到这里其实我有点怀疑自己对于UUID的理解是否正确了。
在谷歌提供的例子中,我们可以看到谷歌的程序员的程序水平很高,一些好的编码习惯我们可以学习一下,像是在try..catch中才定义的变量,我们应该在try...catch之前声明一个临时变量,然后再在try...catch后赋值给我们真正要使用的变量。这种做法的好处就是:如果我们直接就是使用真正的变量,当出现异常的时候,该变量的使用就会出现问题,而且很难进行排查,如果是临时变量,我么可以通过检查变量的值来确定是否是赋值时出错。