CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

How to decode "r=64;g=128;b=255;on=100;off=1000"

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
marek816



Joined: 06 Aug 2015
Posts: 6

View user's profile Send private message

How to decode "r=64;g=128;b=255;on=100;off=1000"
PostPosted: Thu Aug 06, 2015 2:36 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Aug 06, 2015 4:10 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Aug 06, 2015 4:53 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Aug 06, 2015 10:08 pm     Reply with quote

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: 19499

View user's profile Send private message

PostPosted: Fri Aug 07, 2015 1:10 am     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Aug 07, 2015 4:13 am     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Aug 07, 2015 5:12 am     Reply with quote

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: 19499

View user's profile Send private message

PostPosted: Fri Aug 07, 2015 7:06 am     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Aug 07, 2015 8:53 am     Reply with quote

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: 19499

View user's profile Send private message

PostPosted: Fri Aug 07, 2015 8:56 am     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Aug 07, 2015 3:29 pm     Reply with quote

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

View user's profile Send private message AIM Address

PostPosted: Fri Aug 07, 2015 5:09 pm     Reply with quote

(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.

Very Happy Very Happy
Ttelmah



Joined: 11 Mar 2010
Posts: 19499

View user's profile Send private message

PostPosted: Sat Aug 08, 2015 5:08 am     Reply with quote

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).
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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