Question

I am using ORSSerialPort (Objective C) to send instructions to an Arduino. The problem was that the instructions I was sending are too much for the arduino to handle at once and overflows its buffer (as far as I can tell). I was originally just adding a delay after each instruction so it could handle it, but I would like to receive a response from it after it processes each instruction so that I know it is ready for the next one.

However the reply message from the arduino has to be picked up by ORSSerialPortDelegate which means that it does not stop my main loop from continuing on sending more instructions to the arduino.

What is the best way to go about getting something like this to work? The following code would have to be in a separate thread from the main one I guess so that it doesn't block the main thread but I would like the updateItems loop to be blocked until it gets a signal to continue updating the items.

-(void)updateItems
{
//Loop through each item.
   //Get the next instruction in the item's buffer
   //execute (send via serial to arduino)
   //wait for acknowledged reply --How do I wait for an acknowledgement?

}

- (void)serialPort:(ORSSerialPort *)serialPort didReceiveData:(NSData *)data
{
    NSLog(@"Received Data");

    NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    if([string isEqualTo:@"0\r\n"]) //ack signal
    {
        //Serial port is now ready to accept another instruction

    }

}
Was it helpful?

Solution

EDIT: This answer is old now, and these days ORSSerialPort includes built in API to handle exactly the scenario described here. You can now do something like:

@implementation MyClass

- (void)updateItems
{
    // We can just send these all in a for loop. ORSSerialPort will handle 
    // queuing them and waiting for a response to each before going on to the next request
    for (NSData *command in self.commands) {
        ORSSerialPacketDescriptor *response = 
            [[ORSSerialPacketDescriptor alloc] initWithPacketData:[@"foo" dataUsingEncoding:NSASCIIStringEncoding] userInfo:nil];
        ORSSerialRequest *request = [ORSSerialRequest requestWithDataToSend:command userInfo:nil timeoutInterval:1.0 responseDescriptor:response];
        [self.serialPort sendRequest:request];
    }
}

- (void)serialPort:(ORSSerialPort *)serialPort didReceiveResponse:(NSData *)data toRequest:(ORSSerialRequest *)request
{
    NSLog(@"Received response: %@ to request: %@", data, request.dataToSend);
    if (serialPort.queuedRequests.count == 0) {
        // All done! Do whatever comes next.
    }
}

- (void)serialPort:(ORSSerialPort *)serialPort requestDidTimeout:(ORSSerialRequest *)request
{
    // Something went wrong!
    [self.serialPort cancelAllQueuedRequests]; // Stop sending the rest of the commands
}

Original answer below:

What I do is to keep a queue of commands to be sent. After I receive a proper reply to the most recently sent command (or timeout waiting for a reply), I send the next command in the queue.

You should be able to do something like this:

@interface MyClass ()
    @property BOOL waitingForAck;
@end

@implementation MyClass

- (void)updateItems
{
    for (NSData *command in self.commands) {
        [self.serialPort sendData:command];
        self.waitingForAck = YES;
        while (self.waitingForAck) {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                                             beforeDate:[NSDate distantFuture]];
        }
    }
}

- (void)serialPort:(ORSSerialPort *)serialPort didReceiveData:(NSData *)data
{
    NSLog(@"Received Data");

    NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    if([string isEqualTo:@"0\r\n"]) //ack signal
    {
        //Serial port is now ready to accept another instruction
        self.waitingForAck = NO;
    }
}

@end

A couple notes about this code/approach. It's not very smart about errors. As currently written, it will never time out, so if the Arduino fails to respond to a command for whatever reason, -updateItems will run forever. You can fix that by adding a real timeout. Basically, you take note of the time you sent the command, then if waitingForAck hasn't been set to YES within 1 second (or whatever) of the time you sent the command, you break out of -updateItems and handle the error appropriately.

There is no multithreading involved in this code, everything is done on the main thread (ORSSerialPort uses background threads internally, but you don't need to care about that).

Spinning the run loop in -updateItems means that other parts of your code will continue to run. The code that calls updateItems will block waiting for it to return, but your UI will continue to respond, etc. If you need to prevent that, you should disable the relevant parts of your UI at the beginning of -updateItems, perhaps showing a progress indicator, then reenable them after it finishes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top