1

我正在使用 ORSSerialPort (Objective C) 向 Arduino 发送指令。问题是我发送的指令太多,arduino 无法立即处理并溢出其缓冲区(据我所知)。我最初只是在每条指令之后添加延迟,以便它可以处理它,但我想在它处理完每条指令后收到它的响应,以便我知道它已为下一条指令做好准备。

然而,来自 arduino 的回复消息必须由 ORSSerialPortDelegate 接收,这意味着它不会阻止我的主循环继续向 arduino 发送更多指令。

让这样的事情发挥作用的最佳方法是什么?我猜以下代码必须位于与主线程不同的线程中,这样它就不会阻塞主线程,但我希望 updateItems 循环被阻塞,直到它收到继续更新项目的信号。

-(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

    }

}
4

1 回答 1

1

编辑:这个答案现在已经过时了,现在 ORSSerialPort 包括内置的 API来准确处理这里描述的场景。您现在可以执行以下操作:

@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
}

原答案如下:

我所做的是保留要发送的命令队列。在我收到对最近发送的命令的正确回复(或超时等待回复)后,我发送队列中的下一个命令。

你应该能够做这样的事情:

@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

关于此代码/方法的一些注释。它对错误不是很聪明。正如目前所写的那样,它永远不会超时,因此如果 Arduino 出于某种原因未能响应命令,-updateItems它将永远运行。您可以通过添加真正的超时来解决此问题。基本上,您记下您发送命令的时间,然后如果在您发送命令waitingForAck后的 1 秒(或其他时间)内没有设置为“是”,则您会中断-updateItems并适当地处理错误。

这段代码没有涉及多线程,一切都在主线程上完成(ORSSerialPort 在内部使用后台线程,但你不需要关心)。

旋转运行循环-updateItems意味着代码的其他部分将继续运行。调用的代码updateItems会阻塞等待它返回,但你的 UI 会继续响应,等等。如果你需要阻止这种情况,你应该在开头禁用 UI 的相关部分-updateItems,也许显示一个进度指示器,然后完成后重新启用它们。

于 2013-02-08T16:58:40.207 回答