I am running an app that communicates from my Mac to my iPhone via bluetooth and vice versa. It is all working perfectly (with two generated characteristics one from Mac to iPhone and one from iPhone to Mac) apart from, occasionally my characteristic from mac to iPhone is not being updated and I think this is due to the charachteristic on the mac being set to nil for no apparent reason.
So to update my char on my Mac I do:
- (void)updateValueForCharacteristic:(int)sendID {
NSLog(#"sent %d", sendID);
if (self.characteristic != nil) {
dispatch_block_t block = ^(void) {
NSData *value = [NSData dataWithBytes:&sendID length:sizeof(sendID)];
[_managerout updateValue:value forCharacteristic:self.characteristic onSubscribedCentrals:nil];
};
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, block);
}else{
//why is this sometimes being called?!
}
}
and to receive on the iPhone I do:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error != nil) {
NSLog(#"Error updating value: %#", error.localizedDescription);
return;
}
CBUUID *characteristicUUID = [CBUUID UUIDWithString:_kCharacteristicUUIDin];
NSInteger rec = 0;
[characteristic.value getBytes:&rec length:sizeof(rec)];
NSLog(#"recieved %ld", (long)rec);
if ([characteristic.UUID isEqual:characteristicUUID]) {
NSLog(#"officially recieved %ld", (long)rec);
//handle char (sometimes not noticing)
}
}
Is there anyway that I can verify that my iPhone has received the char. Also should I scrap this technique and try with writing (rather than updating) the char or maybe try with multiple chars rather than just the one?
I really need to make sure that the iPhone gets it or else the program is useless.
update
I have added an else block to if (self.characteristic != nil) { and the else block is being called when my char isn't being updated!!! But I can't find for the life of my why the self.characteristic is being set to nil?! And also how should I go about re-creating the characteristic after?
project
Related
Sending data through the iOS (writeValue: forCharacteristic: type :) method works fine without error, but the value does not change. Do you know why?
> 2017-06-27 14:53:54.846963+0900 BluetoothSample[12662:5477661] Did
> write characteristic <CBCharacteristic: 0x1700ad500, UUID =
> 2A588021-4FB2-40F5- 8204-85315DEF11C5, properties = 0xA, value =
> <1357>, notifying = NO>
uint8_t val = 123;
NSData * valData = [NSData dataWithBytes:(void*)&val length:sizeof(val)];
[self.discoveredPeripheral writeValue:valData forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
- (void) peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error) {
NSLog(#"didWriteValueForCharacteristic With error: %#", [error localizedDescription]);
self.receivePaketDataTextView.text = [[NSString alloc] initWithFormat:#"%#",[error localizedDescription]];
return ;
}
self.receivePaketDataTextView.text = [[NSString alloc] initWithFormat:#"%#",characteristic.value];
}
When I try to write, only the same logs are printed.
Try:
[self.discoveredPeripheral writeValue: valData
forCharacteristic:self.usedCharacteristic
type:CBCharacteristicWriteWithoutResponse];
Use type: CBCharacteristicWriteWithoutResponse
Also check if you have set the notifier
[self.discoveredPeripheral setNotifyValue:YES forCharacteristic:self.usedCharacteristic];
Before hand in your delegates.
On your iOS central the value of the characteristic in didWriteValueForCharacteristic does not reflect the change you just wrote.
You need to issue an explicit read to get the new value.
This is by design, since there is no guarantee that the value you have just written is the current value of the characteristic; only the peripheral can give you the current value.
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
dispatch_async(dispatch_get_main_queue(), ^{
NSData *data = characteristic.value;
uint8_t *array = (uint8_t*) data.bytes;
cadenceValue = [CharacteristicReader readUInt8Value:&array];
self.cadence.text = [NSString stringWithFormat:#"%d", cadenceValue];
});
}
How to get cadence from bLE (Bluetooth low energy) device in swift 2. I am unable to find exact code for this. For this didUpdateValueForCharacteristic delegate method is called.
I have a code of nRF Toolbox but it is in objective c or swift 3 but my project is in swift 2. I tried to call objective c method using bridging header but it was always returned 0 cadence.
I'm not sure the definition of CharacteristicReader, but you might try:
[CharacteristicReader readUInt8Value:&array];
cadenceValue = Int(array[0])
self.cadence.text = [NSString stringWithFormat:#"%d", cadenceValue];
The above assumes that the result of the call to readUInt8Value gets put into the array of UInt8 objects, and the cadence value is in the first byte of the array. You could also check if the proper value is in other bytes by trying cadenceValue = Int(array[1]) or cadenceValue = Int(array[2]), etc.
I´m new in iOS Programming and i´m trying to receive Data from a BLE device. I´m able to connect to the device and send data to the device. The problem is receiving data from the BLE Device.
Im using this as BLE Base:
LGBluetooth
Problem:
#property (nonatomic,strong) NSString* recievedData; //data recieved from Peripheral
- (void)RecieveDataBLE:(LGPeripheral *)peripheral :(NSString*)Service_UUID :(NSString*)Characteristic_UUID {
//Function to read Data from BLE Device
[LGUtils readDataFromCharactUUID:Characteristic_UUID serviceUUID:Service_UUID
peripheral:peripheral
completion:^(NSData *data, NSError *error) {
NSLog(#"Data : %s Error : %#", (char *)[data bytes], error);
recievedData = [NSString stringWithUTF8String:[data bytes]];
}
- (IBAction)sendDOWN:(id)sender {
MessageLabel.hidden = NO;
[self RecieveDataBLE:mBuddy:SERVICE_UUID_DEVICE_INFORMATION:CHARACTERISTIC_UUID_MANUFACTURER];
MessageLabel.text = [NSString stringWithFormat:#"%#",recievedData]; //Output is always old!!
}
After the action sendDown is called, data should be read via the ReceiveDataBle function. This one will then process the data. The problem is, ReceiveDataBle is always too late, and does not receive the information immediately, but after a certain time. A callback function returns the data to ReceiveDataBle. So when the action SendDown is called the Data from the last call appears in the MessageLabel!!
The question is, how can I ensure that I have the newest data available, and not outdated data?
**You can use this type when show updated recievedData **
-(IBAction)sendDOWN:(id)sender
{
MessageLabel.hidden = NO;
[LGUtils readDataFromCharactUUID:Characteristic_UUID
serviceUUID:Service_UUID
peripheral:peripheral
completion:^(NSData *data, NSError *error) {
NSLog(#"Data : %s Error : %#", (char *)[data bytes], error);
recievedData = [NSString stringWithUTF8String:[data bytes]];
MessageLabel.text = [NSString stringWithFormat:#"%#",recievedData];
}
}
I've searched SO for help on this and haven't found anything that will answer, address or get me pointed in the right direction so I've decided to post my issue.
I have a BT Central app running on an Apple TV. I have a peripheral app running on an iPhone and iPad.
The central app is able to connect to both peripheral devices just fine. I'm able to transfer all kinds of data to the central app and have control over all of the phases of the session (didDiscoverPeripheral, didDiscoverServices, didDiscoverChracteristics, etc.) All the delegate methods on both central and peripheral sides are behaving exactly as they should.
When the central app connects to a peripheral and it discovers the "writable" characteristic it sends (writes) an NSString to the peripheral with something like "Hi iPad, you've connected to central" or "Hi iPhone you've connected to central". In doing this I know that everyone is connected, discovered, processed and a reference to the peripherals is saved. None of this is an issue and behaves exactly as is documented by Apple.
On the central app I have a UIButton that performs a write to all of the connected peripherals. I attempt to loop through the connected peripherals and write something to each one inside the loop. Unfortunately only the last connected peripheral receives the written data.
I have a nice NSDictionary of all of the peripheral information and object that I enumerate through. I've even based the loop on the
retrieveConnectedPeripheralsWithServices method. My peripherals all use a custom class for their delegate so I know I'm not crossing the same delegate with multiple peripherals.
Inside the loop I can see that the peripheral is connected, the characteristic I'm targeting has writeWithResponse properties and write permissions. No reference to the peripheral has been lost or released. Everything maps out and looks great.
I just can't write to all the connected peripherals from inside a loop. I've tried queuing up the writes in an NSOperation and or dispatch_async in case it's a timing thing but nothing is working.
If the iPad is the last connect peripheral it gets the write. If the iPhone connect last then it gets the write. The last connected peripheral is the only clue I've got to go on but I'm just not seeing the problem.
At this point I'm out of sticks and carrots and several days of googling and SO searching. Sorry for the long post but I wanted to explain and also show that I'm not just asking out of laziness but have sincerely tried everything I know.
Thanks for any help.
Added relevant code:
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *) advertisementData RSSI:(NSNumber *)RSSI {
//it's in range - have we already seen it?
if([self findPeripheralMatching:peripheral.identifier] == nil) {
//hack if the advertisingData local name or the peripheral GATT name is NULL
NSDictionary *dict = [self cleanupAdvertisementData:peripheral advertisementData:advertisementData];
if(dict == nil) {
[self stop];
[self start];
return;
}
//save a local copy of the peripheral, so CoreBluetooth doesn't get rid of it
XCBluetoothPeripheralDictionary *obj = [[XCBluetoothPeripheralDictionary alloc] init];
obj.peripheral = peripheral;
obj.advertisementData = dict;
[self.peripheralDictionary setObject:obj forKey:[peripheral.identifier UUIDString]];
//and connect is not connected...
if(peripheral.state == CBPeripheralStateDisconnected) {
[self.centralManager connectPeripheral:peripheral options:nil];
}
} //findPeripheralMatching
} //didDiscoverPeripheral
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
//make sure we get the discovery callbacks
XCBluetoothPeripheral *pd = [[XCBluetoothPeripheral alloc] initWithDelegate:self];
pd.subscriptionDictionary = self.subscriptionDictionary;
peripheral.delegate = pd;
//save a copy of the XCPeripheral object
[self.peripheralDictionary objectForKey:[peripheral.identifier UUIDString]].delegate = pd;
//discover and search only for services that match our service UUID
[peripheral discoverServices:#[[CBUUID UUIDWithString:self.serviceUUID]]];
//notify the delegate we connected
XC_SelectorAssert(#selector(bluetoothCentralDidConnect:), self.delegate)
if (self.delegate && [self.delegate respondsToSelector:#selector(bluetoothCentralDidConnect:)]) {
XCBluetoothPeripheralDictionary *dict = [self.peripheralDictionary objectForKey:[peripheral.identifier UUIDString]];
[self.delegate bluetoothCentralDidConnect:dict];
} else {
NSAssert(NO, XCMissingSelectorForProtocol);
}
} //didConnectPeripheral
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
if(error) {
[self callbackError:error];
return;
}
//again, we loop through the array, and if the characteristic matches
//whats in the subscriptionDictionary then we subscribe to it
for (CBCharacteristic *characteristic in service.characteristics) {
if([self isDesiredCharachteristic:characteristic.UUID]) {
[peripheral discoverDescriptorsForCharacteristic:characteristic];
if(characteristic.properties & CBCharacteristicPropertyNotify) {
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
if(characteristic.properties & CBCharacteristicPropertyRead) {
}
if(characteristic.properties & CBCharacteristicPropertyWrite) {
NSLog(#"Writing value to %# - %#", peripheral.identifier, peripheral.name);
NSString *string = [NSString stringWithFormat:#"%# connected to %#",
peripheral.name,
[XCUtilities deviceName]];
[peripheral writeValue:[string dataUsingEncoding:NSUTF8StringEncoding]
forCharacteristic:characteristic
type:CBCharacteristicWriteWithResponse];
}
if(characteristic.properties & CBCharacteristicPropertyWriteWithoutResponse) {
}
[self.subscriptionDictionary objectForKey:(NSString *)characteristic.UUID].characteristic = characteristic;
} //if isMatching
} //for CBCharacteristic
} //didDiscoverCharacteristicsForService
The following method is called from an IBAction
-(void)writeValueToCharacteristic:(CBUUID *)cbuuid value:(NSString *)string {
//get a reference to the characteristic we specified
CBCharacteristic *chr = [self findCharacteristicMatching:cbuuid];
XC_CBCharacteristicAssert(chr)
//enumerate through the discovered peripherals
[self.peripheralDictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, XCBluetoothPeripheralDictionary *obj, BOOL *stop){
XC_CBPeripheralAssert(obj.peripheral)
if(obj.peripheral.state == CBPeripheralStateConnected) {
//check the properties
if(chr.properties & CBCharacteristicPropertyWriteWithoutResponse ||
chr.properties & CBCharacteristicPropertyWrite) {
NSLog(#"Writing value to:\n%#\n%#\n%#\n%#\n%#",
key,
obj.advertisementData,
obj.peripheral.name,
obj.peripheral.delegate,
obj.peripheral);
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
[obj.peripheral writeValue:data forCharacteristic:chr
type:CBCharacteristicWriteWithResponse];
} else {
[self localError:XC_BTErrorPermissionWritable description:XCErrorWritingCharacteristic];
}
} //is connected
}];
} //writeValueToCharacteristic
I would think that if something were wrong with the way I'm saving the peripherals or my custom dictionary or the way I'm using this stuff then my writes would fail for all peripherals and not just one of the two I'm testing with from inside a loop. And I know I'm connected and discovered and all is well because when central initially processes these peripherals it writes to each one as sort of a confirmation that they are indeed ready to go.
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests {
[peripheral respondToRequest:[requests objectAtIndex:0] withResult:CBATTErrorSuccess];
CBATTRequest *aRequest = requests[0];
NSData *aData = aRequest.value;
NSString *string = [[NSString alloc] initWithData:aData encoding:NSUTF8StringEncoding];
[self logToDelegate:string];
}
-(void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
if(error) {
[self callbackError:error];
return;
}
[self logToDelegate:#"didWriteValueForCharacteristic"];
} //didWriteValueForCharacteristic
I use the CC2541 as the peripheral and iPad mini as the Central. I transfer the data every single second through serial port(the baud rate is 19200) from CC2541 as notify. (Also I tried to transfer data in every 100 ms.It seemed to be same in accuracy)
Here's my code:
- (void) peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (error)
{
NSLog(#"Error receiving notification for characteristic %#: %#", characteristic, error);
return;
}
//NSLog(#"Received data on a characteristic.");
if (characteristic == self.rxCharacteristic)
{
NSData *data = [characteristic value];
//NSString* string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSString* string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[self.delegate didReceiveData:string];
}
else if ([characteristic.UUID isEqual:self.class.hardwareRevisionStringUUID])
{
NSString *hwRevision = #"";
const uint8_t *bytes = characteristic.value.bytes;
for (int i = 0; i < characteristic.value.length; i++)
{
NSLog(#"%x", bytes[i]);
hwRevision = [hwRevision stringByAppendingFormat:#"0x%02x, ", bytes[i]];
}
//[self.delegate didReadHardwareRevisionString:[hwRevision substringToIndex:hwRevision.length-2]];
}
}
I tried single-step debug, but it seemed that this code was right. And it's wrong in the transmission from peripheral(because the "string" was wrong when I debugged this). I got about 85% the right data. And 15% data was wrong.
the right data(string) is "12399921" and it will notify as 0x3132333939393231. Often the wrong data will occurs continuously such as 0x31323339393932 and 0x3132E739393231 , 0x31323339393231 and 0x249ACACACA928AFE ...
Maybe it has a way to correct them because it seems that it has some regular there... Or is there any way to avoid the wrong data transmission from the peripheral. Either way will be OK.
Thanks in advance.
Sorry...
I think I found what's wrong by myself.
The accuracy will be much better when baud rate is set to 57600 not 19200.
But it seemed the same for the Android app(it still runs well even no matter the baud rate is 19200 or 57600).