Need help - parsing serial input
An old project needs updating... TL;DR: How do I synchronize on and input two different command strings, and parse them appropriately?
I have a ham antenna rotor controller that is driven by a Basic Stamp-II. It emulates a Yaesu GS-232B (at least partly), and it's been working flawlessly for more than 2 decades. Unfortunately, the host software that drives it was updated recently, so the controller needs to follow.
The commands from the host software consist of a "W" followed by two decimal numbers corresponding to the desired azimuth and elevation positions, followed by a carriage return. The string is repeated every few seconds. I'm using the following code to input the data:
serin serial,baud,2000,noserial,[wait("W"),DEC azinput,DEC elinput]
All's fine so far. The code waits for the "W" character, then slurps in the coordinates. The problem is that the host software now also sometimes sends the command "C2" instead of the "W" command, intending to poll my controller for where it's currently pointing. Receiving a C2 should trigger a response of 2 numbers; my controller doesn't respond, so the host times out and stops. I don't believe the sequence of W vs C commands is fixed (e.g. they don't strictly alternate), so I have to respond to either as requested.
Is there a way to wait for (i.e. synchronize to) either the W or C character, then pick up the rest of the string and act as necessary? I think I need something like wait("w"|"C"), but don't see such a construct in the programming guide.
I don't relish the idea of rebuilding something that's otherwise working just fine. Fixing software with hardware is wasteful... Thoughts?
Comments
The problem is the blocking nature of serial with the BASIC Stamp and the lack of RAM. If you have enough memory you could use a timeout to wait for characters and then parse them manual. This will take code, though, and you lose the DEC feature of SERIN.
Ugh, I was afraid of that. Looks like there might be an Arduino in the controller's future, though it would be a lot easier to modify the host driver (rotctld) to not care if the C2 times out. I looked for a config flag to turn it off, but no joy found.
Thanks for the quick reply, Jon. Much appreciated.
The P1 is a good candidate -- and I have a parser library for it that can allow you to deal with up to 10 elements in your command string. It gets used in camera controllers and a medical instrument that uses microscopy to analyze blood. If you can program PBASIC, you can program Spin.
Thanks. Programming is not a problem - software engineer from way back (learned Basic and Fortran in 1973). Just thinking about rebuilding something that's been working for so long is a bit painful. I went back in my archive; version 1.0 of the code was written in Feb 2000; it's currently on version 1.4 from 2013.
What might be interesting is to create a daughter board with a newer processor on it, and a header to plug into the existing Stamp-II socket. Need to count I/O pins and such. But the easiest approach is probably to just modify the host software. After all, what is open source software for if not for looking at it and making changes?
There is always a PIC micro as well. I do this all the time without any issues. However if you don't want to use a PIC, the fundamentals are really all you need and can be applied in just about any micro. I setup a FIFO (First In First Out) buffer and look for a valid sync character. In your case "C", "W", or "C2", etc. If the sync character is present, then I look at the other data to validate the packet, otherwise i ignore the data packet. I also implement a CRC to add robustness to the data packet. I particularly like MODBUS since it is easy to encode and decode. but you could just do a simple XOR of the data BYTES
Exactly what I've always done
BASIC Interpreter: The PicoMite has a CRC function, built-in.
Since I will usually have one project span multiple platforms all in communication with each other, I have a MODBUS CRC generator for each platform:
PIC (MPASM)
Python
Arduino
Javascript
Perl
Sorry, no Basic Stamp or Propeller
Attached is the MODBUS code for Arduino ...
Could you use the STR bytearray modifier? It takes a bit of available ram to hold the array, but the resulting string should be easy to parse.
mystring VAR byte(16) ' for W aaaaa,eeeee CR. or for C2 CR
serin serial,baud,2000,noserial,[STR mystring \15\13]
' string ends with 15 chars or at CR, remaining bytes are nulled.
Then look for W or C at mystring(0). If yes, W or C, parse and respond appropriately. If not either W or C, then wait for a time interval likely to land you in a quiet spot between commands. It wouldn't sync well if the commands arrive too quickly.