前言:若你你一个电子爱好者,若你酷爱编程,那我分享一个Android BLE application的开发方法,让你可以使用手机和单片机结合,使用手机控制单片机,做一些更有趣的东西,下面开始讲解如何开发一个Android BLE application,参考谷歌官方教程: https://developer.android.com/guide/topics/connectivity/bluetooth-le.html
本文为封装之后的教程,可以更方便的更快速的搭建你的Android BLE application.当然我会把我的源码上传分享给大家。也可以参考官方提供的例程sdk/samples/android-18/legacy/BluetoothLeGatt
我的源码下载:http://download.csdn.net/detail/a353183177/8187165
1. 新建安卓工程
打开Eclipse,File->New->Android Application Project,在Application Name编辑框内填入Application名字,如:BleExample,Minimum Required SDK选择API18:Android 4.3,Target SDK也选择API18:Android 4.3,因为buletooth 4.0必须要Android 4.3及以上版本才能使用,其他默认不变,一直点Next按钮,直到出现Finish按钮,然后点Finish按钮。
2. 添加权限和服务
在manifest file文件中添加:
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
<service android:name="com.elecfreaks.ble.BluetoothLeService" android:enabled="true"/>
3. 新建listView item布局文件
用于显示ListView中每一项的内容,我们这里使用自定义,这样可以让ListView中一项显示更多内容,item_list.xml内容如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<TextView android:id="@+id/textViewDevName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24dp"/>
<TextView android:id="@+id/textViewDevAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12dp"/>
</LinearLayout>
Copy我提供的源码BleExampl中com.elecfreaks.ble包到你的工程src目录里面,然后打开有错误提示的文件,按下shift+ctrl+O键。
4. 修改activity_main.xml增加scanButton按钮和bleDeviceListView
增加内容如下:
<Button
android:id="@+id/scanButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:onClick="scanOnClick"
android:text="scan" />
<ListView
android:id="@+id/bleDeviceListView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/scanButton"
android:layout_below="@+id/scanButton"
android:layout_above="@+id/sendButton"
>
</ListView>
5. 在MainActivity.java中添加scanButton响应事件方法(onClick="scanOnClick")
public void scanOnClick(final View v){
}
6. 为MainActivity类添加成员
private Button scanButton;
private ListView bleDeviceListView;
private BLEDeviceListAdapter listViewAdapter;
private BluetoothHandler bluetoothHandler;
private boolean isConnected;
7. 在MainActivity.onCreate中设置成员的值
scanButton = (Button) findViewById(R.id.scanButton);
bleDeviceListView = (ListView) findViewById(R.id.bleDeviceListView);
listViewAdapter = new BLEDeviceListAdapter(this);
bluetoothHandler = new BluetoothHandler(this);
bluetoothHandler.setOnConnectedListener(new OnConnectedListener() {
@Override
public void onConnected(boolean isConnected) {
// TODO Auto-generated method stub
setConnectStatus(isConnected);
}
});
bluetoothHandler.setOnRecievedDataListener(new OnRecievedDataListener() {
@Override
public void onRecievedData(byte[] bytes) {
// TODO Auto-generated method stub
System.out.printf("REC:");
for(byte b:bytes)
System.out.printf("%02X ", b);
System.out.printf("\n");
}
});
8. 添加setConnectStatus方法
public void setConnectStatus(boolean isConnected){
this.isConnected = isConnected;
if(isConnected){
showMessage("Connection successful");
scanButton.setText("break");
}else{
bluetoothHandler.onPause();
bluetoothHandler.onDestroy();
scanButton.setText("scan");
}
}
private void showMessage(String str){
Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
}
9. 在scanOnClick中添加内容
if(!isConnected){ bleDeviceListView.setAdapter(bluetoothHandler.getDeviceListAdapter());
bleDeviceListView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
String buttonText = (String) ((Button)v).getText();
if(buttonText.equals("scanning")){
showMessage("scanning...");
return ;
}
BluetoothDevice device = bluetoothHandler.getDeviceListAdapter().getItem(position).device;
// connect
bluetoothHandler.connect(device.getAddress());
}
});
bluetoothHandler.setOnScanListener(new OnScanListener() {
@Override
public void onScanFinished() {
// TODO Auto-generated method stub
((Button)v).setText("scan");
((Button)v).setEnabled(true);
}
@Override
public void onScan(BluetoothDevice device, int rssi, byte[] scanRecord) {}
});
((Button)v).setText("scanning");
((Button)v).setEnabled(false);
bluetoothHandler.scanLeDevice(true);
}else{
setConnectStatus(false);
}
10. 发送数据
byte[] data = new byte[1];
data[0] = 0x02;
bluetoothHandler.sendData(data);
11. 接收数据
接收到数据时会调用bluetoothHandler.setOnRecievedDataListener()方法中设置的OnRecievedDataListener.onRecievedData(byte[] bytes)方法,其中bytes为接收到的数据
12. 通过协议发送数据给单片机
在src目录新建Transmitter.java,添加带两个参数的构造函数,如下:
public Transmitter(Context context, BluetoothHandler bluetoothHandler){
this.context = context;
this.mBluetoothHandler = bluetoothHandler;
}
添加sendData()方法:
private void sendData(byte[] bytes){
mBluetoothHandler.sendData(bytes);
}
添加sendPackage()方法:
public void sendPakege(byte[] data, byte cmd){
byte[] bytes = new byte[data.length+6];
bytes[0] = (byte) 0xFA; // 数据头1
bytes[1] = (byte) 0xFB; // 数据头2
bytes[2] = (byte) 0xFC; // 数据头3
bytes[3] = (byte) data.length; // 数据长度
bytes[4] = cmd; // 指令
System.arraycopy(data, 0, bytes, 5, data.length);
for(int i=0; i<data.length; i++){
bytes[data.length+5] ^= data[i]; // 校验和
}
sendData(bytes);
}
13. 通过协议接收单片机数据
在src目录新建MyArray.java,用于连接两个数组,代码如下:
public class MyArray {
static public byte[] arrayCat(byte[] buf1,byte[] buf2){
byte[] bufret=null;
int len1 = 0;
int len2 = 0;
if(buf1 != null)
len1 = buf1.length;
if(buf2 != null)
len2 = buf2.length;
if(len1+len2 > 0)
bufret = new byte[len1+len2];
if(len1 > 0)
System.arraycopy(buf1, 0, bufret, 0, len1);
if(len2 > 0)
System.arraycopy(buf2, 0, bufret, len1, len2);
return bufret;
}
}
Copy我提供例程中的protocol.java到src目录
添加成员private Protocol protocol;
在onCreate()中删除bluetoothHandler.setOnRecievedDataListener();
添加:
protocol = new Protocol(this, new Transmitter(this, bluetoothHandler));
protocol.setOnReceivedDataListener(recListener);
在MainActivity中添加成员:
private static final boolean INPUT = false;
private static final boolean OUTPUT = true;
private static final boolean LOW = false;
private static final boolean HIGH = true;
private boolean digitalVal[];
private int analogVal[];
并且在onCreate中初始化:
digitalVal = new boolean[14];
analogVal = new int[14];
private OnReceivedRightDataListener recListener = new OnReceivedRightDataListener() {
@Override
public int onReceivedData(byte[] bytes) {
// TODO Auto-generated method stub
/*System.out.print("REC:");
for(byte b:bytes)
System.out.printf("%02X ", b);
System.out.println("");*/
byte pin;
int pinValue;
switch(bytes[4]){
case Transmitter.READ_DATA:
if(bytes[Protocol.MODE_INDEX+5] == Protocol.DIGITAL){
pin = bytes[Protocol.PIN_INDEX+5];
pinValue = (short) (bytes[Protocol.PINVALL_INDEX+5]);
if(pinValue > 0){
digitalVal[pin] = HIGH;
}else{
digitalVal[pin] = LOW;
}
}else if(bytes[Protocol.MODE_INDEX+5] == Protocol.ANALOG){
pin = bytes[Protocol.PIN_INDEX+5];
int high = bytes[Protocol.PINVALH_INDEX+5] & 0xff;
int low = bytes[Protocol.PINVALL_INDEX+5] & 0xff;
pinValue = high<<8 + low;
System.out.println("low="+high+" high="+low+" val="+pinValue);
analogVal[pin] = pinValue;
}
break;
case Transmitter.WRITE_DATA:
break;
default:break;
}
return 0;
}
};
14. 使用协议发送数据
protocol.writeAnalogData(9, 20);
protocol.writeDigitalData(3, 1);
15. 使用协议接收数据
protocol.readAnalogDataCommand(9);
protocol.readDigitalDataCommand(3);
注意: 返回的数据由recListener 接收
16. 单片机端协议(arduino)
参考我提供的示例源码AndroidIOControl