Serial Confusion

New to Arduino and new to serial communications. I am using serial monitor or even hyperterminal and I have run into something I don't know how to do.

How do I accept multiple characters and require the user to press enter before executing?

Thanks! Frank

Here's a little function I use:

int read_int()
{
  static byte c;
  static int i;

  i = 0;
  while (1)
  {
    while (!Serial.available())
    {;}

    c = Serial.read();
    Serial.print(c, BYTE);
  
    if (c == '\r')
    {
      Serial.print("\r\n");
      return i;
    }
    if (isdigit(c))
    {
      i = i * 10 + c - '0';
    }
    else
    {
      Serial.print("\r\nERROR: \"");
      Serial.print(c, BYTE);
      Serial.print("\" is not a digit\r\n");
      return -1;
    }
  }
}

Note the if (c == '\r') test - that's checking for the carriage return (the character typically transmitted when you hit the enter key), and uses that condition to terminate the input and return from the function.

-j

thanks for the help. that seemed to do the trick, however I still can’t seem to get something right…

here is my code…

// ALLOWS USE OF TERMINAL WINDOW, OR ETC, TO PROGRAM THE REQUIRED PARAMETERS INTO THE EEPROM.  ACTIVATED WHEN THE USER SENDS THE PROPER CHARACTER
void menu()
{
  // VARIABLE DECLERATIONS
  boolean  showMenu = true;
  boolean  menuReturn = false;
  boolean  showPrompt = true;

  // ACKNOWLEDGE THAT THE PROGRAM MODE WAS ACCEPTED... ELSEWHERE
  Serial_Display.println("Program Halted");    

  do
    {
      int i = 0;
      char incomingChars[10];
      char phraseStr[10];

      if (showMenu)
        {
          // WHEN THE USER FIRST CALLS THE MENU ROUTINE, THIS SECTION WILL BE DISPLAYED.
          // IF THE USER CALLS FOR THE MENU TO BE DISPLAYED AGAIN, THIS WILL BE DISPLAYED
          Serial.println("");
          Serial.println("/--------------------------------------------------|");
          Serial.println("| KNOCK BOARD V1.0                                 |");
          Serial.println("| Squirrel Performance - http://www.squirrelpf.com |");
          Serial.println("|--------------------------------------------------/\n");
          Serial.println("MENU: (press key to select function)");
          Serial.println("\t (g) \t Get all parameters from memory");
          Serial.println("\t (e) \t Program engine constants");
          Serial.println("\t (r) \t Program RPM table for knock lookup");
          Serial.println("\t (v) \t Program max voltage table for knock lookup");
          Serial.println("\t (c) \t Program misc constants");
          Serial.println("\t (m) \t Display this menu");
          Serial.println("\t (q) \t Quit and return to normal operations.");
          showMenu = false;
        }

      if (showPrompt)
        {
          Serial.print("\n-> ");                              // command prompt
          showPrompt = false;
        }
        
      do
        {
          while (!Serial.available())
            {
              ;
            }
          incomingChars[i] = Serial.read();
          if (incomingChars[i] == '\r')
            {
              Serial.println();
              break;
            }
          Serial.print(incomingChars[i]);
          i++;
        }
      while (1);

      if (incomingChars == "g")
        {
         Serial.println("\tNo.\tRPM\tKnockVolts");
         for (int l = 0; l < 8; l++)
           {
             Serial.print("\t");
             Serial.print(l);
             Serial.print("\t");
             Serial.print(rpmLookup[l]);
             Serial.print("\t");
             printDouble(knockLookup[l],3);
             Serial.println("");
           }
           showPrompt = true;
        }      
      else if (incomingChars == "e")
        {
          // insert code here for this action
        }
      else if (incomingChars == "r")
        {
          // insert code here for this action
        }
      else if (incomingChars == "v")
        {
          // insert code here for this action
        }
      else if (incomingChars == "c")
        {
          // insert code here for this action
        }
      else if (incomingChars == "m")
        {
          showMenu = true;
          showPrompt = true;
        }
      else if (incomingChars == "q")
        {
          menuReturn = true;
          Serial.println ("\nReturning to normal operations...\n");
        }
      else
        {
          Serial.print(incomingChars);
          Serial.println(" is an invalid command");
          showPrompt = true;
        }
      
      // add code here to program new data into the EEPROM from serial transmissions
      // remember to reinitialize the EEPROM variables into the lookup variables for immediate use.  call setup???

    }
  while (menuReturn == false);
}

it gives me this in the console. as you can can see, it gives me invalid commands for everything though I have the appropriate if statements. am i not handling variable declerations correctly?

/--------------------------------------------------|
| KNOCK BOARD V1.0                                 |
| Squirrel Performance - http://www.squirrelpf.com |
|--------------------------------------------------/

MENU: (press key to select function)
         (g)     Get all parameters from memory
         (e)     Program engine constants
         (r)     Program RPM table for knock lookup
         (v)     Program max voltage table for knock lookup
         (c)     Program misc constants
         (m)     Display this menu
         (q)     Quit and return to normal operations.

-> m
 is an invalid command

-> mm
 is an invalid command

-> m
 is an invalid command

->

Thanks!

Frank, your difficulty stems from the expressions of the form:

if (incomingChars == "g")

Contrary to what you might think, this does not say "if the string in incomingChars is equal to the string "g". It says "if the address of the incomingChars array is equal to the address of the statically stored string "g"" -- and it never is.

The easiest solution to your problem is to change those expressions to something like

if (incomingChars[0] == 'g')

Regards, and good luck!

Mikal

I had given that a try on a whim, but it gave me this when attempting to compile....

 In function 'void menu()':
error: ISO C++ forbids comparison between pointer and integer

did you do this: if (incomingChars == 'g') instead of this: if (incomingChars[0] == 'g')

incomingChars is a pointer to an array of characters incomingChars[0] is the character value of the first element the array

I will give that a try. It was something I was trying to avoid because if the user types mm, that should not be a valid command.

Well I gave all of that a try and it worked. I guess I will just need to put more hooks in to look to ensure that is the only character when i need it to be the only character....

this is the new output....

=~=~=~=~=~=~=~=~=~=~=~= PuTTY log 2008.10.02 07:01:22 =~=~=~=~=~=~=~=~=~=~=~=
0.100      1.81      0.058      -1.152      -3.000      59.0      Yes      0.000
0.215      2.42      0.175      -1.171      -9.000      60.0      Yes      0.000
0.323      1.21      0.058      -1.191      -3.000      61.0      Yes      0.000
0.430      1.81      0.195      -1.171      -10.000      60.0      Yes      0.000
0.538      1.21      0.058      -1.191      -3.000      61.0      Yes      0.000
0.645      3.63      0.175      -1.171      -9.000      60.0      Yes      0.000
0.752      0.60      0.039      -1.191      -2.000      61.0      Yes      0.000
0.860      3.63      0.175      -1.171      -9.000      60.0      Yes      0.000
0.967      0.60      0.019      -1.191      -1.000      61.0      Yes      0.000
1.074      2.42      0.175      -1.191      -9.000      61.0      Yes      0.000
1.182      0.60      0.039      -1.171      -2.000      60.0      Yes      0.000
1.289      1.81      0.156      -1.210      -8.000      62.0      Yes      0.000
1.396      1.21      0.039      -1.191      -2.000      61.0      Yes      0.000
1.505      3.03      0.156      -1.191      -8.000      61.0      Yes      0.000
1.612      1.81      0.058      -1.191      -3.000      61.0      Yes      0.000
1.719      1.21      0.136      -1.191      -7.000      61.0      Yes      0.000
1.827      1.81      0.058      -1.191      -3.000      61.0      Yes      0.000
1.934      3.63      0.136      -1.191      -7.000      61.0      Yes      0.000
2.042      1.21      0.058      -1.210      -3.000      62.0      Yes      0.000
2.151      3.03      0.097      -1.171      -5.000      60.0      Yes      0.000
2.259      1.21      0.039      -1.210      -2.000      62.0      Yes      0.000
2.367      3.03      0.097      -1.132      -5.000      58.0      Yes      0.000
2.474      1.21      0.058      -1.210      -3.000      62.0      Yes      0.000
2.582      3.03      0.117      -1.113      -6.000      57.0      Yes      0.000
2.690      0.00      0.019      -1.230      -1.000      63.0      Yes      0.000
2.796      1.81      0.058      -1.171      -3.000      60.0      Yes      0.000
2.905      0.60      0.000      -1.210      0.000      62.0      Yes      0.000
3.013      3.03      0.117      -1.113      -6.000      57.0      Yes      0.000
3.121      0.60      0.019      -1.191      -1.000      61.0      Yes      0.000
3.229      3.03      0.097      -1.113      -5.000      57.0      Yes      0.000
3.336      1.21      0.039      -1.191      -2.000      61.0      Yes      0.000
3.444      3.03      0.097      -1.132      -5.000      58.0      Yes      0.000
3.552      0.60      0.019      -1.191      -1.000      61.0      Yes      0.000

/--------------------------------------------------|
| KNOCK BOARD V1.0                                 |
| Squirrel Performance - http://www.squirrelpf.com |
|--------------------------------------------------/

MENU: (press key to select function)
       (g)        Get all parameters from memory
       (e)        Program engine constants
       (r)        Program RPM table for knock lookup
       (v)        Program max voltage table for knock lookup
       (c)        Program misc constants
       (m)        Display this menu
       (q)        Quit and return to normal operations.

-> m

/--------------------------------------------------|
| KNOCK BOARD V1.0                                 |
| Squirrel Performance - http://www.squirrelpf.com |
|--------------------------------------------------/

MENU: (press key to select function)
       (g)        Get all parameters from memory
       (e)        Program engine constants
       (r)        Program RPM table for knock lookup
       (v)        Program max voltage table for knock lookup
       (c)        Program misc constants
       (m)        Display this menu
       (q)        Quit and return to normal operations.

-> q

Returning to normal operations...

8.189      13.34      1.269      -1.132      -65.000      58.0      Yes      0.000
8.298      2.42      0.078      -1.152      -4.000      59.0      Yes      0.000
8.406      0.60      0.039      -1.191      -2.000      61.0      Yes      0.000
8.513      1.81      0.058      -1.191      -3.000      61.0      Yes      0.000
8.621      0.60      0.039      -1.191      -2.000      61.0      Yes      0.000
8.729      1.81      0.078      -1.191      -4.000      61.0      Yes      0.000
8.835      1.21      0.039      -1.171      -2.000      60.0      Yes      0.000
8.942      1.21      0.058      -1.191      -3.000      61.0      Yes      0.000
9.050      1.21      0.058      -1.171      -3.000      60.0      Yes      0.000
9.156      0.60      0.039      -1.191      -2.000      61.0      Yes      0.000
9.265      1.21      0.058      -1.191      -3.000      61.0      Yes      0.000
9.371      1.21      0.039      -1.191      -2.000      61.0      Yes      0.000
9.479      1.81      0.058      -1.191      -3.000      61.0      Yes      0.000
9.586      0.60      0.019      -1.191      -1.000      61.0      Yes      0.000
9.694      1.21      0.039      -1.210      -2.000      62.0      Yes      0.000
9.800      1.21      0.039      -1.191      -2.000      61.0      Yes      0.000
9.909      1.21      0.039      -1.210      -2.000      62.0      Yes      0.000
10.017      1.21      0.039      -1.191      -2.000      61.0      Yes      0.000
10.123      0.60      0.019      -1.210      -1.000      62.0      Yes      0.000
10.232      1.81      0.058      -1.191      -3.000      61.0      Yes      0.000
10.340      0.60      0.039      -1.191      -2.000      61.0      Yes      0.000

/--------------------------------------------------|
| KNOCK BOARD V1.0                                 |
| Squirrel Performance - http://www.squirrelpf.com |
|--------------------------------------------------/

MENU: (press key to select function)
       (g)        Get all parameters from memory
       (e)        Program engine constants
       (r)        Program RPM table for knock lookup
       (v)        Program max voltage table for knock lookup
       (c)        Program misc constants
       (m)        Display this menu
       (q)        Quit and return to normal operations.

-> g
      No.      RPM      KnockVolts
      0      255      0.000
      1      255      0.000
      2      255      0.000
      3      255      0.000
      4      255      0.000
      5      255      0.000
      6      255      0.000
      7      255      0.000

-> 
 is an invalid command

-> 
 is an invalid command

-> mm

/--------------------------------------------------|
| KNOCK BOARD V1.0                                 |
| Squirrel Performance - http://www.squirrelpf.com |
|--------------------------------------------------/

MENU: (press key to select function)
       (g)        Get all parameters from memory
       (e)        Program engine constants
       (r)        Program RPM table for knock lookup
       (v)        Program max voltage table for knock lookup
       (c)        Program misc constants
       (m)        Display this menu
       (q)        Quit and return to normal operations.

-> mg

/--------------------------------------------------|
| KNOCK BOARD V1.0                                 |
| Squirrel Performance - http://www.squirrelpf.com |
|--------------------------------------------------/

MENU: (press key to select function)
       (g)        Get all parameters from memory
       (e)        Program engine constants
       (r)        Program RPM table for knock lookup
       (v)        Program max voltage table for knock lookup
       (c)        Program misc constants
       (m)        Display this menu
       (q)        Quit and return to normal operations.

-> q

Returning to normal operations...

54.882      13.95      1.347      -1.132      -69.000      58.0      Yes      0.000
54.991      2.42      0.078      -1.132      -4.000      58.0      Yes      0.000
55.099      1.81      0.058      -1.191      -3.000      61.0      Yes      0.000
55.207      1.81      0.058      -1.171      -3.000      60.0      Yes      0.000
55.316      1.21      0.039      -1.210      -2.000      62.0      Yes      0.000
55.424      0.00      0.019      -1.210      -1.000      62.0      Yes      0.000

thanks for the help btw.

was trying to avoid because if the user types mm, that should not be a valid command

Not sure what you mean, if you want to compare more than the first character you can use the strncmp function.

if (strncmp(incomingChars,"mm",2) == 0) // do something if the first two chars in the incomingChars array are "mm"

A google search will turn up links to its usage. Its not a very intuitive function but may be what you need

mem has your solution.

If your commands are all (and always will be) single characters, just skip the whole string+enter routine and use a single character/single keystroke. Your code will be shorter and your program will probably feel faster, since it takes fewer keystrokes to make it react.

-j

Frank, my guess is that your ‘comparison between integer and pointer’ error resulted from forgetting to change the double quotes to single quotes":

if (incomingChars[0] == "q") // this is an error: use SINGLE quotes

If I were implementing this menu, I would adopt kg4’s strategy and just inspect the characters singly thus:

char c = Serial.read();
switch(c)
{
  case 'g': // do something for 'g'
  ...

This way you can discard that arbitrarily sized “incomingChars” buffer.

However, many people like the feel and control you get with the “enter key” paradigm. As mem suggested, you can use a variant of strcmp/strncmp to compare the buffered string with those being searched for.

if (!strcmp(incomingChars, "m"))
{
  // do the 'm' thing
}
else if (!strcmp(incomingChars, "g"))
{
  // do the 'g' thing
}

Watch out for a couple of things, though:

  1. Your current code could potentially crash if a user just types “asdfadsfasdf” or something. This overflows the incomingChars buffer. If would be better to abort the do/while loop if “i” gets too big.
  2. Your current code stores the ‘\r’ in the buffer along with the entered text, so any comparison between incomingChars and, say, “m” will always fail. (The buffer contains “m\r”.)
  3. The string is never terminated. Adding the following line should resolve this and #2"
if (incomingChars[i] == '\r')
{
  Serial.println();
  incomingChars[i] = '\0'; // <-- add this line to terminate the string
  break;
}

Regards,

Mikal

Wow you guys rock.

Thanks for the suggestion on the string overflow. I know I had crashed it and I didn’t know why. I will do a break if I gets to big.

I am going to turn the reading of the string into a function to allow for use when i need to lots of other inputs.

the reason for the comment about mm, is that mm is invalid. only m is. so i need to just put a hook saying that if incomingStr[0] = ‘m’ and incomingStr[1] = ‘\r’, then do that code.

thanks!

so i need to just put a hook saying that if incomingStr[0] = ‘m’ and incomingStr[1] = ‘\r’, then do that code.

That strategy will work fine, but don’t forget that DOUBLE equal:

if (incomingChars[0] == 'm' && incomingChars[1] == '\r')

or, for safety, as others (mem) have pointed out, put the constant on the left side of the expression:

if ('m' == incomingChars[0] && '\r' == incomingChars[1])

Mikal

Thanks!

Just a quick question not completely related to this... can void setup() be called later in the program? Right now my setup() pulls values from the EEPROM at start up. I figured to reduce code space, that if I change the EEPROM from the menu, I could then just recall setup() to refresh those values into the normal operating code.

You can but its not advisable. The Arduino convention is that setup is called once only at startup. If you want your sketches to be easily understood by other arduino users you should respect that convention and create a new function to handle the initialization that you may want to do again while the sketch is running. here is an example:

void init(){ // initializes stuff, call this as often as you need }

void setup(){ Serial.begin(9600); // this is only called once! init(); // call your function to initialize your stuff }

void loop(){ if( something) init(); // init is called again if needed }

Just break your EEPROM initialization code out into a separate function. Call it in setup() and again whenever you need to.

-j

Just break your EEPROM initialization code out into a separate function. Call it in setup() and again whenever you need to.

is my previous post not visible ? ;)

is my previous post not visible ?

huh, not sure how I missed that. maybe another one of those times I got interrupted in the middle of a reply....

-j