bulletproof communication between arduino and PC?

Hi,

So I've been working with serial-based communication between the software on my PC and the Arduino, and vice-versa. Incoming serial commands are fast, but arduino is slow to respond - #12 by friolator - Programming Questions - Arduino Forum

This has worked well but occasionally I have an issue where it seems like the data coming over the serial connection is truncated, giving me useless information on the PC side. So far I haven't had problems that I'm aware of in sending messages to the arduino, it's just getting the messages back to the PC.

One example is that the hardware has a bunch of sensors (9 of them, various types: proximity sensors, photosensors, a rotary encoder, etc). I have a function in my Arduino code that reads all of the sensors and compiles the results into a serialized string, to send back to the PC. There, it's parsed and handled appropriately. However, sometimes I only get data for half the sensors. Other times I get data for all of them. or sometimes 2/3 of them. Sometimes I get the sensor ID, but it truncates before the data for that sensor is written to the string that's sent back to the PC.

So what I'm looking for is an extremely reliable way to pass simple bits of data back and forth between the Arduino and the PC, via a serial connection. I'm open to other methods (ethernet?) if they're more reliable, though using the USB connection certainly seems like the easiest and cleanest way.

I plan to beef up my code on the PC side to check all incoming strings for basic validity (since, for example, I'd know that the sensor check should always return 9 values, and if I get less it's a bad string), but I'd love to know if there's something out there that's more reliable out of the box.

Thanks!

The usual thing to do is post the code you are using on the arduino so it might be evaluated for issues.

Well, I'm asking kind of generically here, but I don't have access to the code at the moment, since it's on my machine at work. However, the code couldn't be much simpler. As the Arduino reads each of the sensors (in this case) a string is compiled in the following format:

LABEL_NAME|0:0_1:0_2:0_3:0_4:0_5:0_6:0_7:0_8:0

After "LABEL_NAME|" comes a series of id:value combinations. These combos are separated by underscores. This string is then printed using Serial.println

On the software on the PC, the incoming serial data is caught and split on the |, the left side of which (the label) determines the function to send the remaining string to. That further breaks down the id:value combinations and does stuff with them.

Mostly it works fine, but fairly often it just doesn't seem to get all of the incoming string from the Arduino- it's truncated at seemingly random locations. I never have a problem with strings sent by the software to the Arduino, just with receiving it.

On the PC side, I'm using RealBasic 2011r1.1 and the built-in serial object to deal with incoming and outgoing data. The baud rate on both PC and Arduino is set to 57600. would it be better to set it slower? faster? All of the other serial parameters are set to match one another, based on the Arduino defaults. Is there something I can set in the serial parameters in the Arduino (and also on the PC) to enable some kind of built-in data verification?

thanks!

Could be running out of sRam
Could be the Serial buffer overflowing
Could be your PC is resetting the com port causing the arduino to reboot
Could be a dodgy connection causing a momentary loss of power (therefore rebooting the arduino)
Could be RF interferance causing your data to get misheard by your PC

Could be running out of sRam
Could be the Serial buffer overflowing

How would I go about testing these?

Not sure the serial buffer would be overflowing, though, since the truncation happens at random locations. Sometimes I get data for 1 or 2 sensors, sometimes for all 9.

Oh, this is an Arduino Mega2560, if it makes a difference

Could be your PC is resetting the com port causing the arduino to reboot
Could be a dodgy connection causing a momentary loss of power (therefore rebooting the arduino)

Don't think it's either of these. When the arduino reboots, certain functions kick in that turn relays on inside the hardware the arduino is controlling. I'd know if the unit rebooted because the AC power relays would all click, and they're not.

Could be RF interferance causing your data to get misheard by your PC

So - try a different USB cable?

You could add this function to your sketch. Then somehow show the value it returns each time. If it returns a very low value (or a gradually diminishing one) then you have a problem.

int get_free_memory()
{
 extern int __heap_start, *__brkval;
 int v;
 return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

Oh another idea. Perhaps somehow you're getting a null terminator in your string.

But seriously! Without seeing the code we're just shooting in the dark.

Are you sure the issue is not on the PC side?

Have a look at the code in this demo. The Arduino code does not have to be used with Python and the Python program could be implemented in any PC language.

If you need to send non-Ascii values the slightly more complex code at the top of that Thread can be used.

It is also easy to add an error-check byte to the data for even greater assurance.

...R

Shpaget:
Are you sure the issue is not on the PC side?

No, I'm not. Here's what I know so far:

If I call the "readSensors" command from the Serial Monitor, over and over and over, it works perfectly every time, returning the full serialized string with all the sensor data.

If I call the same function from the RealBasic application, it works most of the time, but not all, and the location where the data is truncated is always different.

I would assume that when I use the Serial Monitor to issue a command to the Arduino, it's the same mechanism as the application is using, correct? That is, the command is still going over the USB cable to the board just like the application, then getting the board's response in the same manner as the app. If this is the case, then it pretty clearly isolates the problem with the application, and I'll ask over on the RealBasic forums if they have an idea of what's going on. But if the Serial Monitor is doing something special, then it doesn't necessarily rule out the Arduino, right?

Because people have asked, I'll post the code from the Arduino below. I don't think anything is wrong with it though, since it works perfectly in the Serial Monitor, even under a stress test scenario, where I pummel the Arduino with "readSensors" commands over and over in rapid succession.

//set up array for sensor state
int sensorState[9]; 

void Transport::readSensors(){
 sensorState[0] = digitalRead(gateOpenSensor);
 sensorState[1] = digitalRead(gateClosedSensor);
 sensorState[2] = digitalRead(leftPerfSensor);
 sensorState[3] = digitalRead(rightPerfSensor);
 sensorState[4] = digitalRead(leftFilmTensionSensor);
 sensorState[5] = digitalRead(rightFilmTensionSensor);
 sensorState[6] = digitalRead(rotaryEncoder);
 sensorState[7] = digitalRead(cameraPlatformUpSensor);
 sensorState[8] = digitalRead(cameraPlatformDownSensor);

 int i;
 String serializedSensorData = String("SENSOR_DATA|");
 for (i = 0; i < 9; i = i + 1) {
    serializedSensorData += i;
    serializedSensorData += ":";
    serializedSensorData += sensorState[i];
    if (i != 8){  //Prevent a trailing underscore in the serialized string
       serializedSensorData += "_";
    }
 }

 Serial.println(serializedSensorData);
}

In the Arduino's main loop, the incoming serial commands are parsed as described in the link in my first post in this thread, and the appropriate function is called. In this case, transport.readSensors()

So as I mentioned above, the issue doesn't seem to be with this code. It seems to be with the communication out to the application, whether that's on the Arduino, in the USB cable, or with the application, is still a bit of a mystery to me.

I also experimented with setting the baud rate to 115k, and that seemed to improve the situation a bit, but I'm still getting fairly frequent truncated strings in the app. I can't test this in the SerialMonitor, because for some reason it won't communicate with the Arduino above 57600, even with the baud rate set to 115k in the serial monitor window.

The use of String objects can lead to memory problems on the Arduino, with unpredictable effects. I would avoid those and use standard C/C++ string functions like strcat(), etc. See http://www.cplusplus.com/reference/cstring/

jremington:
The use of String objects can lead to memory problems on the Arduino, with unpredictable effects. I would avoid those and use standard C/C++ string functions like strcat(), etc. See http://www.cplusplus.com/reference/cstring/

I understand that, but when you say "unpredictable" does that mean it could affect serial communication? Because the code is doing what it's supposed to, on the Arduino, seemingly without fail. Using the Arduino Serial Monitor, it works perfectly every time.

When I connect to the Arduino via my application, however, the results are unpredictable. This points to either a problem with the application or with the serial communication between the Arduino and the application. But I don't think the code above is behaving weirdly at all. When it's misbehaving, it starts happening almost immediately sometimes, and after several minutes other times.

Don't forget that serial communication is slow - it's a common mistake seen in arduino programs where the programmer has assumed that because one character has arrived, all of them have. Serial.available is used to take care of this.

It sounds perhaps as if you have something similar going on in your RealBasic program - how does it know when all the data from the arduino is ready to be read?

I understand that, but when you say "unpredictable" does that mean it could affect serial communication?

Yes, completely unpredictable. Possible effects even include rebooting of the Arduino.

Even better, the output string could be produced with one or more sprintf() statements.

I'd lose the Strings too, but regardless I'd add Serial.flush() after Serial.println()

One little trick I've sometimes used for an unreliable link is to repeat the string twice for each
line - easier to code than a checksum and human-readable too. On the receive side you
check that each line is even in length and consists of a repeated string. While this wastes
1/2 the bandwidth its pretty easy to see whats gone wrong when things get corrupted.

You could use a checksum instead, but that has to be coded up at both ends and tested,
which is human time, often more valuable than the bandwidth it saves!

If you are sending data samples in the face of an unreliable link there's another trick that
can be handy, which is to send each sample twice or more times - for each new message
you repeat the data from the previous one(s) as well, so that any single lost message
can be recovered from since the data gets resent. Again this isn't bandwidth efficient
but is simple - no handshake is needed so it works for a unidirectional stream. The
receive side just has to work out which sample is which, so sequence numbers or timestamps
are useful to include.

Eventually if you're doing tricks like this there's a point when you simply need to switch
over to using an existing networking protocol (rather than this ad-hocery)!

But if the Serial Monitor is doing something special...

It is not.

What you have described is a bug in RealBasic.

The rest of the advice is, of course, valid.

57600 is not a terrible choice but there are better. When communicating with a PC, 1000000 divided by a power of two are the best choices... 1000000, 500000, 250000, 125000, 62500, etcetera.

Is this a known, documented bug, or an assumption based on my posts? If it's a known issue, do you happen to have a link to anything about this? i can't find anything and have been looking, assuming it was a RealBasic problem. I'm using 2011r1.1, which is not the newest, but not ancient, either.

If I can get some time on the machine this afternoon, I'm going to start going through the various suggestions posted here to see if I can find something that clears it up.

Thanks!

friolator:
This points to either a problem with the application or with the serial communication between the Arduino and the application.

The elephant in the room, is the RB framework. A monstrous, slow, lumbering and on occasions, bug ridden beast. The RB framework uses a co-operative 'eventing' model. Which is another way to say, your application must yield frequently and routinely to the framework, which will then inject significant and variable latencies, between callbacks to your own code.

Your version of RB is from the height of the rapid release model, when regression testing took a back seat to new sales, driven by new features. The Serial control was always quite a popular feature, so I would not expect to find it completely borked in an r1 release...But at that time, uhhuuhhuuh! Who knows?

But I don't think the code above is behaving weirdly at all. When it's misbehaving, it starts happening almost immediately sometimes, and after several minutes other times.

What you are lacking throughout, is flow control and there is not much to be done about that. In lieu of flow control you can (only) try to manage the writes, to correspond with the reads. Writing faster than your RB application can read, will lead to truncation when the serial buffers fill up. The variable latencies within the framework, will cause the truncation to appear just as variable.

There are two vital bits of information you have not provided yet;

  1. How many bytes are you passing to println? It is really quite important.

  2. How are you reading the data in RB? Failing to co-operate with the RB framework, can cause truncation. You should avoid loops, whenever you can and return from your code, just as soon as you can. You have one thread of execution. When your RB code is being executed, the RB framework is not reading hardware buffers or doing anything else it needs to do.

If your RB application is polling the serial control in a loop, don't. So called 'tight loops' starve the framework of runtime. If you are using the DataAvailable event handler (which you should), you need to buffer reads between the event being raised by the framework. Like every other function you write in RB, you should return from DataAvailable, promptly and completely.

friolator:
...or an assumption based on my posts?

Assumption? No.

Logical conclusion based on the symptoms you have described and my own experiences. Yes.

MattS-UK:

  1. How many bytes are you passing to println? It is really quite important.

The readSensor function described above returns the following (actual values are different, but the message size is always this big, for this particular function). Other functions in the future may need to send more, but most likely this will be the one that sends the most information back to the application:

SENSOR_DATA|0:0_1:0_2:0_3:0_4:0_5:0_6:0_7:0_8:0

So - 46 bytes, by my count.

  1. How are you reading the data in RB? Failing to co-operate with the RB framework, can cause truncation. You should avoid loops, whenever you can and return from your code, just as soon as you can. You have one thread of execution. When your RB code is being executed, the RB framework is not reading hardware buffers or doing anything else it needs to do.

If your RB application is polling the serial control in a loop, don't. So called 'tight loops' starve the framework of runtime. If you are using the DataAvailable event handler (which you should), you need to buffer reads between the event being raised by the framework. Like every other function you write in RB, you should return from DataAvailable, promptly and completely.

There are no loops in the DataAvailable handler. The entirety of the code in that handler is as follows:

dim incomingString(2) as String
incomingString = Split(arduinoSerialComm.ReadAll, "|")
  
//see if the Label is among the ones we don't want to display in the UI
if (incomingString(0) = "SENSOR_DATA")  Then
   //send this to the readSensors method for processing 
   dim serializedStateValues as String = incomingString(1)
   readSensorStates(serializedStateValues)
else
   serialMessagesDisplay.Text = serialMessagesDisplay.Text + arduinoSerialComm.ReadAll
end if
  
Exception err
   if err isa OutOfBoundsException then
      window1.serialMessagesDisplay.Text = "ERROR: Out Of Bounds Exception" + EndOfLine
   end if

...so all I'm doing is responding with the above if/then in the DataAvailable handler. The plan is to add a few more, but the sensor feedback is the main one. other types of incoming (to the application) serial data will be small - mostly along the lines of "some process is complete," so I'd imagine something in the 8-10 byte range.

In any case, this is then passed along to the readSensorStates() method, which for the moment only does this:

dim sensorStates() as String
sensorStates = Split(serializedStateValues, "_")
    
Window1.serialMessagesDisplay.Text = serializedStateValues +EndOfLine

There's actually more code that processes the data and triggers other events, but for debugging purposes I've got that commented out and I'm still seeing truncation with this simple code, so I didn't include the other stuff here.