Split data from Arduino in Objective-C - ios

Below is a code to receive data from a Bluno Beetle BLE:
/* Data received */
else if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:BLECharacteristic]]){
NSString *data = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
NSLog(#"Received Data = %#", data);
[_receiveText setText:data];
}
However, if I want to display multiple data values, is there a way for me to split the received text/data?
For example I want to display a number and a text, and the Arduino sends over a string. New to coding, so your help and patience will be appreciated!

is there a way for me to split the received text/data?
Yes, of course. You can do whatever you like to the data once you've got it. Take a look at the NSString documentation and you'll find plenty of methods for splitting and otherwise extracting data from strings. Some examples: -componentsSeparatedByString:, componentsSeparatedByCharactersInSet:, -stringByTrimmingCharactersInSet:, -substringWithRange:, etc. There are also other Foundation classes that can help, like NSScanner and NSRegularExpression.
New to coding, so your help and patience will be appreciated!
Reading the fine manual should be your first move no matter what your experience level is. The documentation for Apple's frameworks is generally excellent, and it includes many guides and introductory "getting started" documents that make it easy to get up to speed.

Related

Bluetooth peripheral doesn't answer

I am in big trouble with my exams.
I have to write an iOS app that uses an external sensor made by Texas Instruments, it's called TI Sensortag.
TI's documentation, in my humble opinion, is really poor and complicated to understand for an entry level programmer.
I tried to ask in the E2E forum but they weren't able to help me, their answer was something like "Um, well, we don't know, go away and ask someone else", ...
I added the CoreBluetooth framework to my project an created a CentralManager. I am able to find my device, connect and get his name and (sometimes) his RSSI.
Now what I'm trying to do is to ask my CBPeripheral object if it has some services for me or something like that. I've found the Complete Attribute Table but I have no idea how to use it...
I know I have to activate some services or something like that but I really don't now ho to do it, I googled a lot but I've not found something helpful...
I'm trying to enable my sensor with this method, but I'm doing something wrong.
-(void) configureSensorTag
{
uint8_t myData = 0x01;
NSData *data = [[NSData alloc] initWithBytes:&myData length:1];
[BLEUtility writeCharacteristic:myPer sUUID:#"F000AA00-0451-4000-B000-000000000000" cUUID:#"F000AA02-0451-4000-B000-000000000000" data:data];
[BLEUtility setNotificationForCharacteristic:myPer sUUID:#"F000AA00-0451-4000-B000-000000000000" cUUID:#"F000AA01-0451-4000-B000-000000000000" enable:YES];
NSLog(#"Configured TI SensorTag IR Termometer Service profile");
}
Moreover I'm trying to retrive Sensortag's services with this method
[peripheral discoverServices:nil];
and his delegate
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
NSLog(#"Found service");
if (error) {
NSLog(#"Error: %#", error);
}
}
but it is never called.
Has someone any idea?
Thank you very much!
Unfortunately I can't help you with the details of the iOS, but I can help you with understanding the sensor tag. If you look at that attribute PDF you linked you'll find entries marked "GATT_CLIENT_CHAR_CFG_UUID". It's 16 bits of flags where only the 2 least significant bits are used. It even says in there 'Write "01:00" to enable notifications, "00:00" to disable'. (That's the least significant bit because it's encoded in little-endian format)
So, you're sending a 0x01 to turn on the IR temperature sensor, but you haven't turned on the notifications. Turning it on will then cause the device to stream notifications back to the client. The accelerometer doesn't require turning on, so maybe you should try that first.
I have no idea what that second chunk of code is supposed to be doing... sorry.
Ok ok I got it,
there were any software problem, I mean, not by iOS side.
Sensortag has a wrong firmware and so it did'n work.
I've changed Sensortag and now everything works fine.
Thank you anyway!

Is there a data limit when using NSJSONSerialization in iOS?

I am sorry to bother you but I haven't found any usable topic that would help me.
I use NSMutableRequest through NSURLConnection to get my JSON data. Once the data are received, I serialize it using [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error].
It works just fine when getting a smaller JSON files but it doesn't work when I receive bigger JSON file. I write the log NSLog(#"--->: %#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); and the output is not one but two log records starting with --->:. The first output contains the first part of the JSON and the rest of JSON lays in the second one.
This error won't allow creation of NSDictionary as needed. Every "bigger" JSON received is split somewhere between 7500 - 8000 characters. My question is: Is there a limit that NSJSONSerialization can handle? From my perspective, it seems like it can handle 8 kilobytes of data and that is it. Is there any way to bypass it?
Thank you for your insights.
Thank you guys for your answers. I finally found the solution for my problem. The problem was in saving data badly in didReceiveData method. After applying [receivedData appendData:data]; everything starts to work well. The 8 kB problem is probably a chunk of data sent at once.

decoding a HUGE NSString, running out of memory

I'm looking for ideas on how to improve a process of decoding a 40+MB NSString with base64 encoding and saving it to a file while being able to fit the process into iPad 1's 256 MB of RAM
I get the NSString from NSXMLParser:
id pointerToString;
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if ([currentElement isEqualToString:#"myElement"])
{
pointerToString = [string retain];
}
}
Then I use the pointerToString in a callback:
[handler performSelector: action withObject: pointerToString];
In the callback (id value is the pointerToString). I initialize NSData with the pointerToString while decoding it with base64 encoding.
^(id value)
{
if ( [[value class] isSubclassOfClass:[NSString class]] )
{
NSData *data = [NSData dataFromBase64String:value];
[data writeToFile:file.path atomically:YES];
}
}
the iPad 1 device runs out of memory and gets killed by the iOS when the memory allocation reaches around 130MB after or during the NSData call.
I have determined that in order to process the 40+MB NSString this way, I'd need about 180+MB of RAM (this is what the maximum memory allocation is on iPad 2 & 3, where the process works because of more RAM)
Any ideas/tips ?
Thank you
Edit:
When dealing with a file of this size, you probably do not want to load the entire multi-megabyte file in memory at one time, neither the huge input file nor the almost-as-huge output file. You should be parsing this in a streaming fashion, decoding the data in your foundCharacters as you go along, not holding any significant portions in memory.
The traditional techniques, though, may hold your entire XML file memory in three phases of the process:
As you download the XML file from the server;
As the XML parser parses that file; and
As you do the Base64-decode of the file.
The trick is to employ a streaming technique, that does these three processes at once, for small chunks of the single, large XML file. Bottom line, as you're downloading the entire 50mb file, grab a few kb, parse the XML, and if you're parsing the Base64-encoded field, perform the Base64-decode for that few kb, and the proceed to the next chunk of data.
For an example of this (at least the streaming XML downloading-and-parsing, not including the Base64-decoding), please see Apple's XMLPerformance sample project. You'll see that it will demonstrate two XML parsers, the NSXMLParser that we're all familiar with, as well as the less familiar LibXML parser. The issue with NSXMLParser is that, left to it's own devices, will load the entire XML file in memory before it starts parsing, even if you use initWithContentsOfURL.
In my previous answer, I mistakenly claimed that by using initWithContentsOfURL, the NSXMLParser would parse the URL's contents in nice little packets as they were being downloaded. The foundCharacters method of NSXMLParserDelegate protocol seems so analogous to the NSURLConnectionDelegate method, didReceiveData, that I was sure that NSXMLParser was going to handle the stream just like NSURLConnection does, namely returning information as the download was in progress. Sadly, it doesn't.
By using LibXML, though, like the Apple XMLPerformance sample project, you can actually use the NSURLConnection ability of streaming, and thus parse the XML on the fly.
I have created a little test project, but I might suggest that you go through Apple's XMLPerformance sample project in some detail. But in my experiment, a 56mb XML file consumed well over 100mb when parsing and converting via NSXMLParser but only consumed 2mb when using LibXML2.
In your comments, you describe the desire to download the Base64-encoded data to a file and then decode that. That approach seems a lot less efficient, but certainly could work. By the way, on that initial download, you have the same memory problem (that I solve above). I urge you to make sure that your initial download of the Base64-encoded data does not blithely load it into RAM like most routines do. You want to, assuming you're using NSURLConnection, write the data to the NSOutputStream as you receive the data in didReceiveData, not hold it in RAM.
See the didReceiveResponse in AdvancedGetController.m of Apple's AdvancedURLConnections example for an example of how to write a file as it's being received, rather than typical patterns of adding it to a NSMutableData (because most of these routines just assume you're dealing with a reasonably sized file). (Ignore all the stuff in that AdvancedURLConnections sample about authentication and the like, but focus on understanding how it's writing to the NSOutputStream as it goes.) This technique will address the first of the three problems listed at the top of this answer, but not the latter two. For that, you'll have to contemplate using LibXML2 as illustrated in Apple's XMLPerformance sample project, or other similar techniques.
The method
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
is probably not receiving all the data at once.
Doc is saying
"Sent by a parser object to provide its delegate with a string representing all or part of the characters of the current element."
So it is called multiple times.
It looks like you are trying to write the whole string at once (sorry if I am wrong).
So you could append the received data to the file by doing the following:
You can use a combination of
-writeData:
and
-seekToEndOfFile
methods from NSFileHandle class for writing NSData to the end of a file.
But be carefull with your base64 encoding on partial data receivment!

Using output parameters with ARC

So I have read this question, which seems to be exactly the kind of problem I am having, but the answer in that post does not solve my problem. I am attempting to write a data serialization subclass of NSMutableData. The problematic function header looks like this:
-(void)readString:(__autoreleasing NSString **)str
I do some data manipulation in the function to get the particular bytes the correspond to the next string in the data stream, and then I call this line:
*str = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
No errors in this code. But when I try to call the function like so:
+(id) deserialize:(SerializableData *)data
{
Program *newProgram = [[Program alloc] init];
[data readString:&(newProgram->programName)];
On the line where I actually call the function, I get the following error:
Passing address of non-local object to __autoreleasing parameter for write-back
I have tried placing the __autoreleasing in front of the NSString declaration, in front of the first *, and between the two *'s, but all configurations generate the error.
Did I just miss something when reading the other question, or has something in the ARC compiler changed since the time of that post?
EDIT:
It seems that the problem is coming from the way I am trying to access the string. I can work around it by doing something like this:
NSString* temp;
[data readString&(temp)];
newProgram.programName = temp;
but I would rather have direct access to the ivar
You can't. You might gain insight from LLVM's document Automatic Reference Counting, specifically section 4.3.4. "Passing to an out parameter by writeback". However, there really isn't that much extra detail other than you can't do that (specifically, this isn't listed in the "legal forms"), which you've already figured out. Though maybe you'll find the rationale interesting.

stringWithContentsOfFile and initWithContentsOfFile return null after several runs

I am creating an iOS app which reads in a text file and displays the contents in a UIText field.
For the 1st three consecutive runs of thee app (Restarting a new session without exiting),
the data is read in fine. However on the fourth attempt, the data returned from the file is all nulls.
I've verified the file integrity. The issue exists when using stringWithContentsOfFile or initWithContentsOfFile.
After many hours of troubleshooting, I believe the issue is somehow related to a buffer being cleared within the above mentioned methods.
Any insight regarding this issue is greatly appreciated. I've tried many things with no luck.
Here's the code I use to read in the file:
TheString = [NSString stringWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:#"My_TextFile" ofType:#"txt"] encoding:NSUTF8StringEncoding error:NULL];
Here's the code I use to display certain contents of the file (The contents are placed in an array of type NSArray):
NSArray *My_Array;
My_Array= [TheString componentsSeparatedByString:#"\n"];
/* Obtain specific data to display */
DisplayedData = [My_Array objectAtIndex:M[l]-1];
:
:
/* Display the data in the view */
MyUITextView.text = DisplayedData;
/* Log the data */
NSLog(#"%#", MyUITextView.text);
On the 4th invocation of the code above, the data returned is blank and NSLOG is returning nulls
Thanks so much for any help!
Maybe I'm a little bit late with answer, but, anyway, maybe somebody will find it useful.
OK, I have also spent a day trying to figure out why my custom class for scrollable view is working 3 times and refuse at the 4-th time... I found that the problem has quite the same attributes as yours: nested NSString objects unexpectedly disappear. Though pointers point to the same address in memory, memory is already filled with quite arbitrary objects instead my NSStrings.
And I paid attention that I created these NSStrings using the following class method:
+ (id)stringWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)enc error:(NSError **)error
So, I'm not the owner of these NSStrings.
And I assumed that to be the owner can be a solution, so I created my NSStrings through alloc and
- (id)initWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)enc error:(NSError **)error
instance method.
App was repaired!

Resources