Code down below. The "Serial" part at least. Showing the entire code would be quite big. I have to notice the Arduino only receives data, it doesn't write. Normally a message appears each second. But... since the message is triggered on the other system by sensors, it may happen it could be triggered twice. A rare occasion, but not impossible.
I haven't made the suggested start/stop character changes yet, it's just "as it is" right now. It wouldn't surprise me if there is something stupid inside hehe. One thing it does wrong already, is trying to convert the received data directly to work variables. This could cause an error when for example "atoi( )" is called. But I don't think that would disrupt the communication though...
Resetting or skipping junk should be possible. In fact I changed the code earlier today so I could pull out the cable, re-insert, and just continue. Nevertheless, being disrupted means the machine misses a few camera-checks. As said, its not critical (we're not producing Rocket components). If it would happen a few times a week, well, I can live with that. If it happens once per few hours or so, it becomes a different story. I know you guys can't just tell me the chance on faults, as it really depends on the environment, wiring, the other device, and so on. But maybe some general hints? Also, I got to convince the Arduino isn't worse than a PLC for this particular case... unless the hardware (or Arduino Serial library) actually could be a problem source...
Not using a string library btw, just char buffers.
const int SERIAL_TIMEOUT = 15; // = 3 seconds time out
int offline = SERIAL_TIMEOUT;
void pollNVisionCommunication()
{
/**** 1. TIME OUT CHECK ****/
// This function is called each 200 msec. Normally we receive something each 500 to 1400 msec.
// If nothing was received for 3 seconds, either the machine conveyor belt has been
// paused, or the communication is disrupted
if (progState != PROGSTATE_PAUSED)
{
if (--offline < 0)
{
offline = 0;
progState = PROGSTATE_IDLE; // Lost connection, or still waiting
// If the belt is still turning, it means we're not receiving (valid) messages
// Ifso, reset the connection
if ( !machineStopped )
{
Serial.println( "ERROR, Serial3 Reset" ); // Send diagnostic message via XBee
Serial3.end();
delay(25);
Serial3.begin( 9600 );
Serial3.flush();
offline = SERIAL_TIMEOUT; // 3 seconds
} // Reset Serial 3
} //
} // belt not paused
/**** 2. TIME OUT CHECK ****/
// After each trigger (belt must move), the camera sends this string:
// 0 1 2 3 4 5 6 7 8 9
// jobname,photoDelay,blisterDiscard,rowCount,charge,row1Result,row2Result,row3Result,row4Result,row5Result
// for example
// "ProductX,250,23,2,4731ab-cx,1,1,1,0,0"
//
// * The last 3 bits (row3/4/5) are optional and may not be included
char txBuffer[MAXJOBNAMELEN+1];
int comma = 0;
int indx = 0;
bool jobOrChargeChanged = false;
bool validMessage = false;
for (int i=0; i<=MAXJOBNAMELEN; i++) txBuffer[i] = 0; // Clear tx buffer
int avail = Serial3.available();
while ( avail > 0 && indx < MAXJOBNAMELEN &&
comma < 10 // If the message has more than 10 comma's, its garbage for sure
)
{
char c = Serial3.read();
if ( c == 13 || c == 10 || c == 0 ) break; /* terminator, exit receive loop */ else
if ( c == ',')
{ // In case of numeric data, convert here
// Otherwise, just reset the "index", and increment "comma" to fill the next field
if (comma==1)
{
cPhotoDelay = max( 0, min( 2000, atoi( txBuffer) )); // <-- ! goes wrong possibly
for (int i=0; i<=MAXJOBNAMELEN; i++) txBuffer[i] = 0; // clean
} else
if (comma==2)
{
cDiscardBlst = max( 1, min( atoi( txBuffer ), MAX_ROWBLISTERS-2 )); <-- ! goes wrong possibly
}
++comma;
indx = 0;
} else
switch (comma)
{
case 0 : { // Read chars for "JobName"
if (cJobName[indx] != c) jobOrChargeChanged = true;
cJobName[indx++] = c;
break;
}
case 1 : { txBuffer[indx++] = c; break; } // Read chars for PhotoDelay
case 2 : { txBuffer[indx++] = c; break; } // Read chars for Blister discard count
case 3 : { cRowCount = max( 1, min( 5, byte(c) - 48)); break; // single char rowcount }
case 4 : {
if (cCharge[indx] != c) jobOrChargeChanged = true;
cCharge[indx++] = c;
offline = SERIAL_TIMEOUT; // Received something with at least 4 commas, thus a valid message probably
validMessage = true;
break;
}
case 5 : { cRow1Fault = (c != 48); break; }
case 6 : { cRow2Fault = (c != 48); break; }
case 7 : { cRow3Fault = (c != 48); break; }
case 8 : { cRow4Fault = (c != 48); break; }
case 9 : { cRow5Fault = (c != 48); break; }
}
// Next char
avail = Serial3.available();
if ( avail==0 ) // wait a short moment, maybe the host is still sending
{
delay(150);
avail = Serial3.available();
}
}
/**** 3. FINALIZE ****/
// Change job in case the received jobName was different than the previous one
Serial3.flush();
if (jobOrChargeChanged && validMessage)
{
resetJob( );
}
} // pollNVisionCommunication