Parsing the fields is a little more involved; the first
thing we need to know is where a particular field is
located in the message string. For this, the field_pos
method is used. This takes a zero-indexed field number
and returns its position. If the field number passed is not
valid for the message, then -1 is returned:
pri field_pos(fn) | idx
if (fn < 0)
idx := 0
repeat while (fn > 0)
idx := -1
The position of the desired field is located by iterating
through the characters in the message, decrementing the
field number value when a separator is encountered.
When the field number hits zero, we're sitting on the
position of the desired field in the message and that is
returned to the caller.
The final step is to extract and convert the various
fields and make them available to the calling application;
this is handled with the parse_fields method:
pri parse_fields | pos, fc, idx
taddr := strings.asc2val(@queue)
pos := field_pos(1)
cmd := strings.asc2val(@queue+pos)
pos := field_pos(2)
if (strings.instr(@queue, string(STR_MSG))
pos := field_pos(3)
pcount := strings.asc2val(@queue+pos)
if (pcount > 0)
pcount <#= P_MAX
repeat idx from 0 to pcount-1
pos := field_pos(3+idx)
param[idx] := strings.asc2val
At the top, we're going to reset all the internal
variables; this sets all values returned to the user to zero
since we don't know how many parameters are included
in the next message. We use zero because the parser
allows for positive and negative values (signed, 31 bits).
The first thing extracted is the target address for the
message. This is removed from the message using the
asc2val method from the strings object. Over the last year
or so, I've been collecting and translating useful string
functions and I finally stuck them all into a handy object.
The target address is in field 0 of the message, so we don't
have to use field_pos for this one. We do have to skip
over the header, though, as the parser only allows for
spaces, identifiers, and numeric characters.
The next field extracted is the command. We do need
to use field_pos here because we don't know how long
the target address field is. That's the nice thing about this
simple little protocol: The fields can be variable width, so
entering values in a terminal program is quite easy.
The final mandatory extraction is the parameters
count. You'll remember that this field has a special case:
When we transmit a string message, the parameters count
is set to "@." We can look for this using the instr method
and if it's found, the string in field three is copied to an
internal byte array called tfield. The parse_fields method
exits with a return value of MSG_STR ( 2).
Simple command messages will have a parameter
count of zero, so we can bypass the parameters extraction.
When a message does have parameters, a loop finds and
extracts them from the message. Numeric messages return
a value of MSG_NUM (1) from parse_fields.
Okay, time to put this stuff to work. Here's an update
to the demo loop I presented earlier. As you can see, I'm
running the loop using a fixed timing value and using that
to drive a simple timer/rtc that our project can use:
pub main | t, c
t := cnt
c := serial.rxcheck