Sending few values at the same time over bluetooth - ios

I am working on a Bluetooth based application and I am having problems when I try to send data from the iPhone to the other device.
I have no problem when I have to send just one value, using something like this:
- (void)sendData:(NSInteger)mel {
NSData *myData = [NSData dataWithBytes:&mel length:sizeof(mel)];
[self.myDevice writeValue:myData forCharacteristic:self.myCharacteristic type:CBCharacteristicWriteWithoutResponse];
}
But, for some characteristics I need send 2 or more values at the same time (for example in this case, variable mel and another one) but I haven’t been able yet to do it.
Does somebody know how to do this? Thanks in advance.
UPDATE 1
What I tried to send two values is
unsigned char bytes[] = {mel, interval};
NSMutableData *myData = [NSMutableData new];
[myData appendBytes:&bytes length:sizeof(bytes)];
[self.myDevice writeValue:myData forCharacteristic:self.myCharacteristic type:CBCharacteristicWriteWithoutResponse];
But this works like if the second value didn't exist

You can't use sizeof(bytes) to get the number of bytes in the array. It's simply going to return 4 since that is the size of a char *.
One options would be to use sizeof(mel) + sizeof(interval) instead of sizeof(bytes).

Related

iOS Bluetooth Performing Write Long

I'm working on a project with an iPhone connecting to an ESP32 using BLE. I'm trying to write a 528 byte long blob to a characteristic. Since the blob is longer than the max 512 allowed per write I'm trying to do a Write Long.
I'ved tried a couple things
1 - If I just try to write the blob I see the first chunk go through with Prepare Write set but there are no subsequent writes.
Why is it stopping after the first chunk?
2 - If I try to chuck it manually based on the size returned from maximumWriteValueLengthForType I see all the data is sent correctly but Prepare Write is not set so they aren't handled correctly.
How do I specify Prepare Write / Execute Write requests?
Here's a code snippet covering the implementation #2
NSData *blob = [request value];
NSUInteger localLength = 0;
NSUInteger totalLength = [blob length];
NSUInteger chunkSize = [peripheral maximumWriteValueLengthForType:type];
uint8_t localBytes[chunkSize];
NSData *localData;
do
{
if(totalLength > chunkSize) {
NSLog(#"BIGGER THAN CHUNK!!!!!!!!!!!!!!!!!!!!!");
NSLog(#"%tu", chunkSize);
for ( int i = 0; i < chunkSize; i++) {
localBytes[i] = ((uint8_t *)blob.bytes)[localLength + i];
}
localData = [NSMutableData dataWithBytes:localBytes length:chunkSize];
totalLength -= chunkSize;
localLength += chunkSize;
}
else {
NSLog(#"Smaller than chunk");
uint8_t lastBytes[totalLength];
for (int i = 0 ; i < totalLength; i++) {
lastBytes[i] = ((uint8_t *)blob.bytes)[localLength + i];
}
localData = [NSMutableData dataWithBytes:lastBytes length:totalLength];
totalLength = 0;
}
// Write to characteristic
[peripheral writeValue: localData forCharacteristic:characteristic type:type];
} while( totalLength > 0);
Long writes are affected by the same limit of 512 bytes maximum for the characteristic value. Long writes are only useful when MTU is too short to write the full value in one packet. Maybe you're trying to write out of this allowed range or something.
Newer iOS versions communicating with BLE 5 devices use a large enough MTU to fit a characteristic value of 512 in one packet (if the remote device also supports such a big MTU).
If you want to write bigger values than 512 bytes, you will need to split it up into multiple writes, so that the second write "overwrites" the first value sent, rather than appending to it. You can also use L2CAP CoC instead which eliminates this arbitrary 512 byte limit.
You have the right general approach but you can't just send the chunks sequentially. There is a limited buffer for sending Bluetooth data and your loop will write data into that buffer more quickly than the Bluetooth hardware can send it.
The exact approach you need to take depends on whether your characteristic supports write with response or write without response.
If your characteristic uses write with response, you should send a chunk and then wait until you get a call to the didWriteValueFor delegate method. You can then write the next chunk. The advantage of this approach is essentially guaranteed delivery of the data. The disadvantage is it is relatively slow.
If your characteristic uses write without response then you call write repeatedly until you get a call to didWriteValueFor with an error. At this point you have to wait until you get a call to peripheralIsReady. At this point you can start writing again, beginning with the last failed write.
With this approach there is the potential for lost data, but it is faster.
If you have to move large amounts of data, an L2Cap stream might be better, but you need to handle data framing.

What should I learn to really understand NSData, NSMutableData, NSString in iOS?

I am an intermediate student in iOS development, I am trying to make a method that uploads an image to a server. I understand the server side scripting in PHP.
But when I am following a tutorial to upload an image in Xcode, I don't really grasp about NSData, NSObject, NSMutableData, NSString, it seems the tutorial doesn't really the fundamental aspect of NSData, NSMutableData, NSString...
If want to take beautiful display, I should learn about auto layout, collection view etc.
So, what kind of topic in iOS development that I should learn to really understand about these things step by step? It seems that I never learn specifically about these things. I don't know where to start.
The code to upload an image is like this:
func createBodyWithParameters(parameters: [String: String]?, filePathKey: String?, imageDataKey: NSData, boundary: String) -> NSData {
let body = NSMutableData();
if parameters != nil {
for (key, value) in parameters! {
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString("\(value)\r\n")
}
}
let filename = "user-profile.jpg"
let mimetype = "image/jpg"
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n")
body.appendString("Content-Type: \(mimetype)\r\n\r\n")
body.appendData(imageDataKey)
body.appendString("\r\n")
body.appendString("--\(boundary)--\r\n")
return body
}
func generateBoundaryString() -> String {
return "Boundary-\(NSUUID().UUIDString)"
}
}
extension NSMutableData {
func appendString(string: String) {
let data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
appendData(data!)
}
}
Learning about objects you have described may mean many things. If you are after their capabilities then the documentation should be enough. But if you are more after what is under the hood and why we need these objects then I could only suggest you to look into some older language like C.
These objects NSData, NSMutableData, NSString are all data containers, buffers. But NSObject is just a base class from which all other objects inherit.
So a bit about NSData and NSMutableData:
In C when creating a raw buffer you use malloc which reserves a chunk in memory which you may use as you please. Once done you need to call free to release that memory or you will have a memory leak.
void *voidPointer = malloc(100); // Reserved 100 bytes of whatever data
int *intPointer = (int *)malloc(sizeof(int)*100); // Reserved enough bytes to fit 100 integers whatever their size may be
intPointer[13] = 1; // You may use these as normal array
free(voidPointer); // Free this memory
free(intPointer); // Free this memory
So NSData is basically a wrapper for that and does all of it for you. You may even access the raw pointer by calling bytes on NSData object.
Then the mutable version NSMutableData is just a subclass which has some additional functionality. You may actually append data. From what is under the hood appending data is not so simple. You need to allocate a new memory chunk, copy old data to it, copy new data and release the previous memory chunk.
void *currentData = malloc(100); // Assume we have some data
void *dataToAppend = malloc(100); // Another chunk of data we want to append
void *combinedData = malloc(200); // We need a larger buffer now
memcpy(combinedData, currentData, 100); // Copy first 100 bytes to new data
memcpy(combinedData+100, dataToAppend, 100); // Copy next 100 bytes to new data
free(currentData); // Free old data
free(dataToAppend); // Free old data
... use combinedData here ...
free(combinedData); // Remember to free combined data once done
These are all really simple methods but they may already be pain to write and it is easy to produce bugs doing so. So NSData or NSMutableData and even Data in Swift are all just data containers that make your developer life easier. And in Objective-C conversion from data to C buffers is as easy as it gets:
NSData *myData = [NSData dataWithBytes:myDataPointer length:myDataLength];
void *myRawPointer = [myData bytes];
The NSString is not really that different. In C we again have character pointer which is used as string so we write something like:
char *myText = "Some text";
These are a bit special, a convenience really. We could as well do:
char *myText = (char *)malloc(sizeof(char)*100);
And then fill the data character by character:
myText[0] = 'S';
myText[1] = 'o';
myText[2] = 'm';
...
myText[9] = 't';
myText[10] = '\0'; // We even need to set a null terminator at the end
and then we needed to free the memory again... But never mind the C strings, NSString is again a wrapper that is responsible to allocate the memory, assign data and do whatever you want with it. It has may methods you can use simply to make your life easier.
As to the code you posted it is a combination of the two. In your case your API accepts images as multipart form data requests which you may understand as a raw image file with a few texts added around it just to explain what the data contains. It is one of a generally used way but not the only one. You might as well just post the raw image data or you might even post a JSON containing a base64 string encoded data. Also as usually these texts are represented as an utf8 encoded data.
In the end it is a set of standards that are generally used so our computers may communicate between each other. Your image is most likely defined by a standard from png or jpg on how to present it with a string of bytes, your strings are defined by utf8 standard and your whole request body is defined by some HTTP standards (not even sure what part of it is that). And the objects you use and want to learn about are just some helpers for achieving your result. Understanding them in most cases is like understanding a screwdriver; you won't need to in most cases, but you do need to know they exist and you need to know when to use them.
The code itself you posted is relatively bad but should do its job. For a beginner it might be a bit confusing even. Probably a more logical pseudocode for this solution would be something like:
let imageData: Data // My image data
let headerString: String // Text I need to put before the image data
let footerString: String // Text I need to put after the image data
var dataToSend: Data = Data() // Generate data object
dataToSend.append(headerString.utf8Data) // Append header
dataToSend.append(imageData) // Append raw data
dataToSend.append(footerString.utf8Data) // Append footer
I hope this clears up a few things.

-[NSString dataUsingEncoding:] gives garbage at end of string in iOS 9, not iOS 8

The following code runs fine on iOS 8 but when run on iOS 9.0.2 I get some odd results:
NSString * input = #"Hi there";
NSData * data = [input dataUsingEncoding:NSASCIIStringEncoding];
Byte *byteData = (Byte*)malloc(data.length);
memcpy(byteData, [data bytes], data.length);
NSString * result = [NSString stringWithCString:(const char*)byteData encoding:NSASCIIStringEncoding];
NSLog(#"Result: %#", result);
iOS 8.4 (iPhone 6 Plus) byteData is Hi there
iOS 9.0.2 (iPhone 6S) byteData is Hi there\xb6<M\x13
On iOS 9 I end up with a load of garbage at the end of the string.
This feels like a 32 bit vs 64 bit issue as it looks like on iOS 9 the byteData length is twice as long?
Apple have their table of 32 to 64 bit changes here:
https://developer.apple.com/library/ios/documentation/General/Conceptual/CocoaTouch64BitGuide/Major64-BitChanges/Major64-BitChanges.html
data.length is an unsigned long long. Could this be returning different lengths when malloc is called? The code above returns 8 for data.length when run on each version of iOS.
This just feels quite odd and I've run out of angles to attack it from. Hopefully someone out there might be able to shed some light on this one.
Thanks!
Update
I can fix it using
NSString * result = [[NSString alloc] initWithBytes:byteData length:data.length encoding:NSASCIIStringEncoding];
but I'd still like to know why I get a different result on the two iOS versions with
NSString * result = [NSString stringWithCString:(const char*)byteData encoding:NSASCIIStringEncoding];
A “C string” ends with a NUL byte. Since you created data using dataUsingEncoding:, data does not contain a C string.
Since stringWithCString:encoding: is reading outside of valid memory (looking for the NUL terminator), the behavior is undefined and thus allowed to changed at any time.
Use cStringUsingEncoding: to create data and you'll get the NUL terminator you need.

Extract and Convert NSData from BLE to Float in Swift

I'm developing an IOS App that handles a BlueTooth SensorTag.
That SensorTag is based on the TI BLE SensorTag, but we had some of the Sensors removed.
In the sourcecode of the original IOS App from TI the XYZ-Values are calculated like follows
with KXTJ9_RANGE defined as 1.0 in my implementation and KXTJ9 is the Accelerometer built on the SensorTag
+(float) calcXValue:(NSData *)data {
char scratchVal[data.length];
[data getBytes:&scratchVal length:3];
return ((scratchVal[0] * 1.0) / (64 / KXTJ9_RANGE));
}
The data comes as hexadecimal like "fe850d" and by the method will be cut in 3 parts.
Now i'm trying to convert this method to swift, but i get the wrong numbers back
e.g. fe should return something around 0.02 what the objective C Code does
My Swift Code so far
class Sensor: NSObject {
let Range: Float = 1.0
var data: NSData
var bytes: [Byte] = [0x00, 0x00, 0x00]
init(data: NSData) {
self.data = data
data.getBytes(&bytes, length: data.length)
}
func calcXValue()->Float {
return ((Float(bytes[0]) * 1.0) / (64.0 / Range))
}
...
}
I believe problem must lie around my Float(bytes[0]) because that makes 254 out of fe whereas scratchVal[0] in ObjectiveC is around 64
But my main problem is, i was all new with IOS programming when i had to begin with this project, so i chose to use Swift to learn and code the app with it.
Right now i use the original Objective C Code from TI to use with our SensorTag, but i would prefer using Swift for every part in the App.
On all current iOS and OS X platforms, char is a signed quantity,
so that the input fe is treated as a negative number.
Byte on the other hand is an alias for UInt8 which is unsigned.
Use [Int8] array instead to get the same behaviour as in your Objective-C code.
it depends on the BLE device Endianness, in relation to your device Endianness.
wiki on Endianness
To keep it simple you need to check which does two method
NSData *data4 = [completeData subdataWithRange:NSMakeRange(0, 4)];
int value = CFSwapInt32BigToHost(*(int*)([data4 bytes]));
or
NSData *data4 = [completeData subdataWithRange:NSMakeRange(0, 4)];
int value = CFSwapInt32LittleToHost(*(int*)([data4 bytes]));
And check which one make more sense when you parse the data.

How to convert dispatch_data_t to NSData?

Is this the right way?
// convert
const void *buffer = NULL;
size_t size = 0;
dispatch_data_t new_data_file = dispatch_data_create_map(data, &buffer, &size);
if(new_data_file){ /* to avoid warning really - since dispatch_data_create_map demands we care about the return arg */}
NSData *nsdata = [[NSData alloc] initWithBytes:buffer length:size];
// use the nsdata... code removed for general purpose
// clean up
[nsdata release];
free(buffer); // warning: passing const void * to parameter of type void *
It is working fine. My main concern is memory leaks. Leaking data buffers is not fun. So is the NSData, the buffer and the dispatch_data_t new_data_file all fine?
From what I can read on http://opensource.apple.com/source/libdispatch/libdispatch-187.7/dispatch/data.c it seems the buffer is DISPATCH_DATA_DESTRUCTOR_FREE. Does that mean it is my responsibility to free the buffer?
Since iOS 7 and macOS 10.9 (Foundation Release Notes) dispatch_data_t is an NSObject (NSObject <OS_dispatch_data>) in 64 bit apps.
dispatch_data_t can now be freely cast to NSData *, though not vice versa.
For the most part, your code is correct.
+initWithBytes:length: will copy the buffer sent in so, you don't have to worry about freeing the buffer after the data, you can safely free the data first.
According to the documentation, you do NOT free the data after you are done with it:
If you specify non-NULL values for buffer_ptr or size_ptr, the values returned in
those variables are valid only until you release the newly created dispatch data
object. You can use these values as a quick way to access the data of the new
data object.
You simply release the new_data_file variable (ARC will not do this for you).

Resources