How to correctly implement didReceiveWriteRequests? iOS6 CoreBluetooth - ios

I implement iOS CoreBluetooth client & server for sending data
client site
[self.connectedPeripheral writeValue:mainData forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
and
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
{
NSString *s= [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
NSLog(#"didWriteValue characteristic.value: %# ", s);
}
and server site
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests
{
NSData *res= [[NSString stringWithFormat:#"Hello"] dataUsingEncoding:NSUTF8StringEncoding];
[self.peripheral updateValue:res
forCharacteristic:self.writeCharacteristic
onSubscribedCentrals:nil];
[peripheral respondToRequest:aReq withResult:CBATTErrorSuccess];
}
however, client can't receive any data.
Any idea?
Thanks for your help.

According to Apple's doc on CoreBluetooth, you should use:
- (void)respondToRequest:(CBATTRequest *)request withResult:(CBATTError)result;
to reply to the central.This is also a choice when you receive a reading request

To use:
- (BOOL)updateValue:(NSData *)value forCharacteristic:(CBMutableCharacteristic *)characteristic onSubscribedCentrals:(NSArray *)centrals;
you first need to subscribe to it using
- (void)setNotifyValue:(BOOL)enabled forCharacteristic:(CBCharacteristic *)characteristic;
from client side.

Related

How to write value to BLE device from iPhone using blemanager?

- (void)bleManagerPeripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
NSString * str = #"0A0B";
NSString * hexStr = [NSString stringWithFormat:#"%#",[NSData dataWithBytes:[str cStringUsingEncoding:NSUTF8StringEncoding]
length:strlen([str cStringUsingEncoding:NSUTF8StringEncoding])]];
for(NSString * toRemove in [NSArray arrayWithObjects:#"<", #">", #" ", nil])
hexStr = [hexStr stringByReplacingOccurrencesOfString:toRemove withString:#""];
NSData *data = [hexStr dataUsingEncoding:NSUTF8StringEncoding];
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
[peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
I want to send a message or command to BLE machine like Tea-Maker Machine but device is not responding to my message and I am getting following error:
Error Domain=CBATTErrorDomain Code=3 "Writing is not permitted." UserInfo={NSLocalizedDescription=Writing is not permitted.}
My question is how to send data from our iOS app (iPhone) to the BLE machine by using blemanager?

Write to multiple bluetooth Peripherals from Central

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

didWriteValueForCharacteristic getting null in Characteristic value

I am sending the date from to the peripheral by using the below code.
NSString* alternative = [NSString stringWithFormat:#"hello"];
NSData *dataToSend = [alternative dataUsingEncoding:NSUTF8StringEncoding];
[self.discoveredPeripheral writeValue:dataToSend forCharacteristic:self.discoveredCharacteristics type:CBCharacteristicWriteWithResponse];
While sending called the delegate method.
- (void)peripheral:(CBPeripheral *)iPeripheral didWriteValueForCharacteristic:(CBCharacteristic *)iCharacteristic error:(NSError *)iError {
NSLog(#"didWriteValue characteristic.value: %# ", iCharacteristic.value);
NSLog(#"Error = %#", iError);
}
But, here I am getting null value.
I can receive in peripheral app also in,
- (void)peripheralManager:(CBPeripheralManager *)iPeripheral didReceiveWriteRequests:(NSArray *)iRequests {
[iPeripheral respondToRequest:[iRequests objectAtIndex:0] withResult:CBATTErrorSuccess];
CBATTRequest *aRequest = iRequests[0];
NSData *aData = aRequest.value;
NSDictionary *aResponse = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:aData options:NSJSONReadingMutableContainers error:nil];
NSLog(#"Received Data = %#", aResponse);
}
receiving result value is same null.
- (void)peripheral:(CBPeripheral *)iPeripheral didWriteValueForCharacteristic:(CBCharacteristic *)iCharacteristic error:(NSError *)iError
The couldn't recognize the issue while sending the data to peripheral.
Any answer will help me a lot

iOS central writing is not permitted

I am a ios developer.
I can take a value from arduino sensor. But i cannot send a message by using the following the method.
[peripheral writeValue:dataToWrite forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
"dataToWrite" value is alloc by using NSString*
NSString* data = #"1";
NSData* dataToWrite = [data dataUsingEncoding:NSUTF8StringEncoding];
and the following code is full code of "DiscoverCharacteristics in service"
//DISCOVER CHAR
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
if (error) {NSLog(#"DISCOVER_CHAR - Error");return;}
NSString* data = #"1";
NSData* dataToWrite = [data dataUsingEncoding:NSUTF8StringEncoding];
for (CBCharacteristic * characteristic in service.characteristics) {
NSLog(#"DISCOVER_CHAR - Characteristic : %#",characteristic);
[peripheral writeValue:dataToWrite forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
}
In this point, I want to summarize my question.
My Question is
"Even i used the [Peripheral writeValue:forCharacteristic:type] Method. why an error message is shown in log monitor? " Like "Writing is not permitted."
Do i need to get some permission to writing the message for an Arduino?
OR I have to change my following code?
OR I have a problem in Arduino(Acutally, Arduino can get a message from other device... so, Arduino source code is fine. maybe...)
NSString* data = #"1";
NSData* dataToWrite = [data dataUsingEncoding:NSUTF8StringEncoding];
I'm a bit of a bluetooth noob. So it's probably something obvious I've overlooked but any help would be much appreciated.
Thank you!:)
Thank you a lot!!!!!!! Paulw11. Finally, i sent a data from iphone to Arduino. what i checked properties of my characteristic is
<CBCharacteristic: 0x13564a680, UUID = FFE1, properties = 0x16, value = (null), notifying = NO>.
My characteristic's properties = 0x16. but i can not find a 0x16 in enum of properties. Still, I don't know the 0x16 meaning. Someone said that "Values representing the possible properties of a characteristic. Since characteristic properties can be combined, a characteristic may have multiple property values set."
URL: Interpret Characteristic Properties (iOS and BLE) .
Anyway I could find a solution by PaulW11 help! How i can do it? Here is my way of solution.
At first, My method is [peripheral writeValue:dataToWrite forCharacteristic:characteristic *type:CBCharacteristicWriteWithResponse]*;.
The problem code is type field. I had to understand the type. The type has a meaning of write, read and so on. So, I changed the type from CBCharacteristicWriteWithResponse to CBCharacteristicPropertyWrite. That is what i did.
[peripheral writeValue:dataToWrite forCharacteristic:characteristic *type:CBCharacteristicPropertyWrite*];
Thank you again! Paulw11
try to print error inside following method:
-(void)peripheral:(CBPeripheral *)peripheral
didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error
{
NSLog(#"%#",error.localizedDescription);
}
If the error is "Writing is not permitted", then you might not writing the data correctly.Convert the string value in this format
NSData* data = [#"string_to_write" dataUsingEncoding:NSASCIIStringEncoding];
And try this:
[self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithoutResponse];
Hope it works!!

iPad GCDAsyncSocket doesn't read

I really need some help with my project...
I need to exchange data with my server written in Java. I tried using GCDAsyncSocket, and I can send message to server, read it on server, but when server sends response to client, I can't (don't know how to) read it on client. Here is part of my code:
- (void) someMethod{
NSError *err = nil;
asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
if(![asyncSocket connectToHost:#"localhost" onPort:7777 error:&err]){
// If there was an error, it's likely something like "already connected" or "no delegate set"
NSLog(#"I goofed: %#", err);
}
NSString *requestStr = #"<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><root><service>1</service><type>1</type><userProperties><username>ivo</username></userProperties></root>";
NSData *requestData = [requestStr dataUsingEncoding:NSUTF8StringEncoding];
[asyncSocket writeData:requestData withTimeout:-1.0 tag:0];
[asyncSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:1.0 tag:0];
[asyncSocket disconnectAfterWriting];
}
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{
if (tag == 0)
NSLog(#"First request sent");
else if (tag == 2)
NSLog(#"Second request sent");
}
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#",str);
}
Please help, if there is another way I am willing to try as I am getting desperate...
I see that you're sending XML, with no particular terminator at the end of your request data, yet you're expecting the server to send a response terminated by a \r\n?
What does the protocol specify?
Sending and receiving data over tcp is a common cause of confusion because tcp is stream based. It has no concept of individual reads/writes. It treats all data as conceptually a never ending stream. The protocol dictates message boundaries. For a better explanation, see the "Common Pitfalls" article from GCDAsyncSocket's wiki:
https://github.com/robbiehanson/CocoaAsyncSocket/wiki/CommonPitfalls
I think it will help explain a lot.

Resources