LED state toggle on serial port command

Hi,

I have written a piece of code for arduino. I am giving command on serial port for LED at pin 13 to toggle. I need two type of swtching, one is continuous and other is once for each time command is sent. The code is attached below:

char command;
String string;
#define led 13


void setup()
{
pinMode(led, OUTPUT);
Serial.begin(9600);
}

void loop()
{
  while(Serial.available() > 0)
  {
    command = ((byte)Serial.read());
    string += command;
    delay(3);
  }

  if(string == "CONT")
  {
          digitalWrite(led, HIGH);
          delay(500);
          digitalWrite(led, LOW);
          delay(500);
  }
  if(string == "SWITCH")
  {
          int state= digitalRead(led);
          digitalWrite(led, !state);
          delay(2000);
                             
  }
  

}

For string =" CONT", it works fine. But for string = "SWITCH", the LED does not change its state. I tried different logic but failed.

Need help.

Thanks.

I think it just works fine. Only don't try to send "SWITCH" as second command :wink: What will string be after you send "CONT" and after that "SWITCH" ?

Some tips:

Watch indentation!

Don't just make every variable global. Command has no right to be global.

Why delay() the serial read??????

Why cast Serial.read() to a byte???

Give variable proper names. So not led, but ledPin etc.

Don't use #define for pins, use const byte.

Don't use String. Just use strings. See serial input basics
https://forum.arduino.cc/index.php?topic=396450.0

Don't use delay(), switch to millis(). See Blink without delay.

Hmm.
It means I have to clear the serial buffer everytime before sending next command. Am I getting it right?

I have implemented changes as per your suggestions. I think they are fine now.

String string;
const byte ledPin=13;


void setup()
{
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}

void loop()
{
  while(Serial.available() > 0)
  {
    char command = ((byte)Serial.read());
    string += command;
   }

  if(string == "CONT")
  {
          digitalWrite(ledPin, HIGH);
          delay(500);
          digitalWrite(ledPin, LOW);
          delay(500);
  }
  if(string == "JOG")
  {
      jog();                           
  }
  
}
void jog()
{
      Serial.println("Blink once");
      digitalWrite(ledPin, !digitalRead(ledPin));
      delay(2000);
      Serial.flush();
}

Thanks.

arihant122:
It means I have to clear the serial buffer everytime before sending next command. Am I getting it right?

No, because when there is something in the serail buffer you read it (thus clearing it). It's string that's not empty :wink: And you don't clear that... So still the same problem.

arihant122:
I have implemented changes as per your suggestions. I think they are fine now.

You didn't fix the problem... You only gave it another name. But let me see:
You indeed gave it a better name, made it a const byte, removed command from global and removed the delay() from the Serial.read(). But that still leaves 4 other points :wink: And of course the real problem :wink:

I didn't get the string thing you advised.

const byte ledPin=13;

void setup()
{
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}

void loop()
{
  while(Serial.available() > 0)
    {
      char command = (Serial.read());
      String mode+= command;
    }

  if(mode == "CONT")
    {
       digitalWrite(ledPin, HIGH);
       delay(500);
       digitalWrite(ledPin, LOW);
       delay(500);
    }
  if(mode == "JOG")
    {
      jog();                           
    }
  
}
void jog()
{
    Serial.println("Blink once");
    digitalWrite(ledPin, !digitalRead(ledPin));
    delay(2000);
    Serial.flush();
}

Yeah the real problem still exist. Trying to get solution. thanks

The answer is, clear the String/string. But when you want to clear it is up to you.... Because that can make all the difference :slight_smile:

For string/String, search for "arduino string vs String". String is an object which has some problems... A real C/C++ style string is a NULL terminated char array :slight_smile:

And for the indentation, just press Ctrl+T and see how that looks.

Hi,

I could reach upto this much only. When I send the "JOG" command, LED blinks once (as required) and blink continuously on sending "CONT" command.
But I have to send "JOG" first as you mentioned in your first reply. I am not able to figure a way out.
Here is the sketch:

String mode;
const byte ledPin = 13;


void setup()
{
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop()
{
  while (Serial.available() > 0)
  {
    char command = (Serial.read());
    mode += command;
  }

  if (mode == "CONT")
  {
    digitalWrite(ledPin, HIGH);
    delay(500);
    digitalWrite(ledPin, LOW);
    delay(500);
  }
  if (mode == "JOG")
  {
    //Serial.println("I Blinked");
    digitalWrite(ledPin, !digitalRead(ledPin));
    delay(500);
    mode = "";
  }

}

I don't know how to remove "CONT" string before sending "JOG" as next command as done in the JOG case.
Will String.Remove help?

hint, don't rely on the string containing "CONT" if you want to blink continues. Just know (aka save in a variable) you want to do that :slight_smile:

I tried this but same result.

String next_mode, curr_mode;
const byte ledPin = 13;


void setup()
{
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop()
{
  while (Serial.available() > 0)
  {
    char command = (Serial.read());
    next_mode += command;
  }

  if (next_mode == "CONT" ||  curr_mode == "CONT")
  {
    digitalWrite(ledPin, HIGH);
    delay(500);
    digitalWrite(ledPin, LOW);
    delay(500);
    if (next_mode == "JOG"){
        curr_mode = next_mode;
    next_mode = "";}
  }
  if (next_mode == "JOG" || curr_mode == "JOG")
  {
    //Serial.println("I Blinked");
    digitalWrite(ledPin, !digitalRead(ledPin));
    delay(500);
    if (next_mode == "CONT"){
        curr_mode = next_mode;
    next_mode = "";}
  }

}

I have to work on my programming skill first. :confused: :slightly_frowning_face:

Thanks for your support.
I will get back very soon.

Like I said, String is pretty wasteful. Especially to just save you want to blink continues...

String mode;
const byte ledPin = 13;
bool blinkContinues = false;


void setup()
{
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop()
{
  while (Serial.available() > 0)
  {
    char command = (Serial.read());
    mode += command;
  }

  if (mode == "CONT")
  {
    blinkContinues = true;
    mode = "";
  }
  else if (mode == "JOG")
  {
    //Serial.println("I Blinked");
    blinkContinues = false;
    digitalWrite(ledPin, !digitalRead(ledPin));
    delay(500);
    mode = "";
  }
  
  if(blinkContinues){
    doBlink();
  }

}

void doBlink(){
  digitalWrite(ledPin, HIGH);
  delay(500);
  digitalWrite(ledPin, LOW);
  delay(500);
}

BUTTTTT, this has a lot of disadvantages... First, you use the wasteful String. Second, if you send anything other then "JOG" or "CONT" it stuck. And you use delay()s. In the second a blink takes new actions are ignored. That might not be very bad but it means you can't expand the program very easy. If you want to blink, you can only blink. Do nothing else. Which is a shame...

So next task, fix those three points :slight_smile:

Sorry I was away so could not reply. I will implement your suggestions and post it here for your suggestions.
Thanks for your continuous guidance. :slight_smile:

Now I understood after looking at your program what you meant when you said
"don't rely on the string containing "CONT" if you want to blink continues. Just know (aka save in a variable) you want to do that".
I feel stupid now.:frowning:

I am using string because I want to control the LED from LabVIEW based program. Writing that String makes it easier for user to understand. I will post that also after completion.

When labView sends a string lets say its "JOG". Does it send anything after the G? Typically it will. A EOL char or a newline char, return or something. Find out what that is. Then, don't just blindly read everything. Read each character one by one into a string until you see the end character. Lastly, add a '\0' to terminate the command you got. Now you know you have a complete command in your command buffer(string). Go look to see what it is.

Hope this helps.

-jim lee

arihant122:
Now I understood after looking at your program what you meant when you said
"don't rely on the string containing "CONT" if you want to blink continues. Just know (aka save in a variable) you want to do that".
I feel stupid now.:frowning:

Wise lesson learned :wink:

arihant122:
I am using string because I want to control the LED from LabVIEW based program. Writing that String makes it easier for user to understand. I will post that also after completion.

I don't say you can't send strings to the Arduino. But that's something different then using Strings (with a capital) on the Arduino. A normal C/C++ style string is a NULL-character terminated char array :wink:

char myString[14] = "A string in C"

And did you already think about what should happen if you would send "Hello" and then "JOG"? Or "JO" followed by "CONT"? Or "Jog"? Aka anything else then exactly "CONT" or "JOG"?

Thanks again.

I have made some changes in the program as below. The blinking interval is set as a variable.
Sorry for using too many "delay()" as I am yet to get used to "millis()" :relaxed: . I will change that afterwards.

I will display a message about wrong command input when anything other than required string is entered.

String mode;
const byte ledPin = 13;
bool blinkContinues = false;
unsigned long interval = 500; //default interval is 500 milliseconds


void setup()
{
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
  Serial.println("Enter C for continuous blink");
  Serial.println("Enter J for single blink");
}

void loop()
{
  while (Serial.available() > 0)
  {
    char command = (Serial.read());
    mode += command;
    command = 0;
  }

  if (mode == "C")        // COMMAND FOR CONTINUOUS PULSE
  {
    delay(2000);
    if(Serial.available() > 0)
    {
    Serial.println("");
    Serial.println("set blink interval");
    interval= Serial.parseInt();          //set blinking interval
    Serial.println("Blink interval is ");
    Serial.println(interval);
    blinkContinues = true;
    mode = "";
    }
  }
  else if (mode == "J")   // COMMAND FOR SINGLE BLINK 
  {
    blinkContinues = false;
    digitalWrite(ledPin, !digitalRead(ledPin));
    delay(500);
    mode = "";
  }
    else
    {
          digitalWrite(ledPin, LOW);      //this is default state
          mode = "";
    }
  
  if(blinkContinues){
    doBlink();
  }

}

void doBlink(){
  digitalWrite(ledPin, HIGH);
  delay(interval);
  digitalWrite(ledPin, LOW);
  delay(interval);
}

jimLee:
When labView sends a string lets say its "JOG". Does it send anything after the G? Typically it will. A EOL char or a newline char, return or something. Find out what that is. Then, don't just blindly read everything. Read each character one by one into a string until you see the end character. Lastly, add a '\0' to terminate the command you got. Now you know you have a complete command in your command buffer(string). Go look to see what it is.

Hope this helps.

-jim lee

Thank you Jim. I will keep that in mind.
I will add termination characters to identify start and end of string.

I have made some changes. I am not using String anymore in this program.

//String mode;
char command;
const byte ledPin = 13;
bool blinkContinues = false;
unsigned long interval = 500; //default interval is 500 milliseconds


void setup()
{
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
  Serial.println("Enter C or c for continuous blink");
  Serial.println("Enter J or j for single blink");
}

void loop()
{
  while (Serial.available() > 0)
  {
    command = (Serial.read());
    //command = 0;
  }

  if (command == 'C' || command == 'c')        // COMMAND FOR CONTINUOUS PULSE
  {
    delay(2000);
    Serial.println("");
    Serial.println("set blink interval");
    if (Serial.available() > 0)
    {

      interval = Serial.parseInt();         //set blinking interval
      Serial.println("Blink interval is ");
      Serial.println(interval);
      blinkContinues = true;
      command = 0;
    }
  }
  else if (command == 'J' || command == 'j')   // COMMAND FOR SINGLE BLINK
  {
    blinkContinues = false;
    digitalWrite(ledPin, !digitalRead(ledPin));
    delay(500);
    command = 0;
  }
  else
  {
    
    //delay(2000);
    //Serial.println("Enter valid command");
    digitalWrite(ledPin, LOW);      //this is default state
    command = 0;
    
  }

  if (blinkContinues) {
    doBlink();
  }

}

void doBlink() {
  digitalWrite(ledPin, HIGH);
  delay(interval*.5);
  digitalWrite(ledPin, LOW);
  delay(interval*.5);
}

Now I want to enter continuous mode blink rate along with 'c' command. Something like C200 which would imply continuous blink with 200 blinks per second.

Thanks.

Couple of things for that

Stop using delay() (didn't I already said that? :wink: )

You can use toLower() so you don't have to check for the small letter and the capital letter.

  if (command == 'C' || command == 'c')        // COMMAND FOR CONTINUOUS PULSE
  {
    if (Serial.available() > 0)

Is not a good idea. The program runs faster then the serial does (especially at only 9600) so there is a big change only 'c' is received yet. Granted, the delay will probably fix that but you don't want to use delay().

Myself, I'm not even a fan of parseInt() because it's a blocking function :confused: I would just remember I saw a command that needed folowing by number and just collect them as I see new serial data. And start using it the moment I see a not-ascii number (like a line end '\n')