I´m new in iOS Programming and i´m trying to receive Data from a BLE device. I´m able to connect to the device and send data to the device. The problem is receiving data from the BLE Device.
Im using this as BLE Base:
LGBluetooth
Problem:
#property (nonatomic,strong) NSString* recievedData; //data recieved from Peripheral
- (void)RecieveDataBLE:(LGPeripheral *)peripheral :(NSString*)Service_UUID :(NSString*)Characteristic_UUID {
//Function to read Data from BLE Device
[LGUtils readDataFromCharactUUID:Characteristic_UUID serviceUUID:Service_UUID
peripheral:peripheral
completion:^(NSData *data, NSError *error) {
NSLog(#"Data : %s Error : %#", (char *)[data bytes], error);
recievedData = [NSString stringWithUTF8String:[data bytes]];
}
- (IBAction)sendDOWN:(id)sender {
MessageLabel.hidden = NO;
[self RecieveDataBLE:mBuddy:SERVICE_UUID_DEVICE_INFORMATION:CHARACTERISTIC_UUID_MANUFACTURER];
MessageLabel.text = [NSString stringWithFormat:#"%#",recievedData]; //Output is always old!!
}
After the action sendDown is called, data should be read via the ReceiveDataBle function. This one will then process the data. The problem is, ReceiveDataBle is always too late, and does not receive the information immediately, but after a certain time. A callback function returns the data to ReceiveDataBle. So when the action SendDown is called the Data from the last call appears in the MessageLabel!!
The question is, how can I ensure that I have the newest data available, and not outdated data?
**You can use this type when show updated recievedData **
-(IBAction)sendDOWN:(id)sender
{
MessageLabel.hidden = NO;
[LGUtils readDataFromCharactUUID:Characteristic_UUID
serviceUUID:Service_UUID
peripheral:peripheral
completion:^(NSData *data, NSError *error) {
NSLog(#"Data : %s Error : %#", (char *)[data bytes], error);
recievedData = [NSString stringWithUTF8String:[data bytes]];
MessageLabel.text = [NSString stringWithFormat:#"%#",recievedData];
}
}
Related
I'm very new to Objective-C and would like to communicate between an iOS app which I'm working on and my Python 3 socket server (which works). The problem is I don't know how to use sockets in Objective-C and don't know where to start when installing libraries in Xcode 8. For now I just want to be able to send data to the server and receive a response back on the iOS device. I have seen sys.socket.h but again, I don't know how to use it, any help would be much appreciated.
Thanks!
Few years ago I used SocketRocket. I am posting some of the code of it from my old project. I don't know if that still works but you might get the idea of using it.
Connecting to server
NSMutableURLRequest *pushServerRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"ws://192.168.1.1"]];
[pushServerRequest setValue:#"WebSocket" forHTTPHeaderField:#"Upgrade"];
[pushServerRequest setValue:#"Upgrade" forHTTPHeaderField:#"Connection"];
[pushServerRequest setValue:"somekey" forHTTPHeaderField:#"Sec-WebSocket-Protocol"];
SRWebSocket *wsmain = [[SRWebSocket alloc] initWithURLRequest:pushServerRequest]; //Declare this as a global variable in a header file
wsmain.delegate=self;
[wsmain open];
Delegate Methods
-(void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message
{
NSLog(#"Message %#",message);
}
- (void)webSocketDidOpen:(SRWebSocket *)webSocket
{ NSLog(#"Connected");
}
Sending Commands
[wsmain send:commands];
By sending commands, you will receive a response in didReceiveMessage method.
have you seen https://github.com/robbiehanson/CocoaAsyncSocket?
It's easy to use socket
NSString *host = #"10.70.0.22";
int port = 1212;
_socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
NSError *error = nil;
[_socket connectToHost:host onPort:port error:&error];
if (error) {
NSLog(#"%#",error);
}
delegate
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{
NSLog(#"success");
}
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{
if (err) {
NSLog(#"error %#",err);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self connectToServer];
});
}else{
NSLog(#"disconnect");
}
}
Another option is socket.io. It has server libraries and a iOS client library written in Swift. It's API is quite extensive.
I am running an app that communicates from my Mac to my iPhone via bluetooth and vice versa. It is all working perfectly (with two generated characteristics one from Mac to iPhone and one from iPhone to Mac) apart from, occasionally my characteristic from mac to iPhone is not being updated and I think this is due to the charachteristic on the mac being set to nil for no apparent reason.
So to update my char on my Mac I do:
- (void)updateValueForCharacteristic:(int)sendID {
NSLog(#"sent %d", sendID);
if (self.characteristic != nil) {
dispatch_block_t block = ^(void) {
NSData *value = [NSData dataWithBytes:&sendID length:sizeof(sendID)];
[_managerout updateValue:value forCharacteristic:self.characteristic onSubscribedCentrals:nil];
};
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, block);
}else{
//why is this sometimes being called?!
}
}
and to receive on the iPhone I do:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error != nil) {
NSLog(#"Error updating value: %#", error.localizedDescription);
return;
}
CBUUID *characteristicUUID = [CBUUID UUIDWithString:_kCharacteristicUUIDin];
NSInteger rec = 0;
[characteristic.value getBytes:&rec length:sizeof(rec)];
NSLog(#"recieved %ld", (long)rec);
if ([characteristic.UUID isEqual:characteristicUUID]) {
NSLog(#"officially recieved %ld", (long)rec);
//handle char (sometimes not noticing)
}
}
Is there anyway that I can verify that my iPhone has received the char. Also should I scrap this technique and try with writing (rather than updating) the char or maybe try with multiple chars rather than just the one?
I really need to make sure that the iPhone gets it or else the program is useless.
update
I have added an else block to if (self.characteristic != nil) { and the else block is being called when my char isn't being updated!!! But I can't find for the life of my why the self.characteristic is being set to nil?! And also how should I go about re-creating the characteristic after?
project
So I am using the pokedex API as a learning curve for IOS and web services,
Here is my didrecivedata when the connection completes
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
//If the resposne recieved is good the call this function
// NSLog(#"data is %#", data);
//NSString *myString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
//NSLog(#"string is %#", myString);
//Put data into a string
NSError *e = nil;
pokeDictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&e];
NSLog(#"dictionary is %#", pokeDictionary);
}
This outputs Json to the console, I can log it into the console like this
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// do something with the data
// receivedData is declared as a method instance elsewhere
NSLog(#"Succeeded!");
NSLog(#"The Pokemon's name is %#", pokeDictionary[#"name"]);
NSLog(#"The Pokemon's attack is %#", pokeDictionary[#"attack"]);
NSLog(#"The Pokemon's speed is %#", pokeDictionary[#"speed"]);
}
However tried to extract Json into text fields like this
{
self.pokemonAttack.text = (#"The Pokemon's speed is %#", pokeDictionary[#"name"]);
self.pokemonAttack.text = (#"The Pokemon's speed is %#", pokeDictionary[#"attack"]);
self.pokemonSpeed.text = (#"The Pokemon's speed is %#", pokeDictionary[#"speed"]);
}
Error is "expression result unused", I guess my main issue is I am not comfortable with objective-c and just hacking around in IOS. For this I apologise and understand the comments of saying do objective-c courses
If you can point me in the right direction I can continue my trial by fire, I guess I should also be moving to swift soon
First of all you should know that the delegate method connection:didReceiveData: may be called multiple times as the connection loads the data incrementally. It may be called once if your returned data is very short, but it will most likely break at some point as you'll end up trying to parse incomplete data.
Regarding the warning you're getting - you just can't format strings like that. You're trying to use string formatting like you're calling NSLog, but the NSLog method does the formatting for you. You need to do something like this:
self.pokemonAttack.text = [NSString stringWithFormat:#"The Pokemon's speed is %#", pokeDictionary[#"speed"]];
I use the CC2541 as the peripheral and iPad mini as the Central. I transfer the data every single second through serial port(the baud rate is 19200) from CC2541 as notify. (Also I tried to transfer data in every 100 ms.It seemed to be same in accuracy)
Here's my code:
- (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.");
if (characteristic == self.rxCharacteristic)
{
NSData *data = [characteristic value];
//NSString* string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSString* string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[self.delegate didReceiveData:string];
}
else if ([characteristic.UUID isEqual:self.class.hardwareRevisionStringUUID])
{
NSString *hwRevision = #"";
const uint8_t *bytes = characteristic.value.bytes;
for (int i = 0; i < characteristic.value.length; i++)
{
NSLog(#"%x", bytes[i]);
hwRevision = [hwRevision stringByAppendingFormat:#"0x%02x, ", bytes[i]];
}
//[self.delegate didReadHardwareRevisionString:[hwRevision substringToIndex:hwRevision.length-2]];
}
}
I tried single-step debug, but it seemed that this code was right. And it's wrong in the transmission from peripheral(because the "string" was wrong when I debugged this). I got about 85% the right data. And 15% data was wrong.
the right data(string) is "12399921" and it will notify as 0x3132333939393231. Often the wrong data will occurs continuously such as 0x31323339393932 and 0x3132E739393231 , 0x31323339393231 and 0x249ACACACA928AFE ...
Maybe it has a way to correct them because it seems that it has some regular there... Or is there any way to avoid the wrong data transmission from the peripheral. Either way will be OK.
Thanks in advance.
Sorry...
I think I found what's wrong by myself.
The accuracy will be much better when baud rate is set to 57600 not 19200.
But it seemed the same for the Android app(it still runs well even no matter the baud rate is 19200 or 57600).
I am currently developing an app that that will be used by association members that are going to a large annual conference.
The app will pull data from a database that is created by the app and populate it via a web service. The web service is split into 8 pages (this will likely go up). Each page represents a table in the database. The app will have several table views that will be populated by data in one or more of the tables in the database.
What I need is a the best method for going through the list of tables, connecting to their respective web service pages and then populating the respective database tables. This updating needs to take place in the background so the UI doesn't become unresponsive and/or show a downloading/updating/waiting kind of status.
So far I have a static array of the table names and have a loop that goes through the array and appends a URL string with the names, for example:
-(void)startUpdate
{
NSArray* tableNames = #[#"speaker", #"exhibitor", #"workshop", #"workshopspeakers", #"schedule", #"location", #"feedback", #"note", #"usage", #"user"];
NSUInteger loopCount = tableNames.count;
for (int i = 0; i < loopCount; ++i){
NSString *tableName = [tableNames objectAtIndex:i];
[self fetchObjectsWithTableName:[tableName mutableCopy] completion:^(NSArray* objects, NSError*error){
if (error) {
} else {
}
}];
}
}
fetchObjectsWithTableName method then has the connections and retrieves the data:
-(void)fetchData:(NSString *)tableName
withCompletion:(completion_t)completionHandler
{
NSString *currentURL = [NSString stringWithFormat:#"https://testapi.someURL.com/api/congress/%#", tableName];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:currentURL]];
[request addValue:#"application/json" forHTTPHeaderField:(#"Accept")];
[NSURLConnection sendAsynchronousRequest:request
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSError* err = error;
NSArray* objects; // final result array as a representation of JSON Array
if (response) {
NSHTTPURLResponse *newResp = (NSHTTPURLResponse*)response;
if (newResp.statusCode == 200) {
NSLog(#"FetchData - Status code = %li", (long)newResp.statusCode);
if ([data length] >0 && error == nil)
{
NSError* localError;
objects = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (objects) {
if (completionHandler) {
completionHandler(objects, nil);
}
//NSLog(#"Objects in current table - %# = %#", tableName, objects);
[self.tables addObject:objects];
// NSLog(#"Tables now = %#", self.tables);
NSLog(#"FetchData - Objects in current table - %# = %lu", tableName, (unsigned long)objects.count);
return;
} else {
err = localError;
}
} else {
NSLog(#"FetchData - objects is empty");
return;
// err = ...
}
}
NSLog(#"FetchData - Response code not 200#");
}
if (objects == nil) {
NSLog(#"FetchData - Nothing found in table: %#", tableName);
//assert(err);
if (completionHandler) {
completionHandler(nil, err);
}
}
}];
}
This currently goes through the array of table names, makes a connection based on each one and pulls back JSON data and stores it in a temporary array 'objects'. I think what I need now is that in each iteration of this 'objects' array is copied to the relevant table in the database, i.e. 'speaker' table name makes a connection: https://testapi.someURL.com/api/congress/speaker and the JSON is entered into the database under the table 'speaker'. How and where do I do that? Will I need to add a completion handler to startUpdate? If so, how? I don't understand completion handlers despite looking at several examples. Thanks.
No, do it in the NSURLConnection completion block after you have updated your temporary storage.
But, change your approach overall.
If you're only willing to change a bit, start using NSOperationQueue to limit the number of connections that you're trying to make at the same time. Preferably also use Core Data.
If you're willing to make a bigger change, definitely move to Core Data and look at using a framework like RestKit to do all of the download, mapping and storage for you.
(note, in both cases you need to set the max concurrent operation limit to prevent the app from flooding the network with requests - a limit of 5 should be good).