If you do not process the type of frame until you've read the double carriage return (CR) at the end and you then insert a null byte after the double CR, then you can extract the frame type information using sscanf()
or other parsing techniques.
char frame_type[32];
char colon[2];
int offset;
if (sscanf(ring_buffer, ":\\%31[^:]%[:]%n", frame_type, colon, &offset) != 2)
…deal with malformatted frame…
Now you have the frame type in frame_type
as a string. The slightly odd %[:]
only matches a colon; what's crucial, though, is that it gets counted as a successful conversion, and the %n
conversion will be valid. If it was changed to:
if (sscanf(":\\%31[^:]:%n", frame_type, &offset) != 1)
…deal with malformatted frame…
you would not know whether the trailing colon was matched or not, and you wouldn't know whether offset
held a valid value (the offset into the string where the preceding character, the colon, was found).
You have at least two frame types; how many are there in total (at the moment, and could there be in the future)? At one level, it doesn't matter. You could use a series of string comparisons against the possible frame types, or you could use a hash of the frame type string and compare that against the set of valid hash values, or you could devise another mechanism.
Once you know which frame type you have, you know what format string to use to read the rest of the data. Since you read up to a double CR, you know that the ending contains that — you don't need to validate it again.
For example, for the sensor frame, you might use:
if (sscanf(ring_buffer + offset, "%.2f;%.2f;%.2f;%d;%d;%u",
&input.temperature, &input.current, &input.voltage,
&input.dutycycle, &input.lightsensor, &input.message) != 6)
…deal with malformatted sensor frame…
Or, for the command frame, you might use:
if (sscanf(ring_buffer + offset, "%u;%.5f\r\r", &input.command, &input.data) != 2)
…deal with malformatted command frame…
The only complicating factor is that you're using a ring buffer. That could mean that your sensor frame is split so that the first 5 bytes are at the end of the ring buffer and the remainder at the start of the buffer. Frankly, if you can afford the space and copying, converting the ring buffer to a regular (linear?) buffer will be easiest. If that is absolutely not an option, then you are probably stuck with not using sscanf()
at all; you will either need to write your own variant of sscanf()
that can be told about the shape of the ring buffer and work with that, or you will have to work character at a time.
Perhaps your custom function is:
int rbscanf(const char *rb_base, int rb_len, int rb_off, const char *format, ...);
The ring buffer starts at rb_base
and is rb_len
bytes long in total; the data starts at &rb_base[rb_off]
. You might need to specify the buffer length and the data length (rb_len
and rb_nbytes
). You might already have a structure describing the ring buffer, in which case, you could pass (a pointer to) that to the function.
Alternatively, if you process the data before you've read the entire frame, then you can validate as you read the bytes. You'll still need to accumulate strings and numbers for conversion. You'll probably use strtol()
and strtod()
rather than atoi()
and atof()
; you will need to know about errors, including trailing unconverted characters, which the atoi()
and atof()
functions cannot tell you about. Care is required with the strtoX()
functions, but they are effective.