1、概述
1.1 背景
本文基于《Android创建Service后台常驻服务并使用Broadcast通信》扩展蓝牙通信的功能,使用手机作为Client,HC06芯片作Service进行通信。
1.2 需求
1)手动打开手机的蓝牙开关,打开app,自动连接HC06
2)按键1、2、3、4,通过HC06向下位机发送命令1、2、3、4
3)下位机反馈信息通过Toast回显到屏幕
2、知识点
2.1 HC06
HC06是主从一体化的蓝牙串口模块,主从可指令切换,AT指令少于HC-05,使用简单。
如下图设备管理器所示,连接后相当于串口来使用即可,同时Arduino编程上与普通Serial串口类编程方法一致。
2.2 Android蓝牙开发
Android 平台支持蓝牙网络协议栈,实现蓝牙设备之间数据的无线传输,以下是建立蓝牙连接所需要的基本类[1]:
1)BluetoothAdapter 类:代表了一个本地的蓝牙适配器。发现其他蓝牙设备,查询绑定了的设备,
使用已知的 MAC 地址实例化一个蓝牙设备和建立一个BluetoothServerSocket(作为服务器端)来监听来自其他设备的连接。
2)BluetoothDevice 类:代表了一个远端的蓝牙设备,使用它请求远端蓝牙设备连接或者获取 远端蓝牙设备的名称、地址、种类和绑定状态。
3)Bluetoothsocket 类:代表了一个蓝牙套接字的接口(类似于 tcp 中的套接字),他是应用程 序通过输入、输出流与其他蓝牙设备通信的连接点。
4)Blueboothserversocket 类:代表打开服务连接来监听可能到来的连接请求(属于 server 端), 为了连接两个蓝牙设备必须有一个设备作为服务器打开一个服务套接字。
5)Bluetoothclass 类:描述了一个蓝牙设备的一般特点和能力。他的只读属性集定义了设备 的主、次设备类和一些相关服务。
3、实现
3.1 HC06与Arduino连接
主要引脚接法:
1)VCC:接Arduino的5V。
2)GND:接Arduino的GND。
3)TXD:发送端,一般表示为自己的发送端,接Arduino的RX。
4)RXD:接收端,一般表示为自己的接收端,接Arduino的TX。
线接好后,把Arduino上电后,蓝牙的指示灯是闪烁的,表明没有设备连接上(建立 RFCOMM 信道),注意一下匹配和连接的区别:
1)匹配:只是说明对方设备发现了你的存在,并拥 有一个共同的识别码,并且可以连接。
2)连接上:表示当前设备共享一个 RFCOMM 信道并且两者之间可以交换数据。
也就是是说蓝牙设备在建立 RFCOMM 信道之前,必须是已经配对好了的。
以下给出一个简单的串口控制IO口的简单例子,IO口控制可以做LED操作,或者衍生到电机驱动L298N的控制。
在循环体中,间隔每30ms读取一个命令位,做相应的动作。
void setup()
{
Serial.begin(9600);
pinMode(L1, OUTPUT);
pinMode(L2, OUTPUT);
pinMode(R1, OUTPUT);
pinMode(R2, OUTPUT);
Serial.println(F("L298N Program start..."));
}
void loop()
{
int value = 0;
if ( Serial.available() )
{
value = Serial.read();
}
else
{
value = 'x';
}
if ( value == '0' )
{
go_stop();
Serial.println(F("0:stop"));
}
else if ( value == '1' )
{
go_up();
Serial.println(F("1:up"));
}
else if ( value == '2' )
{
go_down();
Serial.println(F("2:down"));
}
else if ( value == '3' )
{
go_left();
Serial.println(F("3:left"));
}
else if ( value == '4' )
{
go_right();
Serial.println(F("4:right"));
}
delay(30);
}
3.2 Android蓝牙编程
从需求1上进行设计,即进入App之前先打开蓝牙,并匹配过HC06设备。
将蓝牙操作的动作设计在Service类中处理,使用双进程操作。
定义变量,其中UUID为固定的值
private static final UUID HC_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private BluetoothAdapter mBtAdapter = null;
private BluetoothSocket mBtSocket = null;
private OutputStream outStream = null;
private InputStream inStream = null;
private boolean mBtFlag = true;
获取蓝牙适配器方法,由于寻找设备连接设备为耗时动作,放在线程中处理
/**
* Called by onStartCommand, initialize and start runtime thread
*/
private void myStartService() {
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
if ( mBtAdapter == null ) {
showToast("Bluetooth unused.");
mBtFlag = false;
return;
}
if ( !mBtAdapter.isEnabled() ) {
mBtFlag = false;
myStopService();
showToast("Open bluetoooth then restart program!!");
return;
}
showToast("Start searching!!");
threadFlag = true;
mThread = new MyThread();
mThread.start();
}
子线程主要做两件事:连接蓝牙设备和读蓝牙信息
/**
* Thread runtime
*/
public class MyThread extends Thread {
@Override
public void run() {
super.run();
myBtConnect();
while( threadFlag ) {
readSerial();
try{
Thread.sleep(30);
}catch(Exception e){
e.printStackTrace();
}
}
}
}
看看蓝牙的连接,其中BluetoothDevice 的获取有两种方法,一种是已知HC06的MAC地址直接连接,文中使用的另一种方式:通过查找已匹配的设备来获取BluetoothDevice。
获取Device后建立RFcomm信道,然后打开IOStream就可以进行读写[2]。
/**
* device control
*/
public void myBtConnect() {
showToast("Connecting...");
/* Discovery device */
// BluetoothDevice mBtDevice = mBtAdapter.getRemoteDevice(HC_MAC);
BluetoothDevice mBtDevice = null;
Set<BluetoothDevice> mBtDevices = mBtAdapter.getBondedDevices();
if ( mBtDevices.size() > 0 ) {
for ( Iterator<BluetoothDevice> iterator = mBtDevices.iterator();
iterator.hasNext(); ) {
mBtDevice = (BluetoothDevice)iterator.next();
showToast(mBtDevice.getName() + "|" + mBtDevice.getAddress());
}
}
try {
mBtSocket = mBtDevice.createRfcommSocketToServiceRecord(HC_UUID);
} catch (IOException e) {
e.printStackTrace();
mBtFlag = false;
showToast("Create bluetooth socket error");
}
mBtAdapter.cancelDiscovery();
/* Setup connection */
try {
mBtSocket.connect();
showToast("Connect bluetooth success");
Log.i(TAG, "Connect " + HC_MAC + " Success!");
} catch (IOException e) {
e.printStackTrace();
try {
showToast("Connect error, close");
mBtSocket.close();
mBtFlag = false;
} catch (IOException e1) {
e1.printStackTrace();
}
}
/* I/O initialize */
if ( mBtFlag ) {
try {
inStream = mBtSocket.getInputStream();
outStream = mBtSocket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
showToast("Bluetooth is ready!");
}
读串口放置于子线程中处理。
/**
* Read serial data from HC06
*/
public int readSerial() {
int ret = 0;
byte[] rsp = null;
if ( !mBtFlag ) {
return -1;
}
try {
rsp = new byte[inStream.available()];
ret = inStream.read(rsp);
showToast(new String(rsp));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return ret;
}
/**
* Write serial data to HC06
* @param value - command
*/
public void writeSerial(int value) {
String ha = "" + value;
try {
outStream.write(ha.getBytes());
outStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
4、结论
本文与《 Android创建Service后台常驻服务并使用Broadcast通信》共同整理总结了Android与Arduino的蓝牙通信方法,为后续蓝牙遥控小车做下基础。
后续将制作一个自定义摇杆来进行方向的控制,到时再将工程放出来,谢谢大家支持!
参考文章:
[1] Android蓝牙开发指南
[2] 一篇比较详细的蓝牙编程实例, http://www.2cto.com/kf/201312/265157.html