Reading Long Characteristic Values Using Corebluetooth

CoreBluetooth Characteristic value

The specification limits the maximum length of a characteristic value in 512 octets (Bluetooth specification V4.0 Vol 3. Part F 3.2.9). On the central side you start a read request with the readValueForCharacteristic: API. Then on the peripheral side you receive the corresponding callback:

CBMutableCharacteristic *characteristic = // the characteristic with long data

- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
// Ensure offset is in the valid range
if (request.offset > characteristic.value.length) {
// respond with error
[self.peripheralManager respondToRequest:request withResult:CBATTErrorInvalidOffset];
return;
}

NSRange range = NSMakeRange(request.offset, characteristic.value.length - request.offset);
request.value = [characteristic.value subdataWithRange:range];

[self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
}

The callback will be called with increasing offset values as long as the response is not less than the Maximum Transferrable Unit (MTU). The MTU is negotiated by the system and you have no way to query it in advance. For this reason the range is set up to stretch all the way to the data length so the system can decide how much of the data it is going to send to the central. By default it is 20 but iOS 7 has some tweaks that enable greater sizes. Check out the WWDC videos for more detail.

With this info you should be able to figure out a way to expose your data. Keep in mind that the more services and characteristics you have, the longer the discovery will take. Using long reads and long writes is just a convenience.

Reading a BLE Peripheral Characteristic and checking its value?

Updated For Swift3

After you execute that method, the delegate of your peripheral is going to asynchronously receive the peripheral(_:didUpdateValueFor:error:) method. In that method you can query the value of the passed characteristic parameter. value will be an NSData which you can pull the bytes out of. E.g.

// MARK: - CBPeripheralDelegate
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if let e = error {
print("ERROR didUpdateValue \(e)")
return
}
guard let data = characteristic.value else { return }
...
}

The value method actually returns an Optional around the expected Data, so a let guard is the way to go.

Usually a characteristic will have a simple value encoded in it's up-to-20-byte Data payload. E.g. maybe it's a simple UInt16 counter. To

To convert between these Data glumps and meaningful numbers, have a look at the answer to round trip Swift number types to/from Data (I've included my own implementation of that below).

So for example, if you know that the characteristic of interest is some counter that is a meant to be extracted as a UInt16, I would fill out the above example with something like:

// MARK: - CBPeripheralDelegate
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if let e = error {
print("ERROR didUpdateValue \(e)")
return
}
guard let data = characteristic.value else { return }
print("counter is \(UInt16(data:data))")
}

// Data Extensions:
protocol DataConvertible {
init(data:Data)
var data:Data { get }
}

extension DataConvertible {
init(data:Data) {
guard data.count == MemoryLayout<Self>.size else {
fatalError("data size (\(data.count)) != type size (\(MemoryLayout<Self>.size))")
}
self = data.withUnsafeBytes { $0.pointee }
}

var data:Data {
var value = self
return Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
}
}

extension UInt8:DataConvertible {}
extension UInt16:DataConvertible {}
extension UInt32:DataConvertible {}
extension Int32:DataConvertible {}
extension Int64:DataConvertible {}
extension Double:DataConvertible {}
extension Float:DataConvertible {}

How to read information from core bluetooth device

Take a nice good read through the framework. if you have come this far you shouldn't have any problem finding 'discoverCharacteristics' and the peripheral delegate callback 'didDiscoverCharacteristic'. You need to know the UUID of the services and characteristics you want to discover and apply it to those methods.

Then you can read with 'readValueForCharacteristic' and the delegate callback 'didUpdateValueForCharacteristic'.

This is sent from my phone, so I will maybe edit a bit when I get to a computer. Hope it helps

New question:

[connectedPeripheral readValueForCharacteristic:wantedCharacteristic] 

and at peripheral delegate

- (void) peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{

NSLog(@"Characteristic value : %@ with ID %@", characteristic.value, characteristic.UUID);
[delegate characteristicValueRead:characteristic.value];
}

works for me



Related Topics



Leave a reply



Submit