之前兩篇都是在說與手機的連接,連接方法,和主動配對連接,都是手機與手機的操作,做起來還是沒問題的,但是最終的目的是與單片機的藍牙模塊的通信。
下面是到目前為止嘗試的與單片機的通信方法,沒有成功,但是從思路上來說沒有問題,最大的問題是與單片機配對的時候,單片機的藍牙模塊的PIN配對碼是寫死的,固定為1234, 而手機這邊連接配對都是自動生成的PIN配對碼,這種方式在手機與手機配對的時候是極為方便的,但是在這里與單片機連接卻成了最大的問題,因為手機自動生成而且每次都不一樣,所以沒法與單片機藍牙模塊的1234相同也就沒法陪對了。下面只是介紹的到目前為止我們的大題思路,具體代碼很多,而且涉及到項目也就沒有貼。 如果關于上面的問題哪位同學有思路或者做過類似的項目還請指點。
首先,如何開啟藍牙設備和設置可見時間: private void search() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (!adapter.isEnabled()) { adapter.enable(); } Intent enable = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); enable.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 3600); //3600為藍牙設備可見時間 startActivity(enable); Intent searchIntent = new Intent(this, ComminuteActivity.class); startActivity(searchIntent); }
public class ComminuteActivity extends Activity { private BluetoothReceiver receiver; private BluetoothAdapter bluetoothAdapter; private List<String> devices; private List<BluetoothDevice> deviceList; private Bluetooth client; private final String lockName = 'YESYOU'; private String message = '000001'; private ListView listView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.search_layout); listView = (ListView) this.findViewById(R.id.list); deviceList = new ArrayList<BluetoothDevice>(); devices = new ArrayList<String>(); bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); bluetoothAdapter.startDiscovery(); IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); receiver = new BluetoothReceiver(); registerReceiver(receiver, filter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { setContentView(R.layout.connect_layout); BluetoothDevice device = deviceList.get(position); client = new Bluetooth(device, handler); try { client.connect(message); } catch (Exception e) { Log.e('TAG', e.toString()); } } }); } @Override protected void onDestroy() { unregisterReceiver(receiver); super.onDestroy(); } private final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case Bluetooth.CONNECT_FAILED: Toast.makeText(ComminuteActivity.this, '連接失敗', Toast.LENGTH_LONG).show(); try { client.connect(message); } catch (Exception e) { Log.e('TAG', e.toString()); } break; case Bluetooth.CONNECT_SUCCESS: Toast.makeText(ComminuteActivity.this, '連接成功', Toast.LENGTH_LONG).show(); break; case Bluetooth.READ_FAILED: Toast.makeText(ComminuteActivity.this, '讀取失敗', Toast.LENGTH_LONG).show(); break; case Bluetooth.WRITE_FAILED: Toast.makeText(ComminuteActivity.this, '寫入失敗', Toast.LENGTH_LONG).show(); break; case Bluetooth.DATA: Toast.makeText(ComminuteActivity.this, msg.arg1 + '', Toast.LENGTH_LONG).show(); break; } } }; private class BluetoothReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (isLock(device)) { devices.add(device.getName()); } deviceList.add(device); } showDevices(); } } private boolean isLock(BluetoothDevice device) { boolean isLockName = (device.getName()).equals(lockName); boolean isSingleDevice = devices.indexOf(device.getName()) == -1; return isLockName && isSingleDevice; } private void showDevices() { ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, devices); listView.setAdapter(adapter); }} 這里需要提一下的是,startDiscovery()這個方法和它的返回值,它是一個異步方法,會對其他藍牙設備進行搜索,持續(xù)時間為12秒。 搜索過程其實是在System Service中進行,我們可以通過cancelDiscovery()方法來停止這個搜索。在系統(tǒng)搜索藍牙設備的過程中,系統(tǒng)可能會發(fā)送以下三個廣播: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是搜索到的設備的類型,比如搜索到的是手機還是耳機或者其他,之后我會寫一篇關于它的介紹。 在這個上面我現(xiàn)在在想,是否通過判斷搜索到的設備類型來識別單片機藍牙模塊與手機藍牙的不同,采取不一樣的配對方式,從而不自動生成配對碼。不知是否可行,一會嘗試。
搜索到該設備后,我們就要對該設備進行連接和通信。 public void connect(final String message) { Thread thread = new Thread(new Runnable() { public void run() { BluetoothSocket tmp = null; Method method; try { method = device.getClass().getMethod('createRfcommSocket', new Class[]{int.class}); tmp = (BluetoothSocket) method.invoke(device, 1); } catch (Exception e) { setState(CONNECT_FAILED); Log.e('TAG', e.toString()); } socket = tmp; try { socket.connect(); isConnect = true; } catch (Exception e) { setState(CONNECT_FAILED); Log.e('TAG', e.toString()); } if (isConnect) { try { OutputStream outStream = socket.getOutputStream(); outStream.write(getHexBytes(message)); } catch (IOException e) { setState(WRITE_FAILED); Log.e('TAG', e.toString()); } try { InputStream inputStream = socket.getInputStream(); int data; while (true) { try { data = inputStream.read(); Message msg = handler.obtainMessage(); msg.what = DATA; msg.arg1 = data; handler.sendMessage(msg); } catch (IOException e) { setState(READ_FAILED); Log.e('TAG', e.toString()); break; } } } catch (IOException e) { setState(WRITE_FAILED); Log.e('TAG', e.toString()); } } if (socket != null) { try { socket.close(); } catch (IOException e) { Log.e('TAG', e.toString()); } } }} 這里包括寫入和讀取,用法和基本的Socket是一樣的,但是寫入的時候,需要將字符串轉化為16進制: private byte[] getHexBytes(String message) { int len = message.length() / 2; char[] chars = message.toCharArray(); String[] hexStr = new String[len]; byte[] bytes = new byte[len]; for (int i = 0, j = 0; j < len; i += 2, j++) { hexStr[j] = '' + chars[i] + chars[i + 1]; bytes[j] = (byte) Integer.parseInt(hexStr[j], 16); } return bytes; }
連接設備之前需要UUID,所謂的UUID,就是用來進行配對的,全稱是Universally Unique Identifier,是一個128位的字符串ID,用于進行唯一標識。網上的例子,包括谷歌的例子提供的uuid,通用的'00001101-0000-1000-8000-00805F9B34FB'也試過了,在配對的時候都是自動生成了配對碼,也無法正常與單片機的藍牙模塊連接,所以,我就利用反射的原理,讓設備自己提供UUID嘗試。到這里其實我有點懷疑自己對于UUID的理解是否正確了。 在谷歌提供的例子中,我們可以看到谷歌的程序員的程序水平很高,一些好的編碼習慣我們可以學習一下,像是在try..catch中才定義的變量,我們應該在try...catch之前聲明一個臨時變量,然后再在try...catch后賦值給我們真正要使用的變量。這種做法的好處就是:如果我們直接就是使用真正的變量,當出現(xiàn)異常的時候,該變量的使用就會出現(xiàn)問題,而且很難進行排查,如果是臨時變量,我么可以通過檢查變量的值來確定是否是賦值時出錯。
作者:jason0539 微博:http://weibo.com/2553717707 博客:http://blog.csdn.net/jason0539(轉載請說明出處) |
|