11
votes

I am trying to develop iOS app, which is detecting BLE device and need to invoke a write command. I can successfully connect to BLE device and discover its service and characteristics. But I am getting an Unknown Error during the write data on connected peripheral. Below is the code what I have written:

Discovered Characteristic:

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
  NSArray *characteristics = [service characteristics];

  if (peripheral != self.peripheral) {
    NSLog(@"Wrong Peripheral.\n");
    return ;
  }

  if (error != nil) {
    NSLog(@"Error %@\n", error);
    return ;
  }

  for (CBCharacteristic *characteristic in characteristics) {

    if ([[characteristic UUID] isEqual:RWT_POSITION_CHAR_UUID]) {
      self.positionCharacteristic = characteristic;
    }
  }
}

Here is the function for write data on BLE device

- (void)writeCommand{

    // See if characteristic has been discovered before writing to it
    if (!self.positionCharacteristic) {
        return;
    }
    NSString *cmd1=@"CQ+WHORU\r";

    NSData *dataToWrite = [cmd1 dataUsingEncoding:NSUTF8StringEncoding];

    CBCharacteristic *chr = self.positionCharacteristic;
    NSLog(@"%@,%lu...%@",chr.UUID,chr.properties,chr);

    NSInteger dataSize = [[NSByteCountFormatter stringFromByteCount:dataToWrite.length countStyle:NSByteCountFormatterCountStyleFile] integerValue];
    if (dataSize > 130) {
        NSLog(@"Cannot send more than 130 bytes");
    }
    else
    {
        [self.peripheral writeValue:dataToWrite forCharacteristic:self.positionCharacteristic type:CBCharacteristicWriteWithResponse];
    }
}

- (void)peripheral:(CBPeripheral *)peripheral
didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
         error:(NSError *)error
{
    if (error)
    {
        NSLog(@"Error writing characteristic value: %@", [error localizedDescription]);
    }
    else
    {
        NSLog(@"Successfully writing characteristic value");
    }
}

In "didWriteValueForCharacteristic" delegate method I am getting below error:

Error writing characteristic value: Unknown error.

What could be possibly be the reason for this error?

Also If I am trying to define characteristic for sending data on BLE device like:

CBMutableCharacteristic *writeChar = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:@"ffe1"] properties:CBCharacteristicPropertyWriteWithoutResponse|CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsWriteable|CBAttributePermissionsReadable];
int8_t cmd = *[cmd1 UTF8String];

NSData *data = [NSData dataWithBytes:&cmd length:sizeof(cmd)];

NSInteger dataSize = [[NSByteCountFormatter stringFromByteCount:dataToWrite.length countStyle:NSByteCountFormatterCountStyleFile] integerValue];
if (dataSize > 130) {
    NSLog(@"Cannot send more than 130 bytes");
}
else
{
    [self.peripheral writeValue:dataToWrite forCharacteristic:writeChar type:CBCharacteristicWriteWithResponse];
    //        [self.peripheral writeValue:dataToWrite forCharacteristic:writeChar type:CBCharacteristicWriteWithResponse];
}

then I am getting this error:

**CoreBluetooth[WARNING] CBMutableCharacteristic: 0x1552a270 UUID = Unknown (<ffe1>),Value = (null), Properties = 0x14, Permissions = 0x3, Descriptors = (null), SubscribedCentrals = (
)> is not a valid characteristic for peripheral <CBPeripheral: 0x15549930 identifier = FC71890D-9F71-5E16-086D-D491E1EF7599, Name = " DEMOBLE1", state = connected**
3
Have you check the properties of the characteristic to confirm that it supports writeWithResponse?Paulw11
try with characteristic write option * CBCharacteristicWriteWithoutResponse*, it may be condition that BL device does not support * CBCharacteristicWriteWithResponse*Pawan Rai
Yes, I was try CBCharacteristicWriteWithoutResponse, but its not working. In android development, If I send above command then it reply some value in updateValue delegate method, but I am not getting any value. it is mean write functionality doesn't working.Prashant Chaudhari

3 Answers

11
votes

When you write the data you're requesting a response (CBCharacteristicWriteWithResponse).

The write will fail if the characteristic you're writing to does not have the property CBCharacteristicPropertyWrite set, but only supports writes without response (CBCharacteristicPropertyWriteWithoutResponse).

if ((self.positionCharacteristic.properties & CBCharacteristicPropertyWrite) == CBCharacteristicPropertyWrite) {
    // Responses are available, so write with response.
    [self.peripheral writeValue:dataToWrite forCharacteristic:self.positionCharacteristic type:CBCharacteristicWriteWithResponse];
}
else if ((self.positionCharacteristic.properties & CBCharacteristicPropertyWriteWithoutResponse) == CBCharacteristicPropertyWriteWithoutResponse) {
    // Responses are not available.
    // Write with response is going to fail, so write without response.
    [self.peripheral writeValue:dataToWrite forCharacteristic:self.positionCharacteristic type:CBCharacteristicWriteWithoutResponse];
}
1
votes

I have seen this error while developing both the iOS App (as Central) and the BLE Device firmware (as Peripheral). This would occur when I changed/modified the characteristics on the BLE peripheral.

iOS seems to cache information about the services and characteristics after a connection. I was able to solve this by updating the Bluetooth Address of the peripheral whenever I would build/download new firmware to the Device.

0
votes

First of all, you are using CBCentralManager, right?

Make sure you are doing it in the right way:

  1. Use CBCentralManager

    self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    
  2. Detect when a peripheral connected

    - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
    {
    
        if (connectedDevice) // make sure you disconnect previous device
            [self.centralManager cancelPeripheralConnection:connectedDevice];
    
        connectedDevice = [peripheral retain];
        connectedDevice.delegate = self;
        [connectedDevice discoverServices:nil]; // this will discover all kind of services
    }
    
  3. If something was wrong during the connection, you will find here (for example, low battery)

    - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
        NSLog(@"Did fail to connect: %@",error.description);
        connectedDevice = nil;
    }
    
  4. Something went wrong with a successfully connected BLE (for example, low battery, user turned off the device)

    - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
        NSLog(@"Disconnected: %@",error.description);
        connectedDevice = nil;
    }
    
  5. Here, you can find the available services

    - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
        NSLog([NSString stringWithFormat:@"%@",[advertisementData description]]);
    }
    

6.

    - (void)centralManagerDidUpdateState:(CBCentralManager *)central{
        NSString *messtoshow;

        switch (central.state) {
            case CBCentralManagerStatePoweredOn:
            {
                [NSString stringWithFormat:@"Bluetooth is currently powered on and available to use."];

                CBUUID *heartRate = [CBUUID UUIDWithString:HRM_SERVICE_UUID];
                CBUUID *batteryLevel = [CBUUID UUIDWithString:BATTERY_LIFE_SERVICE_UUID];

                NSDictionary *scanOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];

                [self.centralManager scanForPeripheralsWithServices:[NSArray arrayWithObjects:heartRate,batteryLevel,nil] options:scanOptions];
                break;
            }

        }
    }
  1. From now on, you can implement the CBPeripheralDelegate methods

    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
    {
        for (CBService *service in peripheral.services) {
            NSLog(@"Discovered service: %@", service.UUID);
            [peripheral discoverCharacteristics:nil forService:service];
        }
    }
    
    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
    {
        if ([service.UUID isEqual:[CBUUID UUIDWithString:HRM_SERVICE_UUID]])  {
            for (CBCharacteristic *aChar in service.characteristics)
            {
                if ([aChar.UUID isEqual:[CBUUID UUIDWithString:HRM_MEASUREMENT_CHARACTERISTIC_UUID]]) {
                    [connectedDevice setNotifyValue:YES forCharacteristic:aChar];
                }
            }
        }
    
        if ([service.UUID isEqual:[CBUUID UUIDWithString:BATTERY_LIFE_SERVICE_UUID]])  {
            for (CBCharacteristic *aChar in service.characteristics)
            {
                if ([aChar.UUID isEqual:[CBUUID UUIDWithString:BATTERY_LEVEL_CHARACTERISTIC_UUID]]) {
                    [connectedDevice readValueForCharacteristic:aChar];
                }
            }
        }
    }
    
    - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
    {
        NSError* err = nil;
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:HRM_MEASUREMENT_CHARACTERISTIC_UUID]]) {
                NSData *data = [characteristic value];
        }
    
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:BATTERY_LEVEL_CHARACTERISTIC_UUID]]) { 
    
        }
    }
    

This should work fine. I've implemented it in the same way, and there isn't any problem. I hope this helps.