|
|
View previous topic :: View next topic |
Author |
Message |
marek816
Joined: 06 Aug 2015 Posts: 6
|
How to decode "r=64;g=128;b=255;on=100;off=1000" |
Posted: Thu Aug 06, 2015 2:36 pm |
|
|
My first post.
New to C so please forgive ignorance.
I'm trying to decode "r=64;g=128;b=255;on=100;off=1000" within a loop to give command and value variable pairs (r and 64 etc) that I can pass to a function to execute based on a switch statement of the command variable.
I tried using the strtok example but when I nest the "=" strtok function within the ";" strtok loop it messes up the ptr reference and I can't figure out how to dereference the inner pointer.
Any ideas?
Thanks in advance!
Marek |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Aug 06, 2015 4:10 pm |
|
|
Change your protocol. Use NMEA-0183 or a truncated version of it.
There is plenty of sample code for parsing that protocol. The data
section will look like this (with ASCII characters):
Code: | 64,128,255,100,1000 |
With just comma separators it's much easier to parse. |
|
|
marek816
Joined: 06 Aug 2015 Posts: 6
|
|
Posted: Thu Aug 06, 2015 4:53 pm |
|
|
The protocol doesn't always arrive in the same order or with all fields present.
I can parse the ; easy but I cannot seem to successfully make a copy of the semi parsed string (r=100) within the strtok loop as reusing the strtok function ends up using the same ptr reference and I can't figure out how to de-refence it... |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Aug 06, 2015 10:08 pm |
|
|
Quote: | when I nest the "=" strtok function within the ";" strtok loop it messes up
the ptr reference and I can't figure out how to dereference the inner pointer. |
Post a small compilable test program that demonstrates the problem.
Example of a small test program:
http://www.ccsinfo.com/forum/viewtopic.php?t=22049&start=1
Yours could be bigger than that, but it must be compilable. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Fri Aug 07, 2015 1:10 am |
|
|
Are the numbers always integer?.
This is arriving on a serial port?.
What are the 'keywords'. You show 'r', 'g', 'b', 'on' & 'off'. Any others?. |
|
|
marek816
Joined: 06 Aug 2015 Posts: 6
|
|
Posted: Fri Aug 07, 2015 4:13 am |
|
|
Here is the code extract, I think all of the declarations should be there:
Command string can be any commands and length in the format command1=a;command2=b;command<x>=c etc etc
****************************************
unsigned char R='R';
unsigned char G='G';
unsigned char B='B';
unsigned int *ptr;
unsigned char command_string[100];
unsigned char parsed_command[25];
unsigned char delimiter[2]=";";
unsigned char equals[2]="=";
ptr=strtok(command_string,delimiter);
while(ptr!=0) {
printf(ptr);
printf("\r\n");
strcpy(*parsed_command,ptr);
execute_command(parsed_command);
command_loc=ptr;
ptr = strtok(0, delimiter); //index through and find ;'s
}
**************this calls the below routine
void execute_command(unsigned int command_value)
{
command_value=0;
unsigned int command=0;
unsigned int value=0;
printf("Executing %s\r\n",command_value); //debug
command=strtok(command_value,equals);
printf("Command=%s\r\n",command); //debug
value=strtok(0,equals);
printf("Value=%s\r\n",value); //debug
command=strlwr(command); //convert to lower case ready for case statements
switch (command)
{
CASE "red":
LEDPWM(R,value);
break;
CASE "green":
LEDPWM(G,value);
break;
CASE "blue":
LEDPWM(B,value);
break;
DEFAULT:
printf("Command %s not recognised\r\n",command);
break;
}
}
This most recent code uses the strcpy function to try to dereference the strtok pointer but it doesn't work. |
|
|
marek816
Joined: 06 Aug 2015 Posts: 6
|
|
Posted: Fri Aug 07, 2015 5:12 am |
|
|
I think the problem might be due to the strtok function in string.h using a static char to save the pointer.
I suspect this gets overwritten by the nested call to strtok in my execute_command function call.
Is there any way to call strtok in it's own memory space so that the saved pointer locations don't overwrite each other?
Thanks |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Fri Aug 07, 2015 7:06 am |
|
|
No, it's trying to use strings as case statements.
This is _not_ a C ability.
C+, yes, but not C....
Key to understand throughout, is that a 'string' is not a fundamental C type. In C, 'strings' are just arrays of characters. So using a string in a case, is asking the language to compare an entire array. Not supported. It doesn't complain, since it just casts the first character of the string to a number and tries the comparison on this. C allows totally flexible type casting, which is a powerful ability, but also means that things like this will not be picked up as an error.
Generally though, don't get hooked on the idea of parsing using strings at all. When you read the line yourself as a human, you don't go and look at the whole line as a 'entity', instead you read along, see the letter 'r', and then the '=' and know that the number following is the value for the red. Do a stream parser, that performs in a similar way. Starts, looks at the first character. 'r', 'b' or 'g', then it knows that the number following is for red/green/blue. Read the '=', then read the number and decode this. If instead of these characters you have an 'o', then look for 'n', or 'f' for the next character. Work through the sequence of characters, so the translation is done as soon as you have read them. No need to store the whole line. |
|
|
marek816
Joined: 06 Aug 2015 Posts: 6
|
|
Posted: Fri Aug 07, 2015 8:53 am |
|
|
My commands might not all be the same number of characters (if I use single characters I limit myself to 26 (A-Z).
Anyway, I just solved it by declaring another case of strtok with the internal static appended with a 1.
Works great now!
Thanks for your suggestions! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Fri Aug 07, 2015 8:56 am |
|
|
No, you don't limit yourself to 26. You test for one character, and then look for the next. Understand that string comparisons are just sequential tests on characters. _Sequential_. |
|
|
ezflyr
Joined: 25 Oct 2010 Posts: 1019 Location: Tewksbury, MA
|
|
Posted: Fri Aug 07, 2015 3:29 pm |
|
|
Hi,
This issue comes up pretty often, so you should 'pay it forward' and post your working code so that anyone searching the forum archives will find it! _________________ John
If it's worth doing, it's worth doing in real hardware! |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri Aug 07, 2015 5:09 pm |
|
|
(if I use single characters I limit myself to 26 (A-Z).
An attempt at pay-forward here.
total agreement on the limitation you think you have ,
but i assume it's a choice, if as you imply, you control the source format.
With control of the data source,
then than are more like 80 choices after you remove your special delimiters.
beyond that, I'm surprised that
(( if you are in control of the source encoding,))
then you are wasting stream space.
Logically, as soon as a valid identifier is found the '=' becomes
redundant and is a waste of good CPU cycles.
re:
"r=64;g=128;b=255;on=100;off=1000"
i routinely use a time - tested stream parser that expects things like:
'r64<cr>g128<cr>b255<cr>o100<cr>f1000<cr>
Adding Upper Case prefixes alone gives you 52 choices
// where <cr> = \r
But there are more than 52 and my parser/receiver
is just as at home with:
'?64<cr>(128<cr>)255<cr>+100<cr>-1000<cr>
and so on.
You can use nearly any non control character as a value prefix really.
I see no reason you could not expand into a variety of characters greater than 0x90 as prefixes too
and of course if all commands are one character, you can pass strings as args.
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Sat Aug 08, 2015 5:08 am |
|
|
Just as an example to perhaps get people thinking in a less 'stringy' way about parsing, this should parse the data involved here, and be massively smaller than the string solution, and also a lot faster:
Code: |
#include <18F4520.h>
#device ADC=10
#use delay(crystal=20000000)
#use rs232(BAUD=9600,PARITY=N,UART1,ERRORS,bits=8,STREAM=PORT1,RECEIVE_BUFFER=16)
//basic setup including serial - using CCS 16 character buffer
#include <ctype.h> //character type code in here
struct
{
int16 red;
int16 green;
int16 blue;
int16 off;
int16 on;
} values = {0,0,0,0,0}; //values retrieved
int1 have_line=FALSE; //flag to say a line has been decoded
//global variables
#define match(x) if (messages[row][ctr]==x)\
{\
/*the character matches*/ \
if (x=='=')\
{\
/*finished search now need to read number*/ \
doing=row+1;\
temp16=0; /*clear the temporary variable*/ \
break; /*and exit*/ \
}\
ctr++; /*move forward to next character to test*/ \
break; /*and exit*/ \
}
//Save duplicated code for character matching
void parse(int8 chr)
{
//generic parser for the incoming data
const char messages[6][5] = { "r=","g=","b=","off=","on=","zzzz" };
//array of tags to search for - zzzz is a marker for end of array
static int8 ctr=0; //character counter preserved between calls
static int8 row=0;
static int16 temp16; //temporary for number being decoded
static enum {search,red,green,blue,off,on} doing = search; //start searching
switch (doing)
{
case search:
chr=tolower(chr); //ensure working with lower case
//here we are checking againt the text array for a match
match(chr);
//might still have a partial match just one character different
if (ctr!=0)
{
row++; //try next row if so
match(chr);
}
//here no match - since the character has not matched the row/column
//being tested, need to start at column 1, and test all rows
ctr=0;
row=0;
do
{
match(chr);
row++;
} while (messages[row][ctr]!='z');
//could exit with match, or end of array
if (messages[row][ctr]=='z')
{
ctr=0; //end of array fail
row=0;
}
break; //back to start
case red:
case green:
case blue:
case on:
case off:
if (isdigit(chr))
{
temp16*=10;
temp16+=chr-'0'; //convert ASCII to numeric - add to running total
break; //and exit
}
//here non numeric character, so number finished
*(((int16 *)&values.red)+doing-1)=temp16; //store value - horrid C trick...
doing=search; //switch back to searching
ctr=0;
row=0;
if (chr=='\n' || chr=='\r')
{
//end of line seen
have_line=TRUE;
}
break;
}
}
void main()
{
enable_interrupts(GLOBAL); //serial uses INT_RDA - defined for you by compiler
while(TRUE)
{
while (kbhit(PORT1))
parse(fgetc(PORT1));
if (have_line)
{
have_line=FALSE;
//do what you want with the RGB values
//Just for demo, print them
fprintf(PORT1,"Red = %ld, Green = %ld, Blue = %ld, on = %ld, off = %ld\n\r", values.red,values.green,values.blue,values.on,values.off);
values.red=values.green=values.blue=values.on=values.off=0; //just for test clear each time
}
//any other code you want here - so long as you get back to
//the top at least every 16 character times (limited by buffer size),
//parser will merrily parse the arriving data
}
}
|
As setup, I'm using the compiler 'built in' serial buffering ability, and just displaying the values 'parsed'. The string is never stored, just the values extracted. It accepts line feed, or carriage return as the line delimeter, and any non numeric character as the end of the input number (going back to searching for the tags). |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|