I have an Arduino device and PC. I tried the following code on the board:
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);
}
The PC application is written in C++ and uses boost library to communicate with the Arduino device. This is the PC code:
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;
}
When I turn on the device and then start the PC application for first time, it sends the string "2" to the device then the read() blocks and never receives data from the Arduino. If I kill the application and start it again without restarting Arduino everything starts to work fine. I tried with async read and the result was the same, the read timeouts on the first message and then everything starting to works fine. The second message which is received in the PC application is with id 1 which means that the Arduino hasn't received the first message. Any idea where is my mistake?
As mentioned in the comment by Hans, your communication protocol is off.
Your Arduino loop basically says:
Read a value. If there is another available value, do something. Wait 0.2 seconds. Then repeat.
Your PC loop says:
Send a '2'. Then read an array of values. Then repeat.
What you have going on, is your Arduino side of your communication is waiting for more values than are being sent. On the Arduino, poll for Serial.avaliable, and then do the read.
On the PC, you probably don't need to change it.
When you disconnect and reconnect there is an extra bit that gets sent to initialize the baud connection, and that was tricking your protocol into working.
EDIT: Be sure to check the settings that the Serial library is connecting with. Here are settings that I've used before to connect to an Arduino board before using the Qt QextSerialPort library.
port->setFlowControl(FLOW_OFF);
port->setParity(PAR_NONE);
port->setDataBits(DATA_8);
port->setStopBits(STOP_1);
port->setTimeout(500);
Here are two links that maybe helpful:
http://www.webalice.it/fede.tft/serial_port/serial_port.html
https://www.google.com/search?q=arduino+and+boost%3A%3Aasio
I've seen the exact same behavior in one of my projects when using Boost asio and an Arduino Uno on Win7. It appears that when Boost opens the serial port it is sending whatever signal resets the Arduino and triggers the bootloader (it appears to be the same thing that happens when uploading code from the Arduino IDE).
There are three possible workarounds I've come up with:
When the Arduino is accidentally reset (when Boost opens the serial port) it takes a couple seconds to start working again. This seems to be the window of time the bootloader would normally give for the Arduino IDE to begin the code upload. I could get things to work a little better by waiting a couple seconds after opening the port:
serial->open(strPortName);
SleepEx(2000,false);
serial->set_option(...);
...
Even with this method I still usually get garbled data on the first read. Usually it will work properly on the next read though.
Lose the bootloader. Program the Arduino through the 6-pin ISP using a different method than the Arduino IDE. This involves ditching the Arduino IDE and going with something like AVR Studio and a hardware ISP programmer of some sort. Refer to this if you go that route: http://www.engblaze.com/tutorial-using-atmel-studio-6-with-arduino-projects/
Switch libraries. I've seen the above behavior using Boost, but prior to using Boost I was using the CTB library ( https://iftools.com/opensource/ctb.en.php ) which worked quite well and did not reset the Arduino when opening the serial port. In my project I originally switched to Boost to get other features provided by it, but I switched back to CTB for communication because it worked better.
A 4th option is to hopefully get a Boost asio expert to inform us how to open the port without resetting the Arduino (since CTB can do it there must be a way, but I'm not expert enough to know how to do it with Boost).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With