Go Down

Topic: Strange behavior in char based switch/case (Read 1 time) previous topic - next topic

before doing File I/O, i decided to see about sending text commands to the Uno.  The code is supposed to start counting at 'start', stop counting at 'stop', and reset the counter at 'zero'.  If already stopped, and you tell it to stop, it will send you a message saying "already stopped".  However, if i type in "st", it will start counting.  If i type in stop, hit will say "starting...", reset, then stop.  It never sends "Already stopped".  I then changed 'start' to 'begin'.  It now does "already stopped" and no longer displays "starting...", but still resets when stop is typed.

What am i doing wrong to make it behave this way?  Thanks in advance...

Code: [Select]
char inst;
int stat = 0;
int counter = 0;

void setup()
{
  Serial.begin(9600);
  Serial.println("Initializing");
}

void loop()
{
  if (stat == 1)
  {
    Serial.print(counter);
    Serial.println("...");
    counter++;
    delay(100);
  }
  if (Serial.available() )
  {
    inst = Serial.read();
  }
  switch (inst) {
    case 'begin':
      Serial.println("starting..");
      stat = 1;
      inst = ' ';
      break;
    case 'stop':
      if (stat == 0)
      {
        Serial.println("Already stopped");
        inst = ' ';
      }
      else
      {
        Serial.println("stopping...");
        stat = 0;
        inst = ' ';
      }
      break;
    case 'zero':
      Serial.println("resetting...");
      counter = 0;
      inst = ' ';
      break;
    default:
      stat = stat;
  }
}

AWOL

A single char is never going to have the value 'begin'(or start or stop or zero), which is five chars.
What happens if there isn't a character available?
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

so should i do string? I found it interesting that it wouldn't start on "s", but would start on "st".

While nothing is there, it either keeps counting or stops counting depending on the value of stat (see first if statement).  each case resets inst.  If i didn't reset inst, it would keep the last value and run that condition.

AWOL

Unless you're going to expand the command set hugely, I'd stick to single letters.
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

liudr


Unless you're going to expand the command set hugely, I'd stick to single letters.


I second that. I've seen quite a few posts with OP trying to receive strings as commands where a single character is sufficient. Strings are for more complicated situations. Should it arise, you can come ask again. This situation could arise when you want to accomplish something like "start counting but not from zero and from 65535 and count down instead of count up". I'd be happy to share some of my code for such situations. For now, let's stick with single character commands and switch-case.

Ok, i'll stick to single letter commands.  I was probably going to have about 10-20 commands, but i suppose i can map them.  I just wanted to try a little "big boy programming" is all :).  I am interested in the code :).  Thanks again

kf2qd

You have 26 letters and 10 numbers to star with(yhat's more than I have fingers and toes...),so that should last a little while.

WizenedEE

For a second character, you could nest the switch statements:
Code: [Select]

byte val1 = Serial.read();
switch(val1) {
case s:
  byte val2 = Serial.read();
  switch(val2) {
    case r:
    break;
  }
  break;
}

Nick Gammon

You could buffer your input:

http://www.gammon.com.au/serial

Then when you get a newline you process the whole thing. But you can't use switch for strings. You could strcmp them. Or have a lookup table.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

robtillaart

Quote
You have 26 letters and 10 numbers to star with

26 char in lower and uppercase make 52
10 digits
32 other signs like `~!@#$%^&*()_+-={}|[]\:";'<>?,./
---
total of 94...

If you really want to use strings there are a few ways: (be sure to first build up the string!)

1) if then else ladder => simple and robust, slow, most used commands should be at top of the ladder of course
Code: [Select]
if (strcmp(s, "begin")==0) 
{
}
else if (strcmp(s, "next command")==0)
{
}
else if ....


2) perfect hash function followed by a switch => fast, but not trivial to find.
Code: [Select]
int cmd= phash(s);

switch(cmd)
{
  case 1: ..
  case 2: ..
  etc
}


A perfect hash is a hash function that (in this case) for every expected string of a set of commands generates an unique number by manipulating the string. It COULD look like the following:

Code: [Select]
int phash(char *s)
{
  return (s[0]-'a')*26 + (s[1]-'a');
}

This hash function assumes that the first two chars of a command string together form an unique number [and lowercase]. It will generate a number between 0 and 625.

A perfect hash function will generate a consecutive(?) number sequence { 1,2,3,4,5,...n } for a set of commands. This makes a perfect hash quite difficult to find. One advantage of a perfect hash is that the number can be used as the index for an array of functions. ==> func[phash(commandstring)](arguments_string);

Instead of the first 2 chars one can also take the first and the last, or 3,4,5 chars or the string length, the number of vowels etc.

Drawback of hash functions is that if you add a new command your hashfunction might need a redesign, so it is often good to elaborate the completeness of the commandset first.

Hope this helps,
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

mromani

Haven't used it (yet), but seems interesting:

http://arduino.cc/playground/Code/CmdMessenger

This library should ease the implementation of a string-based command set.

liudr

My multi-character command code looks like Nick's code. There has to be a special character to indicate the end of the command, often end of line or something you don't use inside your command, such as a back slash or '#' etc. I chose mine to be '~' since it is the firmware for serial LCDs. The '~' is the last character on the ASCII table that can be typed from a keyboard and is not used in messages. You create a buffer (char array) to store all characters until you get to the end of command character, then interpret the command with a set of syntax. Say your command always comes with

command_identifer [parameter1],[parameter2] end_of_command

Then you can do strcmp on command_identifier, and you can use sscanf to extract parameters (that is, in form of numbers).

I'll have to pretty up my code a bit before I can show it publicly.  :D

robtillaart

Quote
You have 26 letters and 10 numbers to star with

26 char in lower and uppercase make 52
10 digits
32 other signs like `~!@#$%^&*()_+-={}|[]\:";'<>?,./
---
total of 94...

If you really want to use strings there are a few ways: (be sure to first build up the string!)

1) if then else ladder => simple and robust, slow, most used commands should be at top of the ladder of course
Code: [Select]
if (strcmp(s, "begin")==0) 
{
}
else if (strcmp(s, "next command")==0)
{
}
else if ....


2) perfect hash function followed by a switch => fast, but not trivial to find.
Code: [Select]
int cmd= phash(s);

switch(cmd)
{
  case 1: ..
  case 2: ..
  etc
}


A perfect hash is a hash function that (in this case) for every expected string of a set of commands generates an unique number by manipulating the string. It COULD look like the following:

Code: [Select]
int phash(char *s)
{
  return (s[0]-'a')*26 + (s[1]-'a');
}

This hash function assumes that the first two chars of a command string together form an unique number [and lowercase]. It will generate a number between 0 and 625.

A perfect hash function will generate a consecutive(?) number sequence { 1,2,3,4,5,...n } for a set of commands. This makes a perfect hash quite difficult to find. One advantage of a perfect hash is that the number can be used as the index for an array of functions. ==> func[phash(commandstring)](arguments_string);

Instead of the first 2 chars one can also take the first and the last, or 3,4,5 chars or the string length, the number of vowels etc.

Drawback of hash functions is that if you add a new command your hashfunction might need a redesign, so it is often good to elaborate the completeness of the commandset first.

Hope this helps,
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Morris Dovey

From the C Language Standard, ISO/IEC 9899:1999(E) 6.8.4.2:

1) The controlling expression of a switch statement shall have integer type.

3) The expression of each case label shall be an integer constant expression and no two of the case constant expressions in the same switch statement shall have the same value after conversion.
There's always a better way!

Go Up