Writing >20 bytes to an external Bluetooth device in iOS - ios

I have an external device that communicates via Bluetooth with an iPhone Application that has support for iOS6+.
The problem is that I need to write more than 20 bytes to a characteristic and from what I've read and tried this is not possible using BLE.
Here is a basic structure of my method:
NSData * payload = ... //53 bytes, last 2 bytes CRC
[peripheral writeValue:payload forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
The error I get when sending >20 bytes is CBErrorOperationCancelled (code 5) and CBErrorConnectionTimeout (code 6). I believe this is normal.
I divided the data in chunks of 20 bytes (example here: https://github.com/RedBearLab/iOS/issues/8) and the data is not written well on the device:
NSData * chunk1 = ... //first 20 bytes
NSData * chunk2 = ... //next 20 bytes
...
NSData * chunkN = ... //remaining bytes + CRC from whole data
[peripheral writeValue:chunk1 forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];
[peripheral writeValue:chunk2 forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];
...
[peripheral writeValue:chunkN forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
I think the external device treats each byte array, not the whole data.
Also, I tried to split the payload data in chunks of 18 bytes and append the CRC to each chunk of bytes I send. This got me the same result as in appending CRC to the last chunk of data.
In the Android version of the application the whole data is sent, so I know that the device can read >20 bytes on a single command.
My questions are:
Is it possible to send the whole data in chunks to the external
device without modifying the peripherals hardware / software?
Is there a flag / signal byte in the first chunk of data that notifies
the device the length of the whole message?
I've read that in iOS 7 is possible to send a larger byte array. If
I make support for iOS 7+ will it solve my problem?
What alternatives do I have to CB?
Any help will be appreciated. Many thanks!
EDIT
When I try sending data in chunks like in the BTLE sample I get the following response:
// NSLog - data that is written on the peripheral
// Chunk 1 : <2047d001 0f002000 315b0011 b4425543 41524553>
// Chunk 2 : <54202020 20202020 20202020 20202020 20202020>
// Chunk 3 : <20202020 2009059b 56210988 b9b50408 02c7123d>
// Write to peripheral
[self.twCurrentPeripheral writeValue:chunk1 forCharacteristic:self.twCharacteristic type:CBCharacteristicWriteWithoutResponse];
[self.twCurrentPeripheral writeValue:chunk2 forCharacteristic:self.twCharacteristic type:CBCharacteristicWriteWithoutResponse];
[self.twCurrentPeripheral writeValue:chunk3 forCharacteristic:self.twCharacteristic type:CBCharacteristicWriteWithResponse];
// NSLog - Response from peripheral
// Data update char : <20470000 04d00101 00ab42>
// Data update char : <204700>
// Data update char : <0004d001 0100ab42>
// Data update char : <204700>
// Data update char : <0504d001 032c6bcf>
The correct response should be (like in the Android version):
// Response <20470000 04d00101 00ab42>
// Response <20470000 04d00105 006786>

You need to send the data in 20 bytes chanks. Each packet should be at the size of 20 bytes. The other side (the BLE central) should accept the packets and save them until he gets all the packets he was waiting for. For this to happen you will have to create some kind of protocol:
Sending the type of message then the length of the message in bytes and the centeral should read this header and then save the "length" bytes he is waiting for in this message and then parse the entire data.
Note that IOS might fail sending some of the packets when there are many packets to send very fast, so when calling
success = [self.peripheral updateValue:chunk forCharacteristic:myChar onSubscribedCentrals:nil];
success will tell you if the send was successful, if it was not you need to resend this packet.
in that case you should implement the IOS BLE protocol CBPeripheralManagerDelegate
and at the method peripheralManagerIsReadyToUpdateSubscribers you should retry sending the same packet again.

You need to chunk the data in 20 byte length pieces, you got that part right. If you use the "write with response" method, then before sending the next piece of data, you should wait for the peripheral:didWriteValueForCharacteristic:error: callback to happen on your peripheral delegate before sending the next piece. In case of "write without response", you should wait before sending out the next chunk. The stuff you add in the in the writeValue call will be buffered. If the buffer is full, then your call will be dropped.

Related

Server don't receive data send by iOS

I'm send a string to my server (PHP) with the code below:
NSString* str = #"teststring";
NSData* dataToSend = [str dataUsingEncoding:NSUTF8StringEncoding];
uint8_t *dataBytes = (uint8_t *)[dataToSend bytes];
uint64_t length = dataToSend.length;
[outputStream write:dataBytes maxLength:length];
The problem with this is code, is that I don't receive the string (Server recognize that a user enter and I receive a handshake in Xcode console) I test others codes and works very well (receive data), but in this time I'm trying by my own. I believe I'm not converting the message to the appropriate format, and that's why the message is not being received, am I right? If so how I could convert this message to the appropriate format?
Are you sure the output stream has space for writing that many bytes at the moment when you call the write:... method? You should only call write:... on an output stream from within your stream:handleEvent: delegate method, and only if the event code is NSStreamEventHasSpaceAvailable.
And you must check the result to see how many bytes were actually written. If it is fewer bytes than you sent, you must enqueue the remaining data to resend later, whenever your stream:handleEvent: delegate method gets called again.
For more details, see:
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Streams/Articles/WritingOutputStreams.html

GCDAsyncSocket lose data

when i use GCDasyncsocket to get socket Message from the server(java),it works in the ios simulator(xcode 6,iphone6+); but when i use my iphone6+ connect it to testing, it will lose data when it receive big data,details below
i send a request to the server and get 3 answers
the first data the server answer length is 9 ,and receive 9
the second is 149 and receive 149
the last data the server answer is :2912,but the code shows 1448,
i get the 1448 in the below code:
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSLog(#"receive datas from method 1");
NSLog(#"Data length = %d",[data length]);
...
and when i go deep,the code in the gcdasyncsocket shows the same,code below
if (currentRead->bufferOwner)
{
// We created the buffer on behalf of the user.
// Trim our buffer to be the proper size.
[currentRead->buffer setLength:currentRead->bytesDone];
result = currentRead->buffer;
NSLog(#"if %lu %lu",(unsigned long)currentRead->bytesDone,(unsigned long)result.length);
}
it works well in the simulator,but goes wrong in the real phone;i also test in the iphone4s,it works well most times ~~~~~~~~~~
how strange!
can anyone give me some suggests?
i have got the reason; it is that the protocol split the message ;
i use the method to send the length of the message to tell the protocol how to split it ;
also ,there are some other solutions to solve it ;
and after that ,i also found that the protocol will combine some message,so,when you try to solve it ,you can not just think about the split sitution!

IOS: AsyncSocket - readData method's invoke mechanism

things are like this:
I used AsyncSocket to connect with server. when the server send some bytes to client at a time, app will invoke (onSocket: didReadData: withTag:) method; Sometimes, because of the network or other reason, some bytes arrived and the rest bytes arrived later( which means some bytes are delayed).(timeOut = -1)
==================================
Question: how much times did the method(onSocket: didReadData: withTag: ) invoke?
1)only one time - it will wait automatically until the bytes are complete?
2)2 or more times - some arrived(invoke), other arrived(invoke)、、、、、like so?
===================================
which thoughts is right? plz geiv me some advice. Thanks very much.
OK, maybe it's just like this.
Transfer data on TCP: when you send 1000 bytes at a time, because of lag or other reasons, you may receive the 900 bytes first, but the TCP socket will wait the rest 100 bytes
automatically. So if I use asyncSocket to transfer datas and set the timeout, during these seconds, I don't need to deal with the lagged data.
=========================
EDIT
maybe the title of ths question is not appropriate, no one cares the question.
I used the method -readDataToLength: instead, first read header.length of data, and parse it, then read the body.length of data;

RedPark Serial Cable partial data

I have an RFID scanner attached to a RedPark serial cable connected to an iPad app. When people scan their RFID cards, I get a callback with -readBytesAvailable:. However, sometimes it doesn't give me the entire RFID in one call. Sometimes it send it in two calls.
How can I determine if I've received everything? When my code takes the first callback's data and tries to use it, I get an error, because let's say the RFID was "123456789" sometimes I'll get one call with #"12" and a second call with #"3456789". So I try to process #"12" and get a user not found error, then I try to process #"3456789" and get a user not found error.
How can I tell if I'm done reading data? The lengths of the RFIDs can vary from vendor to vendor, so I can't just assume I need to read a certain number of digits.
This is the method I use to receive the data from the scanner through the RedPark:
- (void) readBytesAvailable:(UInt32)length {
NSLog(#"readBytesAvailable: %lu", length);
UInt8 rxLoopBuff[LOOPBACK_TEST_LEN];
[self.rfidManager read:rxLoopBuff Length:length];
NSString *rfid = [[NSString alloc] initWithBytes:rxLoopBuff length:length encoding:NSUTF8StringEncoding];
NSLog(#"rfid=%#", rfid);
[self receivedScanOfRFID:rfid];
}
Serial port gives you no control over packetization. Data is just a stream of bytes with no way to predict which bytes appear in each read call. You have to parse the data stream itself to interpret the contents and understand start/end of your messages. You either need to look for a reliable terminating character or potentially use a timeout approach where you do multiple reads until you get no more data for some period of time. I don't recommend the timeout approach.

How to write data to socket in BlackBerry?

I am sending data to the server twice. First, I send "Hello world" and then I send "Server".
But the server received the data at 1 read. But the server have to read the data in a two-read operation.
Also, I write the data. Then read data from server and then I write the data.
In this case, the server can read the first data. But server can not read the second data.
The server uses read, write, read.
So how to overcome this issue? How do I write data to socket in BlackBerry?
What you describe is how TCP is supposed to work by default. What you are seeing is the Nagle algorithm (RFC 896) at work, reducing the number of outbound packets being sent so they are processed as efficiently as possible. You may be sending 2 packets in your code, but they are being transmitted together as 1 packet. Since TCP is a byte stream, the receiver should not be making any assumptions about how many packets it gets. You have to delimit your packet data in a higher-level protocol, and the receiver has to process data according to that protocol. It has to handle cases where multiple packets arrive in a single read, a single pakcet arriving in multiple reads, and everything in between, only processing packet data when they have been received in full, caching whatever is left over for subsequent reads to process when needed.
Hard to say without a little more detail, but it sounds like you're using 1-directional communication in the first case - i.e. the client writes, then writes again. There are any number of reasons that the server would receive the 2 writes as 1 read. Buffering on the client, somewhere in the wireless stack (or in the BES), buffering on the server side. All of those are legal with TCP/IP.
Without knowing anything more about your solution, have you thought about defining a small protocol - i.e. the client writes a known byte or bytes (like a 0 byte?) before sending the second write? Then the server can read, then recognize the delimiting byte, and say 'aha, this is now a different write from the client'?
As previously said this is an expected TCP behavior to save bandwidth. Note that to deliver your package TCP adds lot of data (e.g. destination port,sequence number, checksums...).
Instead of flushing the data I´ll recommend you to put more work in your protocol. For example you can define a header that contains the number of bytes to read and then the payload (the actual data).
The following code is a protocol encoded in an string with the structure [length];[data]
StringBuffer headerStr = new StringBuffer();
StringBuffer data = new StringBuffer();
//read header
char headerByte = dataInputStream.readChar();
while (headerByte != ';') {
headerStr.append(headerByte);
headerByte = dataInputStream.readChar();
}
//header has the number of character to read
int header= Integer.parseInt(headerStr.toString());
int bytesReaded = 1;
char dataByte = dataInputStream.readChar();
//we should read the number of characters indicated in the header
while (bytesReaded < header) {
data.append(dataByte);
dataByte = dataInputStream.readChar();
bytesReaded++;
}
For the first query, I guess you are using TCP. If you use UDP, then the server will read the packets in the order you want.
Can you be more clear/elaborative on the second query ?
I would try explicitly telling Connector.open to open up the stream as read_write. Then I would ensure that I flush my connections after each time I talked to the server.
SocketConnection connection = (SocketConnection) Connector.open(url, Connector.READ_WRITE);
OutputStream out = connection.openOutputStream();
// ... write to server
out.flush()
I got a solution to overcome to extract both the string
On sender device
Create a header which contains details of that data eg the data
length, datatype etc.
Add this header to the actual data and send it
On recipient device
read the header
retrieve the actual data length from the header
read the next data upto the data length as specified by the header

Resources