I've got a strange problem using CoreBluetooth on iPhone 5C & iPhone 5S.
By scenario, I'm going to receive 83 bytes of data, chunked by 20 bytes (5 chunks in common).
And data are differs on iPhone 5S/5C (please see the dump below): chunk #4 substitutes chunk #3 under these devices.
But on iPad3 (with the 6.1 & 7.0.2 iOS) all is OK
// iPhone 5C, iOS 7.0.2
nRF UART[237:60b] Received data on a characteristic. 0x01=001,0x02=5B2226
nRF UART[237:60b] Received data on a characteristic. 400192,0x05=1.0.0,0x
nRF UART[237:60b] Received data on a characteristic. ,0x09=2,0x0a=0,0x0b=
nRF UART[237:60b] Received data on a characteristic. ,0x09=2,0x0a=0,0x0b=
nRF UART[237:60b] Received data on a characteristic. 100ˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇ
// iPad 3, iOS 6.1.3 && iPad 3, iOS 7.0.2
nRF UART[221:60b] Received data on a characteristic. 0x01=001,0x02=5B2226
nRF UART[221:60b] Received data on a characteristic. 400192,0x05=1.0.0,0x
nRF UART[221:60b] Received data on a characteristic. 07=2013.010,0x08=001
nRF UART[221:60b] Received data on a characteristic. ,0x09=2,0x0a=0,0x0b=
nRF UART[221:60b] Received data on a characteristic. 100ˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇ
// iPhone 4S, iOS 6.1.3
nRF UART[241:60b] Received data on a characteristic. 0x01=001,0x02=5B2226
nRF UART[241:60b] Received data on a characteristic. 400192,0x05=1.0.0,0x
nRF UART[241:60b] Received data on a characteristic. 07=2013.010,0x08=001
nRF UART[241:60b] Received data on a characteristic. ,0x09=2,0x0a=0,0x0b=
nRF UART[241:60b] Received data on a characteristic. 100ˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇ
My question is: how to get a normal data from 5S/5C devices? Perhaps, there are some tricks with CBCharacteristic or CBCentralManager?
Thanks.
EDIT
As #allprog suggested, here is the fragments of the code how I'm using CoreBluetooth.
This is the pretty basic approach:
// CBCentralManager init
self.cm = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
// Getting data chunks from CBCharacteristic in CBPeripheral's Delegate
- (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. %s", [[characteristic value] bytes]);
// ... the rest of code
}
If you're transferring large chunks of data and have to split it up over multiple packets, I would recommend adding some sort of ID to each packet. This will give you the additional information you need on the receiving side to determine whether a packet was dropped, received twice, etc. If you detect a packet was dropped, you'll likely want a way to re-request a packet by ID from the peripheral.
Edit: also, as mentioned in the comments, you can switch to indications to let the Bluetooth stack take care of your needs for guaranteed and non-duplicated delivery.
Related
I'm using GKSession, GKSessionDelegate to implement peer to peer bluetooth connectivity between a number of iX (iPod, iPad, iPhone, ...) devices. I want to display a list of the currently available/connected devices in range.
I'm currently relying on
- (void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state
to do this. Unfortunately, this method does not appear to be reliably called when a device goes out of range. Is there a "better" way to determine if a device is in range?
Some code:
- (void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state {
NSString* connectionStateString=
(state==GKPeerStateAvailable)?#"available":
(state==GKPeerStateUnavailable)?#"unavailable":
(state==GKPeerStateConnected)?#"connected":
(state==GKPeerStateDisconnected)?#"disconnected":#"connecting";
// Add the peer to the Dictionary
NSArray* details=[NSArray arrayWithObjects:[session displayNameForPeer:peerID],connectionStateString,nil];
[connectionPeers setObject:details forKey:peerID];
if (state == GKPeerStateAvailable) {
NSLog(#"Adding peerID:%#",peerID);
[session connectToPeer:peerID withTimeout:60];//'connect' to everything, so data can be sent
}
else if (state == GKPeerStateUnavailable || state==GKPeerStateDisconnected) {
[connectionPeers removeObjectForKey:peerID];
}
[self listPlayers];
}
GKSession is built on Bonjour over Bluetooth and I believe that your problem is that the bonjour service is still showing as active since Bonjour doesn't invalidate service advertisements when a peer has disconnected from the network. I think the mDNS records only expire either when the mDNS cache timeout occurs ( not something you can tweak ) or when the advertising peer manually invalidates the service.
I don't think GKSession is going to easily do what you want here via advertisement. Connected peers should however disconnect once they're out of Bluetooth range.
I am having a BLE device acting as peripheral and an iPhone4s as central.Both are connected via BluetoothLE connection.My question is that can i get to know from the peripheral end that it has been disconnected from the central.
Depending on the implementation of your peripheral (HCI interface or proprietary) you would either get a Disconnection Complete Event (Bluetooth Core Spec 4.0 - Volume 2 Part E section 7.7.5) or the proprietary equivalent event.
Could you provide some more information about the peripheral you are using?
If the Peripheral disconnect you can catch the didDisconnect (on the iOS side)
-(void) centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)
You can also ask iOS about "Known" devices (only those PAIRED, thus remembered/Cached in iOS6)
Go through each one and check the peripheral.isConnected flag.
Unfortunately iOS seems to also cache this for too long so some times you will see the isConnected flag even though connection has been lsot.
From Peripheral side you must keep track of didConnects / didDisconnects.
I have an MFi-compliant device to which I need to stream realtime data from iPhone. I had a look at the ExternalAccessory framework and the EADSessionController class. I am able to communicate with my MFi device by writing bytes to an output stream.
The problem I am facing is when I try to continuously stream bytes from my iPhone application to the MFi device, bytes are combined into chunks of variable size and sent at same time. Instead, I need to send each packet immediately to the external device.
Is there any way to push the stream to send data immediately, rather than aggregating it in these chunks?
I need to receive data periodically through a BlueTooth External Accessory.
I implemented an event-driven model of EA's streams. However, the initial transmission from bluetooth is always delayed. For example, if each packet was 15 bytes long, the stream delegate would not fires until about 150 bytes.
Will polling help?
EDIT:
Also I found it hard to recover the session after the app switching back from background to foreground. Trying to open session again would fail. Any idea?
Read every bytes when NSStreamEventHasBytesAvailable arrives.
Did you develop your own Bluetooth accessory? May be the MCU only flushes after sending every 150 bytes.
Also you mentioned initial transmission. Do you know once the Bluetooth device is paired and connected to iPhone, it has to go through some identification process, handshaking some secret certificate. This may take few and even 10 seconds, depending on signal quality. This may be the cause of delay.
Assuming i build an accessory using "Made for iPhone" program
is it possible to just send raw bytes to a bluetooth receiver without having to pair with accessory or any other "hand-shake"?
the accessory can only receive it cannot transmit
thank you
From what I have seen if you use the GameKit to connect / send data between devices , Explicit Pairing is not required.