Replacment for sscanf

Hi guys. I need function that from data(from Serial port on Arduino) extract "commands" and values.

Data should look like this
"v=125,rpm=50,dist=1505170,vbatt=33,temp=25"

v, rpm, dist, vbatt and temp are "commands" and they are not fixed. Sometime data will be

"v=125"
sometimes
"rpm=125,temp=20"

In general, format is "X=Y" where X is the command and Y is the value. Commands are separated with ,(comma).

Now, sscanf library uses a lot of ROM, that is why I have to leave it.
I made function. It works fine for splitting up commands from data, but not commands internally.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
 char data[] = "v=35,rpm=80,temp=25,dist=31051999,vbat=33";
 char *dataPart;
 dataPart = strtok(data, ",");
 while (dataPart != NULL)
 {
  long cmdValue = 0;
  char *cmd;
  printf("Data part: %s\n", dataPart);
  cmd = strtok(dataPart, "=");
  cmdValue = atoi(strtok(NULL, "="));
  printf("%s | %d\n", cmd, cmdValue);
  printf("Data part AGAIN: %s\n\n", dataPart);
  dataPart = strtok(NULL, ",");
 }



Output is

Data part: v=35
v | 35
Data part AGAIN: v


Why it doesn't work in the way I thought it will and how to make it works?
before we fix it, isnt strtok also heavy?
what exactly do you want back?
a string with "v=35", and then process that and get a string with rpm=80, and so on?

you can do that with something much lighter. here is a 10 second crack at it, hopefully the idea is here even if the code isnt 100%
1
2
3
4
5
6
7
8
9
10
11
char * cp = data;
do
{
   if(*cp == ',') *cp = 0; cp++; //replace every comma with end of string 0 marker. 
}
while(cp);
char * cp2 = data;
do
 --process cp2;   //idea here is to iterate the substrings you created with the zeros above. 
 -- increment cp2 to 1 past the next zero. 
while (cp2 <=cp)


that is
it now processes
"v=35\0rpm=80\0temp=25\0 ..."

v=35\0 process
rpm=80\0 process
temp=25\0 process... etc
Last edited on
I don't know about strtok, but string.h doesn't use ROM at all. Sscanf eats ROM(from 27% ROM usage to over 60% of usage).

Let me explain again.

Arduino will get data through Serial port(Arduino thing, won't talk about it).

That data will be formatted like this

"X=Y"

X is the command, Y is the value. Each command is separated with comma.



Now I want to split all commands so I get from this

"v=10,rpm=50"

to this

"v=10"

After that process, I want to separate each command from its value, store them into variables so later I can do something like this

if(!strcmp(cmd, "X_cmd")) { // do something with value }

And use that process for every command. I hope it's clear now.
Last edited on
> dataPart = strtok(data, ",");
...
> cmd = strtok(dataPart, "=");
> cmdValue = atoi(strtok(NULL, "="));
...
> dataPart = strtok(NULL, ",");
You can't use strtok() to tokenise two strings at the same time, even if one of those strings is embedded inside the other.

For example, if the input is always well-formed, then perhaps you could do this.
1
2
3
4
5
6
7
8
char data[] = "v=35,rpm=80,temp=25,dist=31051999,vbat=33";
char *cmd;
cmd = strtok(data, ",=");
while (cmd != NULL) {
    char *param = strtok(NULL, ",=");
    // do stuff with cmd and param
    cmd = strtok(NULL, ",=");
}

Which is fine, so long as say "v=35=rpm=80=temp=25,dist,31051999,vbat=33" either never happens or isn't an error.

If you want alternate comma equals tokens, you could look at the POSIX function strtok_r, which does allow you to parse multiple strings at the same time.
https://linux.die.net/man/3/strtok_r

Or perhaps separate out all the C=V commands first,
1
2
3
4
5
6
7
8
9
char data[] = "v=35,rpm=80,temp=25,dist=31051999,vbat=33";
char *cmds[10];  // pick a sufficiently large array size
char *cmd;
char *next = data;
int nCmd = 0;
while ( nCmd < 10 && (cmd=strtok(next, ",")) != NULL ) {
    cmds[nCmd++] = cmd;
    next = NULL;
}

then tokenise each cmds[i] with strtok(p,"=")
Last edited on
Thanks for help and knowledge about strtok!
I did in a way I did know(before your post).
It's like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
	char data[] = "0|35|1|80|2|25|3|31051999|4|33";
	char *dataValue;
	long cmd;
	bool part = false; // FALSE = TAKES COMMAND ; TRUE = TAKES VALUE;

	dataValue = strtok(data, "|");

	while (dataValue != NULL)
	{
		if (part) cmdproc(cmd, atoi(dataValue)); // PROCESSES CMD WITH VALUE
			else cmd = atoi(dataValue);

		part = !part;
		dataValue = strtok(NULL, "|");
	}


Data doesn't look nice but it works :D

Thanks for your time.
Now I want to split all commands so I get from this

"v=10,rpm=50"

to this

"v=10"

that is what I gave you, and its very lightweight/fast.
however changing the input char data is an excellent way to reduce the effort. That was a good idea. That makes mine obsolete, but if you find strtok to be slow, or have any more performance concerns, we can look again.
Last edited on
Code will process around 10 commands per minute. It can do 100 commands for 1/2-1ms.
Topic archived. No new replies allowed.