1

I am trying to use android as a BLE peripheral (gatt server). I was able to get it advertising, and I can have the iOS app reeive notifications when certain characteristics are changed, but I am having trouble doing a simple writeValue.

On iOS I have:

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    if (!error) {
        for(int i = 0; i < service.characteristics.count; i++) { //Show every one
            CBCharacteristic *c = [service.characteristics objectAtIndex:i];
            if ([[c.UUID UUIDString] rangeOfString:@"43FF0001"].location!=NSNotFound){
                self.shot_detected_characteristic = c;
                [peripheral setNotifyValue:YES forCharacteristic:self.shot_detected_characteristic];
            }else if ([[c.UUID UUIDString] rangeOfString:@"43FF0002"].location!=NSNotFound){
                self.display_characteristic = c;
                [self writeValue:@"ABC" forCharacteristic:c onPeripheral:peripheral];
            }
            NSLog(@"characteristic uuid: %@", [c.UUID UUIDString]);
        }
    } else {
        NSLog(@"Characteristic discovery unsuccessful");
    }
}
-(IBAction*) sendData {
    CBPeripheral *p = [peripherals lastObject];
    [self writeValue:@"ABC" forCharacteristic:self.display_characteristic onPeripheral:p];
}
-(void) writeValue:(NSString*)value forCharacteristic:(CBCharacteristic*)c onPeripheral:(CBPeripheral *)p {
    NSData *d = [value dataUsingEncoding:NSUTF8StringEncoding];
    if (c.properties & CBCharacteristicPropertyWrite) {
        NSLog(@"Write with response"); //I am getting this message
        [p writeValue:d forCharacteristic:c type:CBCharacteristicWriteWithResponse];
    }else{
        NSLog(@"Unable to write");
    }
}

And on the Android side I have:

private BluetoothManager bluetoothManager;
private BluetoothGattServer gattServer;

private BluetoothGattCharacteristic displayCharacteristic;

private BluetoothDevice connected_device;
private BluetoothLeAdvertiser advertiser;

private void setupBLEService() {
    bluetoothManager = (android.bluetooth.BluetoothManager) this.getSystemService(Context.BLUETOOTH_SERVICE);

    if( ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
    }

    gattServer = bluetoothManager.openGattServer(this, gattServerCallback);

    String uuid_string = "92D9D153-9BE6-43FF-9672-3E2904628B9D";

    BluetoothGattService service = new BluetoothGattService(UUID.fromString(uuid_string), BluetoothGattService.SERVICE_TYPE_PRIMARY);

    shotDetectedCharacteristic = new BluetoothGattCharacteristic(
            UUID.fromString("43FF0001-9BE6-43FF-9672-3E2904628B9D"),
            BluetoothGattCharacteristic.PROPERTY_BROADCAST | BluetoothGattCharacteristic.PROPERTY_NOTIFY | BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE,
            BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE);

    BluetoothGattDescriptor gD = new BluetoothGattDescriptor(UUID.fromString(NOTIFICATION_DESCRIPTOR), BluetoothGattDescriptor.PERMISSION_WRITE | BluetoothGattDescriptor.PERMISSION_READ);
    shotDetectedCharacteristic.addDescriptor(gD);
    service.addCharacteristic(shotDetectedCharacteristic);

    displayCharacteristic = new BluetoothGattCharacteristic(
            UUID.fromString("43FF0002-2BE5-46FE-9162-1D2905728B9B"),
            BluetoothGattCharacteristic.PROPERTY_BROADCAST | BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE,
            BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE);
    service.addCharacteristic(displayCharacteristic);

    gattServer.addService(service);
}

private BluetoothGattServerCallback gattServerCallback = new BluetoothGattServerCallback() {
    @Override
    public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
        super.onConnectionStateChange(device, status, newState);
        if (newState == 2){
            connected_device = device;
            advertiser.stopAdvertising(new AdvertiseCallback() {});
        }
        Log.i("chase", "Connection state changed: "+newState);
    }
    @Override
    public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
        super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
        long millis = System.currentTimeMillis();
        characteristic.setValue( ""+millis );
        Log.i("chase", "reading: "+millis);
        gattServer.sendResponse(device, requestId, 2, offset, characteristic.getValue());
    }
    @Override
    public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
        Log.i("chase", "characteristic write request");
        super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
        characteristic.setValue(value);
        gattServer.sendResponse(device, requestId, 2, offset, characteristic.getValue());
        if (characteristic == displayCharacteristic){
            //TODO: UPDATE DISPLAY
            Log.i("chase", "display write request: "+value.toString());
        }else
            Log.i("chase", "char write request: "+value.toString());
    }
    ...
}

The reason I think the problem is on the iOS side is that I've downloaded the nrf Connect app and am able to successfully write to the android characteristic using nrf connect. Or at least get to the log statements in BluetoothGattServerCallback#onCharacteristicWriteRequest. However, I have also written apps interfacing to BLE chips and I've never had trouble with just writing NSData with [p writeValue:d forCharacteristic:c type:CBCharacteristicWriteWithResponse]; Thanks in advance for any help or ideas of how to troubleshoot this.

Update: When I say that it doesn't work. I don't get any error message at all. [p writeValue:d forCharacteristic:c type:CBCharacteristicWriteWithResponse]; is called, and the android side doesn't have any call to BluetoothGattServerCallback#onCharacteristicWriteRequest and the ios side doesn't have any callback to -(void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;.

However, I did get something interesting. If I comment out the ios line: //[peripheral setNotifyValue:YES forCharacteristic:self.shot_detected_characteristic]; Then I do get a call to android's onCharacteristicWriteRequest and I do get a callback to didWriteValueForCharacteristic (I get an error message, that it couldn't be written, but I think I can deal with that. At least I am getting something). I am subscribing to notifications on a different characteristic, but on the same service and for some reason that is preventing me from writing to data on this other characteristic.

Chase Roberts
  • 9,082
  • 13
  • 73
  • 131
  • Where and exactly what is your error? All I see is that you are "having trouble doing a simple writeValue" and that doesn't say much to me... – Emil Mar 24 '19 at 21:41
  • I've updated the question with some more detail. Basically, I am not getting any error message at all. I'm calling `writeValue:forCharacteristic:onPeripheral:` and not receiving any callbacks. – Chase Roberts Mar 25 '19 at 09:56
  • @ChaseRoberts, did you eventually find a solution? – Keselme Jul 07 '20 at 07:02
  • @Keselme, this was a long time ago, but if I remember correctly, you need to override all of the `BluetoothGattServerCallback` methods and not just the two that I had. I think that once I had added all of the callback methods then it started working. – Chase Roberts Jul 09 '20 at 03:44

0 Answers0