Android BLE отправляет команду, записывая характеристики, не отвечающие
Я пытаюсь отправить следующие байты следующим образом через BLE
byte [] message = new byte [5];
message[0] = (byte) 0x26;
message[1] = (byte) 0x24;
message[2] = (byte) 0x00;
message[3] = (byte) 0x52;
message[4] = (byte) 0xc9;
Я успешно подключился к устройству, используя U_NOVA_SERVICE как UUID службы, U_NOVA_R_CHARACTERISTIC в качестве ЧТЕНИЯ ХАРАКТЕРИСТИК, U_NOVA_W_CHARACTERISTIC AS характеристики записи
Когда дело доходит до выполнения и отправки команд, устройство не имеет ответа. Ниже приведен мой код
package org.zpcat.ble;
import android.annotation.TargetApi;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
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.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.SystemClock;
import org.zpcat.ble.utils.Log;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
/**
* Service for managing connection and data communication with a GATT server hosted on a
* given Bluetooth LE device. */
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class BluetoothLeService extends Service {
//private final static String TAG = BluetoothLeService.class.getSimpleName();
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private String mBluetoothDeviceAddress;
private BluetoothGatt mBluetoothGatt;
private int mConnectionState = STATE_DISCONNECTED;
private BLEServiceCallback mBLEServiceCb = null;
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTING = 1;
private static final int STATE_CONNECTED = 2;
public final static UUID UUID_HEART_RATE_MEASUREMENT = UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);
public final static UUID U_NOVA_SERVICE = UUID.fromString(SampleGattAttributes.NOVA_SERVICE);
public final static UUID U_NOVA_R_CHARACTERISTIC = UUID.fromString(SampleGattAttributes.NOVA_R_CHARACTERISTIC);
public final static UUID U_NOVA_W_CHARACTERISTIC = UUID.fromString(SampleGattAttributes.NOVA_W_CHARACTERISTIC);
public static final String ACTION_DATA_AVAILABLE = "com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
public static final String ACTION_DATA_NOTIFY = "android.ble.common.ACTION_DATA_NOTIFY";
public static final String ACTION_DATA_READ = "android.ble.common.ACTION_DATA_READ";
public static final String ACTION_DATA_WRITE = "android.ble.common.ACTION_DATA_WRITE";
public static final String ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
public static final String ACTION_GATT_DISCONNECTED = "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
public static final String ACTION_GATT_SERVICES_DISCOVERED = "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
public static final boolean AUTO_CONNECT = true;
public static final String EXTRA_ADDRESS = "android.ble.common.EXTRA_ADDRESS";
public static final String EXTRA_DATA = "com.example.bluetooth.le.EXTRA_DATA";
public static final String EXTRA_STATUS = "android.ble.common.EXTRA_STATUS";
public static final String EXTRA_UUID = "android.ble.common.EXTRA_UUID";
static final String TAG = "BluetoothLeService";
private static BluetoothLeService mThis = null;
private final IBinder binder = new LocalBinder();
private Handler mActivityHandler = null;
private BluetoothAdapter mBtAdapter = null;
private volatile boolean mBusy = false;
private final long READING_RSSI_TASK_FREQENCY = 1000;
private static final int READ_RSSI_REPEAT = 1;
private final long READING_MESSAGE_TASK_FREQENCY = 500;
private static final int READ_MESSAGE_REPEAT = 2;
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case READ_RSSI_REPEAT:
if (mBluetoothGatt != null) {
mBluetoothGatt.readRemoteRssi();
}
sendMessageDelayed(obtainMessage(READ_RSSI_REPEAT),
READING_RSSI_TASK_FREQENCY);
break;
}
}
};
private void startReadRssi() {
if (mHandler.hasMessages(READ_RSSI_REPEAT)) {
Log.w("+++++++++ Handler already has Message: READ_RSSI_REPEAT");
}
mHandler.sendEmptyMessage(READ_RSSI_REPEAT);
}
private void stopReadRssi() {
mHandler.removeMessages(READ_RSSI_REPEAT);
}
private final Handler mHandlerM = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case READ_MESSAGE_REPEAT:
if (mBluetoothGatt != null) {
BluetoothGattService service = mBluetoothGatt.getService(U_NOVA_SERVICE);
if(service ==null) return;
BluetoothGattCharacteristic characteristic = service.getCharacteristic(U_NOVA_R_CHARACTERISTIC);
if(characteristic ==null) return;
mBluetoothGatt.setCharacteristicNotification(characteristic, true);
mBluetoothGatt.readCharacteristic(characteristic);
}
sendMessageDelayed(obtainMessage(READ_MESSAGE_REPEAT), READING_MESSAGE_TASK_FREQENCY);
break;
}
}
};
private void startReadMessage() {
if (mHandlerM.hasMessages(READ_MESSAGE_REPEAT)) {
Log.w("+++++++++ Handler already has Message: READ_MESSAGE_REPEAT");
}
mHandlerM.sendEmptyMessage(READ_MESSAGE_REPEAT);
}
private void stopReadMessage() {
mHandlerM.removeMessages(READ_MESSAGE_REPEAT);
}
// Implements callback methods for GATT events that the app cares about. For example,
// connection change and services discovered.
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.d("onConnectionStateChange status = " + status + ", newState = " + newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
if (mBLEServiceCb != null) {
mBLEServiceCb.notifyConnectedGATT();
}
Log.d("Connected to GATT server.");
// Attempts to discover services after successful connection.
Log.d("Attempting to start service discovery:" +
mBluetoothGatt.discoverServices());
startReadRssi();
startReadMessage();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
if (mBLEServiceCb != null) {
mBLEServiceCb.notifyDisconnectedGATT();
}
stopReadRssi();
stopReadMessage();
Log.d("Disconnected from GATT server.");
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.d("onServicesDiscovered status = " + status);
if (status == BluetoothGatt.GATT_SUCCESS) {
if (mBLEServiceCb != null) {
mBLEServiceCb.displayGATTServices();
}
} else {
Log.d("onServicesDiscovered received: " + status);
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
Log.d("onCharacteristicRead status: " + status);
if (status == BluetoothGatt.GATT_SUCCESS) {
displayCharacteristic(characteristic);
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
Log.d("------------- onCharacteristicWrite status: " + status);
// do somethings here.
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
Log.d("onCharacteristicChanged");
displayCharacteristic(characteristic);
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
//Log.d("onReadRemoteRssi rssi = " + rssi + "; status = " + status);
if (mBLEServiceCb != null) {
mBLEServiceCb.displayRssi(rssi);
}
}
};
public void setBLEServiceCb(BLEServiceCallback cb) {
if (cb != null) {
mBLEServiceCb = cb;
}
}
private void displayCharacteristic(final BluetoothGattCharacteristic characteristic) {
String msg = null;
// This is special handling for the Heart Rate Measurement profile. Data parsing is
// carried out as per profile specifications:
// http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
// For all other profiles, writes the data formatted in HEX.
final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
try {
String result = new String ( data, "UTF-8");
mBLEServiceCb.displayData(result);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (mBLEServiceCb != null) {
mBLEServiceCb.displayData(msg);
}
}
public class LocalBinder extends Binder {
BluetoothLeService getService() {
return BluetoothLeService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
// After using a given device, you should make sure that BluetoothGatt.close() is called
// such that resources are cleaned up properly. In this particular example, close() is
// invoked when the UI is disconnected from the Service.
close();
return super.onUnbind(intent);
}
private final IBinder mBinder = new LocalBinder();
/**
* Initializes a reference to the local Bluetooth adapter.
*
* @return Return true if the initialization is successful.
*/
public boolean initialize() {
// For API level 18 and above, get a reference to BluetoothAdapter through
// BluetoothManager.
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
Log.e("Unable to initialize BluetoothManager.");
return false;
}
}
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Log.e("Unable to obtain a BluetoothAdapter.");
return false;
}
return true;
}
/**
* Connects to the GATT server hosted on the Bluetooth LE device.
*
* @param address The device address of the destination device.
*
* @return Return true if the connection is initiated successfully. The connection result
* is reported asynchronously through the
* {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
* callback.
*/
public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
Log.w("BluetoothAdapter not initialized or unspecified address.");
return false;
}
// Previously connected device. Try to reconnect.
if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
&& mBluetoothGatt != null) {
Log.d("Trying to use an existing mBluetoothGatt for connection.");
if (mBluetoothGatt.connect()) {
mConnectionState = STATE_CONNECTING;
return true;
} else {
return false;
}
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
Log.w("Device not found. Unable to connect.");
return false;
}
// We want to directly connect to the device, so we are setting the autoConnect
// parameter to false.
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
Log.d("Trying to create a new connection.");
mBluetoothDeviceAddress = address;
mConnectionState = STATE_CONNECTING;
return true;
}
/**
* Disconnects an existing connection or cancel a pending connection. The disconnection result
* is reported asynchronously through the
* {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
* callback.
*/
public void disconnect() {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w("BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.disconnect();
}
/**
* After using a given BLE device, the app must call this method to ensure resources are
* released properly.
*/
public void close() {
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}
/**
* Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported
* asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
* callback.
*
* @param characteristic The characteristic to read from.
*/
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w("BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
/**
* Requst a write on a give {@code BluetoothGattCharacteristic}. The write result is reported
* asynchronously through the {@code BluetoothGattCallback#onCharacteristicWrite(andorid.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
* callback.
*/
public void writeCharacteristic(BluetoothGattCharacteristic characteristic , byte [] value) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w("BluetoothAdapter not initialized");
return;
}
// mBluetoothGatt.writeCharacteristic(characteristic);
byte [] message = new byte [5];
message[0] = (byte) 0x26;
message[1] = (byte) 0x24;
message[2] = (byte) 0x00;
message[3] = (byte) 0x52;
message[4] = (byte) 0xc9;
characteristic.setValue(message);
boolean status = mBluetoothGatt.writeCharacteristic(characteristic);
}
/**
* Enables or disables notification on a give characteristic.
*
* @param characteristic Characteristic to act on.
* @param enabled If true, enable notification. False otherwise.
*/
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w("BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
// This is specific to Heart Rate Measurement.
if (U_NOVAX_R_CHARACTERISTIC.equals(characteristic.getUuid())) {
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
/**
* Retrieves a list of supported GATT services on the connected device. This should be
* invoked only after {@code BluetoothGatt#discoverServices()} completes successfully.
*
* @return A {@code List} of supported services.
*/
public List<BluetoothGattService> getSupportedGattServices() {
if (mBluetoothGatt == null) return null;
return mBluetoothGatt.getServices();
}
public interface BLEServiceCallback {
public void displayRssi(int rssi);
public void displayData(String data);
public void notifyConnectedGATT();
public void notifyDisconnectedGATT();
public void displayGATTServices();
}
public void reset(float [] data)
{
sendCommand(convert(NOVAXcommand.reset, data )) ;
}
private void sendCommand(byte[] convert) {
// TODO Auto-generated method stub
BluetoothGattService LumService = mBluetoothGatt.getService(U_NOVA_SERVICE);
if (LumService == null) { System.out.println("LumService null"); return; }
BluetoothGattCharacteristic LumChar = LumService.getCharacteristic(U_NOVA_W_CHARACTERISTIC);
if (LumChar == null) { System.out.println("LumChar null"); return; }
//LumChar.setValue(convert);
// boolean status = mBluetoothGatt.writeCharacteristic(LumChar); System.out.println("Write Status: " + status);
writeCharacteristic(LumChar , convert);
}
private byte[] toBytes(char[] chars ) {
CharBuffer charBuffer = CharBuffer.wrap(chars);
ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
byte[] bytes = Arrays.copyOfRange(byteBuffer.array(),
byteBuffer.position(), byteBuffer.limit());
Arrays.fill(charBuffer.array(), '\u0000'); // clear sensitive data
Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data
return bytes;
}
private byte [] convert(char[] prefix , float [] data){
char sync1 = prefix[0];
char sync2 = prefix[1];
char descriptor = prefix[2];
char lengthOfData = 0x00;
if(data.length > 0){
lengthOfData = (char) (data.length);
}
int checkSum = 0x77;
String descriptString = Integer.toString(descriptor, 10);
int descript = Integer.parseInt(descriptString);
int sum = checkSum + descript + data.length;
char dataChar [] = new char [data.length * 4];
for(int i = 0 ; i < data.length ; i ++){
String floatString = String.valueOf(data [i]);
if(floatString.length() ==4){ //if thousand
dataChar[4*i] = (char)((int)(floatString.charAt(0))-0);
dataChar[4*i+1] = (char)((int)(floatString.charAt(1)-0));
dataChar[4*i+2] = (char)((int)(floatString.charAt(2)-0));
dataChar[4*i +3] = (char)((int)(floatString.charAt(3)-0));
}else if(floatString.length() ==3){ //if hundred
dataChar[4*i] = (char)((int)('0'-0));
dataChar[4*i+1] = (char)((int)(floatString.charAt(0)-0));
dataChar[4*i+2] =(char)((int)(floatString.charAt(1)-0));
dataChar[4*i+3] =(char)((int)(floatString.charAt(2)-0));
}else if(floatString.length() ==2){ //if tenth
dataChar[4*i] = (char)((int)('0'-0));
dataChar[4*i+1] = (char)((int)('0'-0));
dataChar[4*i+2] = (char)((int)(floatString.charAt(0)-0));
dataChar[4*i+3] = (char)((int)(floatString.charAt(1)-0));
}else if(floatString.length() ==1){ //if digit
dataChar[4*i] = (char)((int)('0'-0));
dataChar[4*i+1] = (char)((int)('0'-0));
dataChar[4*i+2] = (char)((int)('0'-0));
dataChar[4*i+3] = (char)((int)(floatString.charAt(0)-0));
}else{
break;
}
}
if(data.length > 0){
for(int i = 0 ; i < data.length ; i ++){
sum += data [i];
}
}
if(sum > 255){
sum = sum % 255;
}
char css = (char)sum ;
char [] test = {} ;
if(data.length > 0){
char [] k = { sync1, sync2, descriptor ,lengthOfData } ;
char[] result = new char[k.length+ dataChar.length+1 ];
for (int i = 0 ; i < k .length ; i ++) {
result[i] = k [i];
}
for (int i = 0 ; i < dataChar.length ; i ++) {
result[k.length+i] = dataChar[i];
}
result[k.length + dataChar.length] = css;
test = result ;
}else{
char [] k = { sync1, sync2, descriptor ,lengthOfData , css};
test = k ;
}
for(char c : test){
System.out.println(c);
}
return toBytes(test);
}
}