jaholmes:
Here's a good tutorial on how to read strings:
Gammon Forum : Electronics : Microprocessors : How to process incoming serial data without blocking
Once you've gotten the whole string, there are a gazillion ways to chop it up. If the number of array elements transmitted will always be the same, then the laziest thing would just be to let sscanf() do the heavy lifting of matching the string to a template and extracting the numbers, for e.g.:
int R[9], P[6], T[5];
int fieldsReceived;
fieldsReceived = sscanf(
inputString,
"R<%d,%d,%d,%d,%d,%d,%d,%d,%d>P<%d,%d,%d,%d,%d,%d>T<%d,%d,%d,%d,%d>",
&R[0], &R[1], &R[2], &R[3], &R[4], &R[5], &R[6], &R[7], &R[8],
&P[0], &P[1], &P[2], &P[3], &P[4], &P[5],
&T[0], &T[1], &T[2], &T[3], &T[4]
);
if (fieldsReceived == 20)
{
// Hooray! Got good input. Now do something with it.
}
sscanf() is a fairly beefy piece of code, and will eat up a few kb of your sketch space straight away.
Another approach, especially relevant if your data arrays are variable-length, would be to do a state machine. With that approach, you'd process each received character according to the "state", or context in which you received it. For example, if you've received nothing yet, you're in the "expecting array name" state. When an "R" comes along, you advance to the "expecting array start" state." When a "<" comes along, you advance to the "expecting sign or digit" state. Etc. Etc.
Ahhhh this is perfect! Using sscanf worked beautifully and reading the entire string was as simple as copy/pasting the processing code in the gammon link. Yes, all of my arrays will be of static length and on top of that it's okay to wait until the full string comes in, rather than read byte-by-byte under the state machine method. I will actually use the state machine method on the VB.NET side to get current positions back though.
void loop()
{
while (Serial.available () > 0) { processIncomingByte(Serial.read()); }
}
void process_data (const char * data)
{
fieldsReceived = sscanf(data, "R<%d,%d,%d,%d,%d>P<%d,%d,%d>T<%d,%d,%d,%d>E",&R[0], &R[1], &R[2], &R[3], &R[4], &P[0], &P[1], &P[2], &T[0], &T[1], &T[2], &T[3]);
if (fieldsReceived == 12)
{
// For testing to make sure it's read in right
Serial.print(R[0]);
Serial.print(R[1]);
Serial.print(R[2]);
Serial.print(R[3]);
Serial.print(R[4]);
Serial.print(P[0]);
Serial.print(P[1]);
Serial.print(P[2]);
Serial.print(T[0]);
Serial.print(T[1]);
Serial.print(T[2]);
Serial.println(T[3]);
}
}
void processIncomingByte(const byte inByte)
{
static char input_line [MAX_INPUT];
static unsigned int input_pos = 0;
switch (inByte)
{
case 'E': // end of text
input_line [input_pos] = 0; // terminating null byte
// terminator reached! process input_line here ...
process_data(input_line);
// reset buffer for next time
input_pos = 0;
break;
case '\r': // discard carriage return
break;
default:
// keep adding if not full ... allow for terminating null byte
if (input_pos < (MAX_INPUT - 1))
input_line [input_pos++] = inByte;
break;
}
}
From the VB.NET side, you would have your data string generated in the format:
SerialPort1.WriteLine("R<1,2,3,4,5>P<6,7,8>T<9,10,11,12>E")
R, P, and T denote the three different tables, and E dictates end of text.
One question though about the processIncomingByte function. It seems you specify MAX_INPUT as a global variable. In my particular set of arrays, the character length can vary quite a bit. Across the 3 arrays, there are 20-25 entries in each (60-75 total). Now consider that each entry can be 0 to 100 - so either 1-3 chars. So you can see there is quite a bit of variability of (60-75) x (1-3) + delimiters.
Is there any downside to just making the array something huge like MAX_INPUT=600 if there is space available?