我有一个 Arduino 设备和 PC。我在板上尝试了以下代码:
gpsdata data;
char needtosend;
void setup() {
Serial.begin(9600);
Serial.flush();
data.id = 0;
data.src= 500;
data.lat= 1;
data.lon= 2;
data.alt= 3;
strcpy(data.date, "test date format");
}
void loop() {
if(Serial.available() > 0)
{
needtosend = Serial.read();
if ( needtosend == '2')
{
data.id = ++data.id % 10;
}
byte* buff = (byte*)&data;
Serial.write(buff, sizeof(data));
}
delay (200);
}
PC应用程序是用C++编写的,并使用boost库与Arduino设备进行通信。这是电脑代码:
Serial serial("/dev/ttyUSB0",9600);
gpsdata *data;
char *values = new char[sizeof(gpsdata)];
while(true)
{
try {
serial.writeString("2",1);
serial.read(values,sizeof(gpsdata));
data = (gpsdata *)values;
cout<<data->id<<endl;
}
catch(boost::system::system_error& e)
{
cout<<"Error: "<<e.what()<<endl;
}
catch(timeout_exception& e)
{
cout<<"Error: "<<e.what()<<endl;
}
}
delete[] values;
Serial::Serial(std::string port, unsigned int baud_rate): io(), serial(io,port)
{
serial.set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
}
void Serial::read(char *data, size_t size)
{
boost::asio::read(serial,boost::asio::buffer(data,size));
}
void Serial::writeString(const char* s, int length)
{
boost::asio::write(serial,boost::asio::buffer(s,length))<<std::endl;
}
当我打开设备,然后第一次启动 PC 应用程序时,它会向设备发送字符串“2”,然后 read() 会阻塞,并且永远不会从 Arduino 接收数据。如果我终止应用程序并重新启动它而不重新启动 Arduino,一切都会开始正常工作。我尝试使用异步读取,结果是相同的,第一条消息的读取超时,然后一切开始正常工作。 PC 应用程序中收到的第二条消息的 id 为 1,这意味着 Arduino 尚未收到第一条消息。知道我的错误在哪里吗?
正如 Hans 的评论中提到的,您的通信协议已关闭。
你的 Arduino 循环基本上说:
读取一个值。如果还有其他可用值,请做某事。等待 0.2秒。然后重复。
您的电脑循环显示:
发送“2”。然后读取值数组。然后重复。
您所发生的情况是,您的通信的 Arduino 端正在等待比发送的值更多的值。 在 Arduino 上,轮询Serial.avaliable,然后进行读取。
在 PC 上,您可能不需要更改它。
当您断开连接并重新连接时,会发送一个额外的位来初始化波特率连接,这会欺骗您的协议工作。
编辑: 请务必检查串行库连接的设置。以下是我在使用 Qt QextSerialPort 库之前连接到 Arduino 板时使用的设置。
port->setFlowControl(FLOW_OFF);
port->setParity(PAR_NONE);
port->setDataBits(DATA_8);
port->setStopBits(STOP_1);
port->setTimeout(500);
这里有两个可能有用的链接:
http://www.webalice.it/fede.tft/serial_port/serial_port.html
当我在 Win7 上使用 Boost asio 和 Arduino Uno 时,我在我的一个项目中看到了完全相同的行为。 看起来,当 Boost 打开串行端口时,它会发送任何信号来重置 Arduino 并触发引导加载程序(这似乎与从 Arduino IDE 上传代码时发生的情况相同)。
我想出了三种可能的解决方法:
当 Arduino 意外重置时(当 Boost 打开串行端口时),需要几秒钟才能再次开始工作。 这似乎是引导加载程序通常为 Arduino IDE 提供的开始代码上传的时间窗口。 打开端口后等待几秒钟,我可以让事情工作得更好一些:
serial->open(strPortName);
SleepEx(2000,false);
serial->set_option(...);
...
即使使用这种方法,我在第一次读取时通常仍然会得到乱码数据。 通常它会在下次阅读时正常工作。
丢失引导加载程序。 使用与 Arduino IDE 不同的方法通过 6 引脚 ISP 对 Arduino 进行编程。 这涉及放弃 Arduino IDE,转而使用 AVR Studio 和某种硬件 ISP 程序员。 如果您选择此路线,请参阅此内容:http://www.engblaze.com/tutorial-using-atmel-studio-6-with-arduino-projects/
切换库。 我已经使用 Boost 看到了上述行为,但在使用 Boost 之前,我使用的是 CTB 库(https://iftools.com/opensource/ctb.en.php),它工作得很好并且没有重置 Arduino打开串口时。 在我的项目中,我最初切换到 Boost 是为了获得它提供的其他功能,但我又切换回 CTB 进行通信,因为它效果更好。
第四个选择是希望让 Boost asio 专家告诉我们如何在不重置 Arduino 的情况下打开端口(既然 CTB 可以做到,那么一定有办法,但我不够专业,不知道如何用升压)。
Arduino 网站上从硬件角度对此进行了非常彻底的处理:
http://playground.arduino.cc/Main/DisablingAutoResetOnSerialConnection
它也解释了这个问题的一些硬件解决方案,包括:
只需导入时间库并在
time.sleep(2)
行后添加 Serial.begin(9600);
即可。
朋友!我注意到同样的问题,只有在 PC 上我正在用 Delphi 编写程序。一切似乎都是正确的,我尝试在PC和arduino程序的不同位置添加额外的时间延迟和缓冲区清理,但我仍然没有弄清楚问题是什么。一切都和你写的一模一样——打开arduino后,PC上程序的第一个包不会到达它,但是如果你重新启动PC上的程序,第一个包就会开始到达它。我找到的唯一解决方案是从 PC 发送第一个包裹两次。