Go Down

Topic: serial variable (Read 1 time) previous topic - next topic

So i'm still messing with the code and getting stuff figured out. What i am doing is making my arduino micro into a command interpreter, so that by using a serial monitor i can send a message and have the board do something in real time. like i could tell it to make a certain pin high or low or perhaps run short pieces of pre-made code. let's say i wanted to say hello to the arduino and have it say hello back on an lcd display.

// i send hello over serial
// arduino checks if my command equals hello
// arduino says "Hi, how are you?"

so how do i compare a serial input to an existing word like hello
i have this but it doesn't work

Code: [Select]


#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

int prompt = 0;

void setup(){

  lcd.begin(16, 2);

  Serial.begin(9600);
}

void loop()
{
                                       // when characters arrive over the serial port...
  if (Serial.available()) {
                                       // wait a bit for the entire message to arrive
    delay(100);
                                       // clear the screen
    lcd.clear();
                                       // read all the available characters
    while (Serial.available() > 0) {
                                       // ?? set the variable "prompt" to what is coming in over serial
prompt = (Serial.read());
                                       // ?? does prompt equal hello
     if (prompt == "hello") {
                                       // since it does, print "hi how are you" to lcd
        lcd.print("Hi, how are you?");
       
      }
    }
  }
}


now i know there is a proper and/or shorter way of doing this, how would I?

Melbfella

Hi,

I've needed to do something similar myself recently.

I didn't want to rely on a new line to delimit the lines, so I used a tilde (~) instead.  As each char is read (Serial.read() only reads one char at a time), I build up a string until the delimiter is reached, then work out what do do based on the word/phrase received.

All you need to do is append a tilde to the end of the phrase ie hello~

Here's the code - I hope it helps.

Code: [Select]

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

static String command;

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  checkSerialPort();
  delay(500);
 
}

void checkSerialPort()
{
  int incomingData;

  while (Serial.available() > 0)
  {
    //get the sent data
    incomingData = Serial.read();
    if((String) char(incomingData)=="~")
    {
      //It's the end of a command. go ahead and execute
      Serial.print("We received : ");
      Serial.println((String) command);
      if(command=="hello")
      {
        setLCD("hi how are you");     
      }
      command="";
    }else{
      command+=(String) char(incomingData);       
    }
  }


void setLCD(String chars)
{
  lcd.setCursor(0,0);
  lcd.print(chars); 
}

thank you, this is exactly what i was looking for. I have a couple other questions, where would i add a second command (like i say bye and it says goodbye back) so that it always loops back to the beginning, upon completion of a command. Also would it be possible to split a command into certain parts, like "digitalhigh/5~" it could use the slash to stop one string and start another, so within one command i could specify that i want to turn digital pin 5 high. then I suppose I could just make digitalhigh/1 through digitalhigh/13 as separate commands, anyway. but creating two or three strings from one command might be useful for other things, like programming something where the interpreter knows part of the command but not another. like you could say "name: chris~" and it knows the name command, but has no idea what your name might actually be. in this instance the colon would be the string seperator. Thanks again for that code and thanks in advance for any help you could give me on this.

Arrch

#3
Jun 22, 2013, 06:18 am Last Edit: Jun 22, 2013, 06:20 am by Arrch Reason: 1
http://gammon.com.au/serial

Using this tecnique, you can use a series of if/elseif and strcmp() to process commands

Nick Gammon

Code: [Select]

    if((String) char(incomingData)=="~")


It is completely unnecessary to use String here.

Please note that in versions of the IDE up to and including 1.0.3, the String library has bugs as discussed here and here.

In particular, the dynamic memory allocation used by the String class may fail and cause random crashes.

I recommend reworking your code to manage without String. Use C-style strings instead (strcpy, strcat, strcmp, etc.), as described here for example.

Alternatively, install the fix described here:  Fixing String Crashes

Preferably upgrade your IDE to version 1.0.4 or above at: http://arduino.cc/en/Main/Software




Change it to:

Code: [Select]

    if (incomingData == '~')


That is much simpler and avoids memory fragmentation, in addition to the bugs mentioned above.
Please post technical questions on the forum, not by personal message. Thanks!

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

Melbfella

#5
Jun 22, 2013, 12:55 pm Last Edit: Jun 23, 2013, 02:42 am by Melbfella Reason: 1

thank you, this is exactly what i was looking for. I have a couple other questions, where would i add a second command (like i say bye and it says goodbye back) so that it always loops back to the beginning, upon completion of a command. Also would it be possible to split a command into certain parts, like "digitalhigh/5~" it could use the slash to stop one string and start another, so within one command i could specify that i want to turn digital pin 5 high. then I suppose I could just make digitalhigh/1 through digitalhigh/13 as separate commands, anyway. but creating two or three strings from one command might be useful for other things, like programming something where the interpreter knows part of the command but not another. like you could say "name: chris~" and it knows the name command, but has no idea what your name might actually be. in this instance the colon would be the string seperator. Thanks again for that code and thanks in advance for any help you could give me on this.


No problems, pleased I could help.

As Nick points out, there are issues with the String library in IDEs lower than 1.0.4, so please do go ahead and upgrade to 1.0.4 or 1.0.5 if you're not there already.  I'm using 1.0.5 and it seems to work fine.

In answer your other questions, perhaps this will help :

I use a 3 character 'command' to work out what it is I want to do, then associate a value to it, delimited by a colon.
The four examples I've included in the code would be accessed with strings constructed like this :
hel:0~                            //Just blindly calls a function (0 won't be used).
lcd:hi how are you~          //Writes the string 'hi how are you' to the LCD  
dhi:5~                           //Sets digital pin 5 high
dlo:5~                           //Sets digital pin 5 low

The string is parsed out and a series of if/else if statements decide what it needs to do.

The full version is below - hopefully it does what you need.

Cheers,

Doug.



Code: [Select]


#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

static String command;

void setup()
{
   pinMode(5,OUTPUT);
}

void loop()
{
  checkSerialPort();
  delay(500);
}



void checkSerialPort()
{
   /*
   **Command format :
   <command>:<commandValue>~
   ie
   ABC:123~
   
   */
 String commandChars;
 String commandValue;
 int incomingData;
 
 
 while (Serial.available() > 0)
 {
   //get the sent data
   incomingData = Serial.read();
    if(incomingData=='~')
   {
     //It's the end of a command. go ahead and execute
     Serial.print("We received : ");
     Serial.println(command);
     commandChars = command.substring(0,3);
     commandValue = command.substring(4,command.length());

     Serial.print("commandChars : ");
     Serial.println(commandChars);  
     Serial.print("commandValue : ");
     Serial.println(commandValue);
     command="";
   }else{
     command+=(String) char(incomingData);        
   }
   
   if(commandChars=="hel")
   {
     setLCD("hi how are you");
   }
   else if(commandChars=="lcd")
   {
     setLCD(commandValue);
   }
   else if(commandChars="dhi")
   {
     writeHiLo(stringToNumber(commandValue),HIGH);
   }
   else if(commandChars="dlo")
   {
    writeHiLo(stringToNumber(commandValue),LOW);
   }
 }
}

void writeHiLo(int pin,int value)
{
 digitalWrite(pin,value);
}


void setLCD(String chars)
{
 lcd.setCursor(0,0);
 lcd.print(chars);  
}

int stringToNumber(String thisString)
{
int i;
int value;
int length;
 
 length = thisString.length();
 char blah[(length+1)];
 for(i=0; i<length; i++) {
   blah[i] = thisString.charAt(i);
 }
 blah[i]=0;
 value = atoi(blah);
 return value;
}


Melbfella


Code: [Select]

    if((String) char(incomingData)=="~")


It is completely unnecessary to use String here.

Please note that in versions of the IDE up to and including 1.0.3, the String library has bugs as discussed here and here.

In particular, the dynamic memory allocation used by the String class may fail and cause random crashes.

I recommend reworking your code to manage without String. Use C-style strings instead (strcpy, strcat, strcmp, etc.), as described here for example.

Alternatively, install the fix described here:  Fixing String Crashes

Preferably upgrade your IDE to version 1.0.4 or above at: http://arduino.cc/en/Main/Software




Change it to:

Code: [Select]

    if (incomingData == '~')


That is much simpler and avoids memory fragmentation, in addition to the bugs mentioned above.


Hi Nick,

I did try without the cast when I first wrote this function, but it produces this error :

'ISO C++ forbids comparison between pointer and integer'

My past is in the C#/Visual Basic world, so pointers are a bit of a black art to me - is there something else I need to do?

Cheers,

Doug.

wildbill

I suspect it's a quote/double quote issue. You likely did this:
Code: [Select]
     if(incomingData=="~")

Whereas Nick suggested:
Code: [Select]
     if(incomingData=='~')

Melbfella

#8
Jun 22, 2013, 01:27 pm Last Edit: Jun 22, 2013, 01:40 pm by Melbfella Reason: 1

I suspect it's a quote/double quote issue. You likely did this:
Code: [Select]
    if(incomingData=="~")

Whereas Nick suggested:
Code: [Select]
    if(incomingData=='~')


Well spotted wildbill - thanks for that :)

It does indeed work with single quotes.

The code example provided above has been modified to include this change.

Cheers,

Doug.

thank you all for the help- i've got lots of useful code to play with now.

when compiling your three letter code, i get the following terminal error:

core.a(main.cpp.o): In function `main':
C:\Program Files (x86)\Arduino\Arduino ERW 1.0.3\hardware\arduino\cores\arduino/main.cpp:11: undefined reference to `setup'
C:\Program Files (x86)\Arduino\Arduino ERW 1.0.3\hardware\arduino\cores\arduino/main.cpp:14: undefined reference to `loop'

i'll update to 1.0.4 or 1.0.5 now and see if it fixes things.

AWOL

Quote
when compiling your three letter code, i get the following terminal error

Do you mean the code in reply #5?
It's incomplete, it doesn't have a setup or loop, you'll have to provide your own.
"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.

I figured that, after the 1.0.5 install i'll add the setup and loop.

Melbfella


thank you all for the help- i've got lots of useful code to play with now.

when compiling your three letter code, i get the following terminal error:

core.a(main.cpp.o): In function `main':
C:\Program Files (x86)\Arduino\Arduino ERW 1.0.3\hardware\arduino\cores\arduino/main.cpp:11: undefined reference to `setup'
C:\Program Files (x86)\Arduino\Arduino ERW 1.0.3\hardware\arduino\cores\arduino/main.cpp:14: undefined reference to `loop'

i'll update to 1.0.4 or 1.0.5 now and see if it fixes things.


I've added setup() and loop() to the code example.

zoomkat

Quote
So i'm still messing with the code and getting stuff figured out. What i am doing is making my arduino micro into a command interpreter, so that by using a serial monitor i can send a message and have the board do something in real time. like i could tell it to make a certain pin high or low or perhaps run short pieces of pre-made code. let's say i wanted to say hello to the arduino and have it say hello back on an lcd display.


Simple serial test code to have the board do something.

Code: [Select]

//zoomkat 3-5-12 simple delimited ',' string parse
//from serial port input (via serial monitor)
//and print result out serial port
// CR/LF could also be a delimiter
//send on, or off, from the serial monitor

int ledPin = 13;
String readString;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  Serial.println("serial LED on/off test with , delimiter"); // so I can keep track
}

void loop() {

  if (Serial.available())  {
    char c = Serial.read();  //gets one byte from serial buffer
    if (c == ',') {
      //if (readString.length() >0) {
        Serial.println(readString); //prints string to serial port out
        //do stuff with the captured readString
        if(readString.indexOf("on") >=0)
        {
          digitalWrite(ledPin, HIGH);
          Serial.println("LED ON");
        }
        if(readString.indexOf("off") >=0)
        {
          digitalWrite(ledPin, LOW);
          Serial.println("LED OFF");
        }       
        readString=""; //clears variable for new input
      //}
    } 
    else {     
      readString += c; //makes the string readString
    }
  }
}

Google forum search: Use Google Advanced Search and use Http://forum.arduino.cc/index in the "site or domain:" box.

Go Up