Parser output problem
DiverBob
Posts: 1,110
I'm running into a problem that's got me stumped. I'm parsing an input string formatted as '$,1,2,345'. The $ is the start character, the '1' is a position number (value 1 thru 6), the '2' a command code (currently value is 1, 2, or 3), and the '345' is a parameter (normally a value in degrees of rotation).
The parser is correctly breaking up the values and I get the correct output value to the serial output but I'm getting an incorrect value when I assign the parameter value to a global variable (line where parameter := @Buffer[5]). At the bottom of the code snippet is the results I'm getting from the output. The first line is correctly displaying the output angle (112 degrees) but the FemurAngle value is 22723. I've tried several methods to pass the correct value (112) but ran out of ideas. Hopefully someone can see what I'm doing wrong. I've also attached the full listing of the program if you want to see how this fits into the rest of the motor control code.
The parser is correctly breaking up the values and I get the correct output value to the serial output but I'm getting an incorrect value when I assign the parameter value to a global variable (line where parameter := @Buffer[5]). At the bottom of the code snippet is the results I'm getting from the output. The first line is correctly displaying the output angle (112 degrees) but the FemurAngle value is 22723. I've tried several methods to pass the correct value (112) but ran out of ideas. Hopefully someone can see what I'm doing wrong. I've also attached the full listing of the program if you want to see how this fits into the rest of the motor control code.
pub EnterCommand | teststr, pattern, resaddr, rslt, n, legnumber, command, parameter ' input data from master ' input string format: "$,1,2,345" ' routine derived from Perry James Mole GPS parser for GPS decoding - see thread on GPS Parsing Problems 9/9/2013 io.tx(0,16) Null[0] := 0 repeat longfill(buffer,10,0) repeat while Rx <>= "$" Rx := io.rx(1) cptr := 0 repeat while Rx <>= CR Rx := io.rx(1) if RX == "," buffer[cptr++] := 0 else buffer[cptr++] := Rx if buffer[1] == "6" if buffer[3] == "1" io.str(0,string(13,"Femur: ")) parameter := @buffer[5] io.str(0,parameter) if FemurDone == 0 FemurAngle := parameter io.str(0,string(" FemurAngle: ")) io.dec(0,FemurAngle) FemurDone := 1 if buffer[3] == "2" ' io.str(0,string(13,"Tibia: ")) parameter := @buffer[5] ' io.str(0,parameter) if buffer[3] == "3" ' io.str(0,string(13,"Coxa: ")) parameter := @buffer[5] ' io.str(0,parameter) Output from above routine: Femur: 112 FemurAngle: 22723Hexapod IK Test 4a - Archive [Date 2014.12.11 Time 20.26].zip
Comments
I am following your progress for a while now. I really like your project. So if I sound harsh, please excuse me - English is my third language...
What is <>= supposed to do ? Looks quite wrong to my eyes. This might check for <> but set RX to"$" ?
Rx is a global Variable I assume. Are you sure about the initial value? Why you are using a global Variable at all?
So I would replace thru
to make it more readable. Both variants may do the same. Its just me being German. I stick on details.
Anyways at that point you have a 0 in buffer[0] - the "," after "$" and your first argument in buffer[1] as long as it is a single character. So far so good. buffer[2] is 0. Same works for your second argument buffer[3] as long as it is a single character. buffer[4] is 0. Now you pass more then one byte as argument. But it is still a text (ASCI). It is a string.
Correctly you are using @buffer[5] to resolve the address of the position in the buffer into your Variable parameter. And since your buffer is zero-filled after them bytes is a 0 so the string is terminated. So far so good.
so your first output works perfectly fine:
but the next is wrong. You are outputting the address of the string containing the value not the value itself.
Solution:
you need to convert the string value at that address to the binary value to use it as 'normal' variable. "123" is not 123.
If you subtract 48 from each single ASCI byte you get decimal 0 from string "0" and decimal 9 from string "9". To parse the number you need to convert the first byte and if not done, multiply by 10 and add the next converted byte.
A simpler way MAY be a object "numbers" in the standard Parallax Library from the PropTool. Just include it. It provides various methods. You will be interested in the 4st one from the top: PUB FromStr(StrAddr, Format)
something like this
should do it. You need to include numbers into your OBJ section and figure out what 'format' is or just build your own string to long converter as I did because I never grooked 'numbers' ...
The function you need inside 'numbers' is this one:
If you mentally throw away all the un-needed you may see the algorithm.
I hope this helps a bit
Enjoy!
Mike
for readability and to be sure
It looks like you want to control three servos for each of the six legs of a hexapod. If that is the case I would suggest storing the angle (or servo pulse width) data in an array of 18 bytes or words. Use the first two parameters to calculate the offset into the array. This way you only need one routine to handle the conversion for all the segments of all the limbs.
I'm going to sleep on this and then delete the parser and just re-code it from scratch using the ideas from above and see what happens. This should be a fairly simple parser and output of an angle but it's becoming a bigger problem than I anticipated. I may go back to the original, more complex parser as I didn't have this issue there.
Thanks again Jon for the help!
Bob Sweeney
The issue that is happening right now is the GetCmd routine appears to be outputting differently when I add debug code vice when the debug code is disabled. The code is located in the Q4 Rx V3.61 archive attached. I'm not understanding how this affects the routine and results in different outputs. The value "ÿ" corresponds to 255 according to my ASCII table, not sure what is causing it to appear during the parsing. I copied some of the serial terminal output values into the CODE location below for both situations.
Q4 Rx V3.61 - Archive [Date 2015.01.04 Time 13.50].zip
Hexapod IK Test 5 - Archive [Date 2015.01.04 Time 13.51].zip
It will return a POSITIVE value from 0 to 255 (your usable byte) or -1 for "no character available".
So if you want to wait for a character you need to use just uarts.rx. not uart.rxcheck. Or you need to check for the "no character there" condition (-1)
You are now 'pressing' or 'casting' your rxCheck value into a Byte, thus loosing the distinction between -1 and 255..
255 might be a usable value in your protocol, or might not. you can decide this. If you exclude 255 as a meaningful value in your protocol you can simply ignore this value.
You can also use uarts.rx, witch will wait for the next transmitted byte or rxtime to wait for a specified amount of time.
rxCheck and rxTime allow you to check for a timeout condition and restart your communication if - say - incomplete messages are received and your leg controller is waiting for some byte, lost thru noise or whatever.
IMHO the proper way to handle this (with a robot of this size) is to restart you leg-controller in case of communication problems, informing the master controller about the current positions.
Failure tolerance is a quite important part on 'heavy' equipment like you are working with and should be included into the main design.
Usually serial communication runs perfect between some microcontrollers in close proximity. But **** happens. It does. And you need to take care of that problem in your software. Re-syncing in case of error.
I do understand that you are still fighting with the basic communication between multiple propeller controllers. So step back for a moment and look at the overall design. Each of your leg controllers needs to be sure to have valid communication to the master. If not **** happens.
By now (besides not checking for "no Byte there" them ÿ) you are checking the serial stream for your header "$" and then read in up to 'len' bytes or until you hit CR or 0. Not sure why you check for BackSpace here, this makes just sense for testing with a serial terminal, your master controller will not send BackSpace at all, right?
But you do not check for '**** happened'. And that is where rxTime comes in or rxCheck when you need to do something other in between and are not able to wait as rx and rxtime do.
English is my third language, so I somehow need to talk AROUND the main point, because I am not able to write the main point down as I could do in German.
But I will try anyways because I want to see this thing walking. GO Bob GO!
One way to handle '**** happened' is thru exceptions. Spin can 'throw' and 'catch' them and @Chip did a nice job there, as usual. So if your leg controller decides that all of them inputs make no sense he can in any procedure throw an error(code), catch'd by the main routine. That then can decide to 'reboot' and inform the master.
Highly recommended for you. The last resort before the leg got 'hanging'. Reboot.
Enjoy!
Mike
Basically check for uarts.rxCheck(xx)==-1
if negative do not add to your buffer, just ignore. Nothing there.
figure out some timeout procedure to avoid getting stuck while waiting for some byte in the loop.
I'll try that out and see how it all shakes out, that definitely explains the odd results I was seeing. I agree that good communications is critical to the operation of this robot, the system will be sending commands fast and furiously and can't afford a missed or misinterpreted command. I always add in error checking but not until I get the basic code operational. The leg movement code mainly contains error checking code. The feedback loop I'm working on now is part of the overall schema to validate commands and the successful completion of the commands.
Thanks for taking a look at the code and catching the mistake!