实现一个Android APP主要需求:
1、APP连接蓝牙转以太网的转接板给底板配置广播信息;
2、广播板的状态能通过蓝牙转接板透传给APP;
蓝牙搜索,发现这些之前一个app都做过,但是读写数据没有做,关键点是:
1、GATT连接;
2、服务特征UUID/读特征UUID 配置特征UUID/写特征UUID,这几个特征UUID 最好是找厂家确认。
要接收到蓝牙的数据,关键是读配置Enable功能:setBleNotification方法,网上这块有很多方法,最后生效的是下面的方法。
public void onServicesDiscovered(BluetoothGatt gatt, int status) { | |
if (status == BluetoothGatt.GATT_SUCCESS) { | |
// services are discoverd | |
Log.d(TAG, "onServicesDiscovered: GATT_SUCCESS"); | |
mBluetoothGatt = gatt; | |
} | |
List<BluetoothGattService> list = gatt.getServices(); | |
for(BluetoothGattService service: list){ | |
serviceUid = service.getUuid(); | |
for(BluetoothGattCharacteristic characteristic : service.getCharacteristics()){ | |
//获取到相应的服务UUID和特征UUID | |
characterUid = characteristic.getUuid(); | |
if (characterUid.toString().contains("8e32")){ | |
//readd | |
characterReadUid = characterUid; | |
m_bluetoothGattCharacteristic_read = characteristic; | |
gatt.setCharacteristicNotification(characteristic, true); | |
} | |
if (characterUid.toString().contains("8e40")){ | |
//write | |
characterWriteUid = characterUid; | |
} | |
if (characterUid.toString().contains("2902")){ | |
//read config | |
characterReadConfigUid = characterUid; | |
} | |
} | |
if (serviceUid.toString().contains("8e20")){ | |
break; | |
} | |
} | |
gatt.requestMtu(200); | |
} | |
//关键是这个方法,网上的说法很多,但是最后生效的还是下面的方法 | |
private void setBleNotification(){ | |
if (characterReadConfigUid == null){ | |
characterReadConfigUid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); | |
} | |
BluetoothGattService service = mBluetoothGatt.getService(serviceUid); | |
if(m_bluetoothGattCharacteristic_read != null) { | |
List<BluetoothGattDescriptor> descriptors = m_bluetoothGattCharacteristic_read.getDescriptors(); | |
Log.v(TAG, "len:"+descriptors.size()); | |
// 遍历设置 BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE 值 , 并写出 | |
for(BluetoothGattDescriptor descriptor : descriptors) { | |
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); | |
mBluetoothGatt.writeDescriptor(descriptor); | |
} | |
// BluetoothGattDescriptor descriptor = m_bluetoothGattCharacteristic_read.getDescriptor(characterReadConfigUid); | |
// descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); | |
// mBluetoothGatt.writeDescriptor(descriptor); | |
// descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); | |
// mBluetoothGatt.writeDescriptor(descriptor); | |
mBluetoothGatt.readCharacteristic(m_bluetoothGattCharacteristic_read); | |
} | |
} |
3、写完成的回调;
写需要设置最大MTU,否则大于MTU的字符串发不出去
public void onServicesDiscovered(BluetoothGatt gatt, int status) { | |
if (status == BluetoothGatt.GATT_SUCCESS) { | |
// services are discoverd | |
Log.d(TAG, "onServicesDiscovered: GATT_SUCCESS"); | |
mBluetoothGatt = gatt; | |
} | |
gatt.requestMtu(200); | |
} | |
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { | |
Log.v(TAG, "onMtuChanged:"+status + ",mtu:"+mtu); | |
if(status == BluetoothGatt.GATT_SUCCESS){ | |
setBleNotification(); | |
} | |
} |
4、读数据的回调函数;onCharacteristicChanged 蓝牙收到数据的回调方法,网上有说是onCharacteristicRead方法,实际是这个。
public void onCharacteristicChanged(BluetoothGatt gatt, | |
BluetoothGattCharacteristic characteristic) { | |
if (characteristic.getUuid().equals(characterReadUid)) { | |
//step 7-1:读取出characteristic的value值 | |
// 收到的数据 | |
byte[] receiveByte = characteristic.getValue(); | |
String res = new String(receiveByte); | |
Log.i(TAG, "=====>2 value =" + res + ",len:" + receiveByte.length); | |
if (notifyCallback != null){ | |
notifyCallback.notifyMessage(res); | |
} | |
} | |
} |
5、蓝牙转接板居然还有一个NAT的问题,蓝牙转接板主动转发过来的UDP包,必须使用相同的端口回过去,否则转接板收不到包。
6、主要代码:
GattCommnucationManage.java代码
import android.bluetooth.BluetoothDevice; | |
import android.bluetooth.BluetoothGatt; | |
import android.bluetooth.BluetoothGattCallback; | |
import android.bluetooth.BluetoothGattCharacteristic; | |
import android.bluetooth.BluetoothGattDescriptor; | |
import android.bluetooth.BluetoothGattService; | |
import android.bluetooth.BluetoothProfile; | |
import android.content.Context; | |
import android.util.Log; | |
import java.util.List; | |
import java.util.UUID; | |
import top.keepempty.MyApplication; | |
public class GattCommnucationManager { | |
static GattCommnucationManager instanceGattMgr = new GattCommnucationManager(); | |
private BluetoothDevice selectBleDevice; | |
private Context mContext; | |
public static final String TAG = "GattCommnucationManager"; | |
BluetoothGatt mBluetoothGatt; | |
private UUID serviceUid; | |
private UUID characterUid; | |
private UUID characterReadUid; | |
private UUID characterReadConfigUid; | |
private UUID characterWriteUid; | |
private BleMessageNotify notifyCallback; | |
private GattCommnucationManager(){ | |
} | |
public static GattCommnucationManager getInstance(){ | |
if (instanceGattMgr == null){ | |
instanceGattMgr = new GattCommnucationManager(); | |
} | |
return instanceGattMgr; | |
} | |
public void registerInterface(BleMessageNotify callback){ | |
notifyCallback = callback; | |
} | |
public void setDevice(BluetoothDevice device){ | |
mContext = MyApplication.getInstance(); | |
if (selectBleDevice != null && mBluetoothGatt != null){ | |
mBluetoothGatt.disconnect(); | |
mBluetoothGatt.close(); | |
mBluetoothGatt = null; | |
} | |
selectBleDevice = device; | |
connectSelectDevice(); | |
} | |
public void connectSelectDevice(){ | |
if (selectBleDevice == null){ | |
return; | |
} | |
mBluetoothGatt = selectBleDevice.connectGatt(mContext, false, mGattCallback); | |
} | |
//关闭蓝牙和Gatt输入通道 避免出现Gatt 133状态错误 | |
public void closeBlueToothGatt(){ | |
if (mBluetoothGatt == null){ | |
return; | |
} | |
mBluetoothGatt.disconnect(); | |
mBluetoothGatt.close(); | |
mBluetoothGatt = null; | |
selectBleDevice = null; | |
} | |
/** | |
* 向设备发送数据 | |
* @param value | |
* @return | |
*/ | |
public boolean writeRXCharacteristic(byte[] value) { | |
if (mBluetoothGatt == null){ | |
return false; | |
} | |
BluetoothGattService rxService = mBluetoothGatt.getService(serviceUid); | |
if (rxService == null) { | |
//Service not supported | |
Log.d(TAG, "writeRXCharacteristic: Service not supported"); | |
return false; | |
} | |
BluetoothGattCharacteristic rxChar = rxService.getCharacteristic(characterWriteUid); | |
if (rxChar == null) { | |
// service not supported | |
Log.d(TAG, "writeRXCharacteristic: Service not supported"); | |
return false; | |
} | |
rxChar.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); | |
rxChar.setValue(value); | |
Log.d(TAG, "writeRXCharacteristic: "+String.valueOf(rxChar)); | |
return mBluetoothGatt.writeCharacteristic(rxChar); | |
} | |
public final UUID DEVICE_INFO_SERVICE_UUID = UUID.fromString("00001800-0000-1000-8000-00805f9b34fb"); | |
//Charcteristic UUID | |
public final UUID VID_PID_CHARACTERISTIC_UUID = UUID.fromString("00002a00-0000-1000-8000-00805f9b34fb"); | |
BluetoothGattCharacteristic m_bluetoothGattCharacteristic_read; | |
/** | |
* 连接成功或者失败的回调函数 | |
*/ | |
public final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { | |
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { | |
Log.d(TAG, "onConnectionStateChange: "+newState); | |
if (newState == BluetoothProfile.STATE_CONNECTED) { | |
//bluetooth is connected so discover services | |
Log.d(TAG, "onConnectionStateChange: "+mBluetoothGatt.getDevice().getName()); | |
mBluetoothGatt.discoverServices(); | |
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) { | |
//Bluetooth is disconnected | |
Log.d(TAG, "onConnectionStateChange: disconnected"); | |
mBluetoothGatt = null; | |
} | |
} | |
private void setBleNotification(){ | |
if (characterReadConfigUid == null){ | |
characterReadConfigUid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); | |
} | |
BluetoothGattService service = mBluetoothGatt.getService(serviceUid); | |
if(m_bluetoothGattCharacteristic_read != null) { | |
List<BluetoothGattDescriptor> descriptors = m_bluetoothGattCharacteristic_read.getDescriptors(); | |
Log.v(TAG, "len:"+descriptors.size()); | |
// 遍历设置 BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE 值 , 并写出 | |
for(BluetoothGattDescriptor descriptor : descriptors) { | |
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); | |
mBluetoothGatt.writeDescriptor(descriptor); | |
} | |
// BluetoothGattDescriptor descriptor = m_bluetoothGattCharacteristic_read.getDescriptor(characterReadConfigUid); | |
// descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); | |
// mBluetoothGatt.writeDescriptor(descriptor); | |
// descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); | |
// mBluetoothGatt.writeDescriptor(descriptor); | |
mBluetoothGatt.readCharacteristic(m_bluetoothGattCharacteristic_read); | |
} | |
} | |
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { | |
Log.v(TAG, "onMtuChanged:"+status + ",mtu:"+mtu); | |
if(status == BluetoothGatt.GATT_SUCCESS){ | |
setBleNotification(); | |
} | |
} | |
public void onServicesDiscovered(BluetoothGatt gatt, int status) { | |
if (status == BluetoothGatt.GATT_SUCCESS) { | |
// services are discoverd | |
Log.d(TAG, "onServicesDiscovered: GATT_SUCCESS"); | |
mBluetoothGatt = gatt; | |
} | |
List<BluetoothGattService> list = gatt.getServices(); | |
for(BluetoothGattService service: list){ | |
serviceUid = service.getUuid(); | |
for(BluetoothGattCharacteristic characteristic : service.getCharacteristics()){ | |
//获取到相应的服务UUID和特征UUID | |
characterUid = characteristic.getUuid(); | |
if (characterUid.toString().contains("8e32")){ | |
//readd | |
characterReadUid = characterUid; | |
m_bluetoothGattCharacteristic_read = characteristic; | |
gatt.setCharacteristicNotification(characteristic, true); | |
} | |
if (characterUid.toString().contains("8e40")){ | |
//write | |
characterWriteUid = characterUid; | |
} | |
if (characterUid.toString().contains("2902")){ | |
//read config | |
characterReadConfigUid = characterUid; | |
} | |
} | |
if (serviceUid.toString().contains("8e20")){ | |
break; | |
} | |
} | |
gatt.requestMtu(200); | |
} | |
public void onCharacteristicRead(BluetoothGatt gatt, | |
BluetoothGattCharacteristic characteristic, | |
int status) { | |
if (status == BluetoothGatt.GATT_SUCCESS) { | |
String value = ""; | |
if (characteristic.getUuid().equals(characterReadUid)) | |
{ | |
//step 7-1:读取出characteristic的value值 | |
// 收到的数据 | |
byte[] receiveByte = characteristic.getValue(); | |
Log.i(TAG, "=====>读取到 value =" +String.valueOf(receiveByte) + ",len:"+receiveByte.length); | |
//step 7-2:此处为ascii表字符,需转换为十进制ascii值 | |
//再将十进制ascii值,转换为十六进制 | |
//String VID = changeAsciiTo16(value.charAt(0)); | |
//String PID = changeAsciiTo16(value.charAt(value.length() - 1)); | |
//设备VID、PID读取成功,handle更新主线程界面UI | |
//Log.i(TAG, characteristic.getUuid()+",VID:"+VID+",PID:"+PID); | |
} | |
} | |
} | |
public void onCharacteristicChanged(BluetoothGatt gatt, | |
BluetoothGattCharacteristic characteristic) { | |
if (characteristic.getUuid().equals(characterReadUid)) { | |
//step 7-1:读取出characteristic的value值 | |
// 收到的数据 | |
byte[] receiveByte = characteristic.getValue(); | |
String res = new String(receiveByte); | |
Log.i(TAG, "=====>2 value =" + res + ",len:" + receiveByte.length); | |
if (notifyCallback != null){ | |
notifyCallback.notifyMessage(res); | |
} | |
} | |
} | |
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { | |
super.onCharacteristicWrite(gatt, characteristic, status); | |
if (status == BluetoothGatt.GATT_SUCCESS) { | |
mBluetoothGatt.readCharacteristic(m_bluetoothGattCharacteristic_read); | |
} | |
} | |
}; | |
static public interface BleMessageNotify { | |
void notifyMessage(String msg); | |
} | |
} |