4

I'm trying to receive a notification from the device when I write a value on a characteristic, but I don´t receive anything. I enable the notification on the characteristic and then I write the value. I've seen that the characteristic in the device have changed its value but I can't get the notification. Here is my code:

DeviceActivity:

public class DevicesActivity extends Activity {

private BLEService mBluetoothLeService;
private String mDeviceAddress;
private boolean mConnected = false;
private BluetoothGattCharacteristic mNotifyCharacteristic;

private final ServiceConnection mServiceConnection = new ServiceConnection() {

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder service) {
        mBluetoothLeService = ((BLEService.LocalBinder) service).getService();
        if (!mBluetoothLeService.initialize()) {
            finish();
        }
        mBluetoothLeService.context = DevicesActivity.this;
        mBluetoothLeService.connect(mDeviceAddress);
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        mBluetoothLeService = null;
    }
};

private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if (BLEService.ACTION_GATT_CONNECTED.equals(action)) {
            mConnected = true;
            invalidateOptionsMenu();
        } else if (BLEService.ACTION_GATT_DISCONNECTED.equals(action)) {
            mConnected = false;
            invalidateOptionsMenu();
        } else if (BLEService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
            List<BluetoothGattService> servicesList;
            servicesList = mBluetoothLeService.getSupportedGattServices();
            Iterator<BluetoothGattService> iter = servicesList.iterator();
            while (iter.hasNext()) {
                BluetoothGattService bService = (BluetoothGattService) iter.next();
                if (bService.getUuid().toString().equals(BLEUUID.SERVICE)){
                    mService = bService;
                }
            }
        } else if (BLEService.ACTION_DATA_AVAILABLE.equals(action)) {
            displayData(intent.getStringExtra(BLEService.EXTRA_DATA));
        }
    }
};

private static final String TAG = "BLEDevice";
public static final String EXTRA_BLUETOOTH_DEVICE = "BT_DEVICE";
private BluetoothAdapter mBTAdapter;
private BluetoothDevice mDevice;
private BluetoothGatt mConnGatt;
private int mStatus;
BluetoothGattService mService;

private EditText pinTxt;
private Button cancelBtn;
private Button unlockBtn;
private Button changePinBtn;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
    setContentView(R.layout.activity_devices);

    pinTxt = (EditText) findViewById(R.id.pin_txt);
    cancelBtn = (Button) findViewById(R.id.cancel_btn);
    unlockBtn = (Button) findViewById(R.id.unlock_btn);
    changePinBtn = (Button) findViewById(R.id.change_pin_btn);

    unlockBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            String aux = pinTxt.getText().toString();
            mBluetoothLeService.sendCharacteristic(aux, getBTDeviceExtra());
        }
    });

    mDevice = getBTDeviceExtra();
    mDeviceAddress = mDevice.getAddress();
    if (mServiceConnection == null){
        Log.v("NULL", "mServiceConnection NULL");
    }

    Intent gattServiceIntent = new Intent(this, BLEService.class);
    if (gattServiceIntent==null){
        Log.v("NULL", "mServiceConnection NULL");
    }
    bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
    mStatus = BluetoothProfile.STATE_DISCONNECTED;
}

private void displayData(String data) {
    if (data != null) {
        Toast.makeText(DevicesActivity.this, data, Toast.LENGTH_LONG);
    }
}
@Override
protected void onDestroy() {
    super.onDestroy();
    unbindService(mServiceConnection);
    mBluetoothLeService = null;
}

@Override
protected void onPause() {
    super.onPause();
    unregisterReceiver(mGattUpdateReceiver);
}

private static IntentFilter makeGattUpdateIntentFilter() {
    final IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(BLEService.ACTION_GATT_CONNECTED);
    intentFilter.addAction(BLEService.ACTION_GATT_DISCONNECTED);
    intentFilter.addAction(BLEService.ACTION_GATT_SERVICES_DISCOVERED);
    intentFilter.addAction(BLEService.ACTION_DATA_AVAILABLE);
    return intentFilter;
}

@Override
protected void onResume() {
    super.onResume();
    registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
    if (mBluetoothLeService != null) {
        final boolean result = mBluetoothLeService.connect(mDeviceAddress);
    }

}

private BluetoothDevice getBTDeviceExtra() {
    Intent intent = getIntent();
    if (intent
        == null) {            return null;
    }

    Bundle extras = intent.getExtras();
    if (extras == null) {
        return null;
    }

    BluetoothDevice aux = extras.getParcelable(EXTRA_BLUETOOTH_DEVICE);
    return aux;
}

BLEService:

 private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        String intentAction;
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            intentAction = ACTION_GATT_CONNECTED;
            mConnectionState = STATE_CONNECTED;
            broadcastUpdate(intentAction);
            Log.i(TAG, "Connected to GATT server.");
            // Attempts to discover services after successful connection.
            Log.i(TAG, "Attempting to start service discovery:" +
                    mBluetoothGatt.discoverServices());

        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            intentAction = ACTION_GATT_DISCONNECTED;
            mConnectionState = STATE_DISCONNECTED;
            Log.i(TAG, "Disconnected from GATT server.");
            broadcastUpdate(intentAction);
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            for (BluetoothGattService service : gatt.getServices()) {
                if ((service == null) || (service.getUuid() == null)) {
                    continue;
                }
                if (BLEUUID.SERVICE.equalsIgnoreCase(service.getUuid().toString())) {
                    mService = service;
                }
            }
            broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
        } else {
            Log.w(TAG, "onServicesDiscovered received: " + status);
        }
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic,int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            broadcastUpdate(EXTRA_DATA, characteristic);
            Log.i("CARAC","CARACTERISTICA LEIDA onCharacteristicRead()");
        }
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) {
        readCharacteristic(characteristic);
        broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
        Log.i("CARAC","CAMBIO EN CARACTERISTICA");
    }
};

private void broadcastUpdate(final String action) {
    final Intent intent = new Intent(action);
    sendBroadcast(intent);
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
private void broadcastUpdate(final String action,final BluetoothGattCharacteristic characteristic) {
    final Intent intent = new Intent(action);

    if (PIN_CHARACTERISTIC.equals(characteristic.getUuid())) {

        final String pin = characteristic.getStringValue(0);
        intent.putExtra(EXTRA_DATA, String.valueOf(pin));
    } else {
        // For all other profiles, writes the data formatted in HEX.
        final byte[] data = characteristic.getValue();
        if (data != null && data.length > 0) {
            final StringBuilder stringBuilder = new StringBuilder(data.length);
            for(byte byteChar : data)
                stringBuilder.append(String.format("%02X ", byteChar));
            Log.i("RECIBIDO", "RECIBIDOS DATOS");
            intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
        }
    }
    sendBroadcast(intent);
}

public class LocalBinder extends Binder {
    BLEService getService() {
        return BLEService.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();

public boolean initialize() {
    if (mBluetoothManager == null) {
        mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        if (mBluetoothManager == null) {
            return false;
        }
    }
    mBluetoothAdapter = mBluetoothManager.getAdapter();
    if (mBluetoothAdapter == null) {
        return false;
    }
    return true;
}

public boolean connect(final String address) {
    if (mBluetoothAdapter == null || address == null) {
        return false;
    }

    // Previously connected device.  Try to reconnect.
    if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
            && mBluetoothGatt != null) {
        if (mBluetoothGatt.connect()) {
            mConnectionState = STATE_CONNECTING;
            return true;
        } else {
            return false;
        }
    }

    final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
    if (device == null) {
        return false;
    }

    mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
    mBluetoothDeviceAddress = address;
    mConnectionState = STATE_CONNECTING;
    return true;
}

public void disconnect() {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.disconnect();
}

public void close() {
    if (mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.close();
    mBluetoothGatt = null;
    Log.e("CIERRE", "CONEXION CERRADA");
}

public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.readCharacteristic(characteristic);
    Log.i("READ", "CARACTERISTICA LEIDA");
}

public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
    if (PIN_CHARACTERISTIC.equals(characteristic.getUuid())){
        BluetoothGattDescriptor descriptor =
                new BluetoothGattDescriptor(UUID.nameUUIDFromBytes(BLEUUID.PIN_CHARACTERISTIC_CONFIG_DESCRIPTOR.getBytes()),
                        BluetoothGattDescriptor.PERMISSION_WRITE_SIGNED);

        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        mBluetoothGatt.writeDescriptor(descriptor);
    }
}

public List<BluetoothGattService> getSupportedGattServices() {
    if (mBluetoothGatt == null) return null;
    return mBluetoothGatt.getServices();
}

public void sendCharacteristic(String pin, BluetoothDevice device){

    byte[] pinByte = pin.getBytes();
    int pinInt = Integer.valueOf(pin);

    BluetoothGattCharacteristic ch = (BluetoothGattCharacteristic) mService.getCharacteristic(UUID
            .fromString(BLEUUID.PIN_CHARACTERISTIC_UUID));

    ch.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);

    ch.setValue(pin);

    Toast.makeText(context, "CARACTERISTICA ASIGNADA", Toast.LENGTH_SHORT).show();
    connect(device.getAddress());
    mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
    setCharacteristicNotification(ch, true);

    if (mBluetoothGatt.writeCharacteristic(ch)) {
        Toast.makeText(context, "CARACTERISTICA ESCRITA", Toast.LENGTH_SHORT).show();
    }


}

}

BLEUUID

public class BLEUUID {

// CARACTERISTICA PIN
public  static final String SERVICE ="0000fff0-0000-1000-8000-00805f9b34fb";
public static final String PIN_CHARACTERISTIC_UUID="0000fff9-0000-1000-8000-00805f9b34fb";
public static final String PIN_CHARACTERISTIC_CONFIG_DESCRIPTOR="0x2902";

private static HashMap<String, String> attributes = new HashMap();
static {
    attributes.put(SERVICE, "Service");
    attributes.put(PIN_CHARACTERISTIC_UUID, "Pin");
}

When I debug, onCharacteristicChange() never executes.

Somebody knows where's the problem

emirbet
  • 53
  • 1
  • 1
  • 7
  • I am not sure that writing the characteristic yourself counts as something that should be notified it's not the normal usage case. The characteristic changing by itself is what normally you get notified about. E.g. it represents a temperature or some other measurement that changes so you get an update. – Ifor Apr 30 '14 at 13:35
  • I send the PIN code and the device must send me "OKAY" or "FAIL". In the nRF Master Control app, I see that the value when I send the PIN turn into "OKAY", but I'm trying to catch this value without success. – emirbet Apr 30 '14 at 13:49
  • are you sure the property of the characteristic is `notify`? It could be `indicate`. It's almost the same thing but it's not. For that you need to set: `descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);` Check your device's specification – benka May 01 '14 at 21:44
  • yes, the property is notify, although I tried with ENABLE_INDICATION_VALUE too; and it didn't work neither. – emirbet May 02 '14 at 08:07
  • I've noticed that `android.bluetooth.BluetoothGattCharacteristic.getStringValue()` can crash. The app I'm working on doesn't crash, but the code following `getStringValue()` is never executed. I fixed it by wrapping try{}catch(Exception e){} around it. **see**: `BluetoothGatt﹕ Unhandled exception in callback java.lang.NullPointerException at android.bluetooth.BluetoothGattCharacteristic.getStringValue(BluetoothGattCharacteristic.java:506)` – Someone Somewhere Apr 28 '15 at 07:39
  • i don't know why in your code you connect after you are writing in the characteristic !! you must connect before you write. and check if really there is a connection. after that please do some log and put it . – Fakher Sep 10 '15 at 08:12

1 Answers1

18

Here is the general pattern for how things need to work with BLE on Android:

  1. You try to connect
  2. You get a callback indicating it is connected
  3. You discover services
  4. You are told services are discovered
  5. You get the characteristics
  6. For each characteristic you get the descriptors
  7. For the descriptor you set it to enable notification/indication with BluetoothGattDescriptor.setValue()
  8. You write the descriptor with BluetoothGatt.writeDescriptor()
  9. You enable notifications for the characteristic locally with BluetoothGatt.setCharacteristicNotification(). Without this you won't get called back.
  10. You get notification that the descriptor was written
  11. Now you can write data to the characteristic. All of the characteristic and descriptor configuration has do be done before anything is written to any characteristic.
Douglas Jones
  • 2,522
  • 1
  • 23
  • 27
  • I have to write to a device then device will broadcast some data. For this I write to a characteristics of a service. I got some data through receiver. But that is not my required data. Device will do some task then it will broadcast the data. For that I am thinking that any characteristics of that service will broadcast the required data. I am going on right way. Actually on notification set I am not getting any data.Please suggest – Ajit Nov 03 '14 at 13:42
  • @Ajit, I'm not exactly sure what you are asking but it sounds like you haven't set up the characteristic notifications properly. I added a new step 9, and updated steps 7 and 8 to be more clear. – Douglas Jones Nov 11 '14 at 04:38
  • I got the point but characteristic having write property, not having notification property. – Ajit Nov 11 '14 at 05:19
  • please check http://stackoverflow.com/questions/26800606/ble-4-0-getting-the-broadcast-data-from-device-to-phone – Ajit Nov 11 '14 at 05:20
  • Thanks, you've saved my night. – Miha_x64 Oct 16 '16 at 21:09