刚刚做了一个关于BLE蓝牙通信的Android应,现在回头看看这个过程,想把这些代码分享给那些需要的同学,由于第一次写,很多地方不懂,写的不好,多多包涵。
开始正题,关于BLE蓝牙,有不少博客详细介绍了,在关联的URL处添加了一些可以参考,个人觉得最重要的一部分是关于其UUID部分,service和characteristic都有一个唯一的UUID来识别,我们需要知道蓝牙模块发送数据对应的UUID值,而接收或者发送的数据通过characteristic实现,不同的characteristic具有不同的属性(可读,可写,通知),而我们的characteristic包含在service里,即我们需要知道service和characteristic对应的UUID。
BLE_SERVICE和BLE_SERVICE_READ,其他的UUID包括一些产品信息,以及配置的UUID。
1.BleDefinedUUIDs.java文件
class BleDefinedUUIDs {
//Service对应的UUID
static class Service {
final static UUID BLE_SERVICE=UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb");
};
static class Characteristic {
//characteristic的UUID
// final static public UUID HEART_RATE_MEASUREMENT = UUID.fromString("00002a37-0000-1000-8000-00805f9b34fb");
final static UUID BLE_SERVICE_READ = UUID.fromString("0000fff4-0000-1000-8000-00805f9b34fb");
final static public UUID MANUFACTURER_STRING = UUID.fromString("00002a29-0000-1000-8000-00805f9b34fb");
final static public UUID MODEL_NUMBER_STRING = UUID.fromString("00002a24-0000-1000-8000-00805f9b34fb");
final static public UUID FIRMWARE_REVISION_STRING = UUID.fromString("00002a26-0000-1000-8000-00805f9b34fb");
final static public UUID APPEARANCE = UUID.fromString("00002a01-0000-1000-8000-00805f9b34fb");
final static public UUID BODY_SENSOR_LOCATION = UUID.fromString("00002a38-0000-1000-8000-00805f9b34fb");
}
static class Descriptor {
final static UUID CHAR_CLIENT_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
}
}2.MainActivity.java文件
2.1判断设备是否支持蓝牙以及连接蓝牙(BLE蓝牙不同于手机系统配置的蓝牙,不需要输入密码)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**
* 判断当前设备是否支持ble
*/
if (!getPackageManager().hasSystemFeature(
PackageManager.FEATURE_BLUETOOTH_LE)) {
System.out.println("不支持BLE设备");
}
//得到蓝牙适配器
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, REQUEST_ENABLE_BT);
}
Button start = (Button) findViewById(R.id.start);
lv = (ListView) findViewById();
final TextView state = (TextView) findViewById(R.id.state);
adapter = new LeDeviceListAdapter();
start.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
scanLeDevice(true);
lv.setAdapter(adapter);
adapter.clear();
}
});
lv.setOnItemClickListener(new OnItemClickListener() {
private BluetoothGatt gatt;
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
//把搜索到的设备信息放到listview中,然后连接设备
BluetoothDevice device = adapter.getDevice(position);
state.setText("已连接");
gatt = device.connectGatt(getApplicationContext(), false,
mGattCallback);
}
});
}2.2获得对应的服务以及characteristic
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// System.out.println("Services discovered");
//得到数据信息的service
BluetoothGattService service = gatt
.getService(BLE_SERVICE);
if(service!=null){
mCharacteristic=service.getCharacteristic(BLE_SERVICE_READ);
boolean success=gatt.setCharacteristicNotification(mCharacteristic,true);
if (!success) {
Toast.makeText(MainActivity.this, "数据通知出错", Toast.LENGTH_LONG).show();
}
BluetoothGattDescriptor descriptor = mCharacteristic.getDescriptor(BleDefinedUUIDs.Descriptor.CHAR_CLIENT_CONFIG);
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
}
}
}2.3当characteristic的值发生变化时,能够及时获得变化后的值,接收到的数据根据对应的通信协议来实现数据转换。本次接收的为12位16进制数据,通过实验发现其中包含的电压单位信息以及万用表显示的数据信息,通过将接收到的数据变为字符串,对字符串的截取处理十分方便,然后获得其变为float型以实现数据的真实数值。
/**
* 当service里边的characteristic发生改变调用
*/
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
if(characteristic.equals(mCharacteristic))
{
final byte[] raw = mCharacteristic.getValue();
final String data=bytesToHexString(raw);
final int sign=Integer.parseInt(data.substring(10,11));//判断符号位是否为负,若为负则将其设为0
final int dot = Integer.parseInt(data.substring(1, 2));
final int unit=Integer.parseInt(data.substring(0,1));
runOnUiThread(new Runnable() {
@Override
public void run() {
//决定单位为V还是mV
switch(unit){
case 1:
TextView textView=(TextView)findViewById(R.id.unit);
textView.setText("mV");break;
case 2:
TextView textView_1=(TextView)findViewById(R.id.unit);
textView_1.setText("V");break;
default: Toast.makeText(MainActivity.this,"暂没开放此档位相关功能",Toast.LENGTH_SHORT).show();break;
}
//数据显示
if(sign<8) {
if(dot==3|dot==2|dot==1){
int dotDecision= (int) Math.pow(10,dot);//根据记录以及数据格式类型发现,当dot该位置为1,2,3时,
// 其小数点分别位于右2,3,4位,顾考虑将其除以对应的dot次方,格式网站(https://note.youdao.com/share/?token=517D506A7CF04DB3A2406129928B0B08&gid=15157487#/)
float a=((float)Integer.parseInt(data.substring(10, 12) + data.substring(8, 10), 16))/dotDecision;//将字符串连接转换为float类型
String display=a+"";//将float转换为字符串,以显示
TextView textView = (TextView) findViewById();
textView.setText(display);
}else if(dot==9){
float a=((float)Integer.parseInt(data.substring(10, 12) + data.substring(8, 10), 16))/10;//将字符串连接转换为float类型
String display=a+"";//将float转换为字符串,以显示
TextView textView = (TextView) findViewById();
textView.setText(display);
}
// else {
// Toast.makeText(MainActivity.this,"请确认万用表在V档位",Toast.LENGTH_LONG).show();
// }
}else {
String display=""+0;//考虑到暂时用不到负数,故将其设置为0处理
TextView textView = (TextView) findViewById();
textView.setText(display);
Toast.makeText(MainActivity.this,"请检查万用表是否接反而造成负值",Toast.LENGTH_SHORT).show();
}
// Toast.makeText(MainActivity.this, data, Toast.LENGTH_LONG).show();
}
});
}else {
Toast.makeText(MainActivity.this, "无数据", Toast.LENGTH_LONG).show();
}
}
//将byte类型转换为字符串类型
public String bytesToHexString(byte[] raw){
StringBuilder stringBuilder = new StringBuilder("");
if (raw == null || raw.length <= 0) {
return null;
}
for (int i = 0; i < raw.length; i++) {
int v = raw[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}3.总结

上图为手机界面,由于本次只是接收蓝牙传输数据加以显示,下一步将依据接收到的数据设置阈值以控制其他设备
















