Finite State Machine and serial communication

Hi,

I am using two Arduino Mega's:

Master - This gets it's orders from bluetooth (something like e,f,g... or 1,2,3...). What I want is each case is some letter or number. And and each case sends another letter or number to the Arduino Slave.

Slave - This gets it's orders from Arduino Master over serial (something like a,b,c... or 5,6,7...). And when the right letter or number is received, it does it's case.

This is Arduino Slave short test code without seral.read, because I don't know how to make a working one:

int Button1 = 43;
int Button2 = 45;
int DC1 = 10;
int DC2 = 11;

void setup()
{
  pinMode(Button1, INPUT_PULLUP);
  pinMode(Button2, INPUT_PULLUP);
  pinMode(DC1,OUTPUT);
  pinMode(DC2,OUTPUT);
  Serial.begin(9600);
}
void loop()
{
  switch(state)
  {

    case 0: 
     break;    
      
    case 1:
      if ------------------------ This is where I want something like "serial.read receives letter a"
      {
        Serial.print("DC left");
        digitalWrite(DC1, LOW);
        digitalWrite(DC2, HIGH);
      }
      else if(digitalRead(Button1) == HIGH)
      {
       Serial.print("DC left stop");
        digitalWrite(DC1, LOW);
        digitalWrite(DC2, LOW);
        state = 0;
      }

      
      break;
      
    case 2:
       if ------------------------ This is where I want something like "serial.read receives letter b"
      {
        Serial.print("DC right");
        digitalWrite(DC1, HIGH);
        digitalWrite(DC2, LOW);
      }
      else if(digitalRead(Button1) == HIGH)
      {
       Serial.print("DC right stop");
        digitalWrite(DC1, LOW);
        digitalWrite(DC2, LOW);
        state = 0;
      }
      
      break;
      
    default:
      state = 0;
      break;
  }
}

Arduino Master code is similar. But there will be sensors over analogRead and Wire library. Some Nema steppers and relays.

I think Finite State Machine like code is way to go. I know I can't use strings. And I think I'm overthinking or missing something :smiley: I have tried many thing, but they don't work together. Can It be done in this way, and how?

Sorry for any mistakes. English is not my native language

The serial input basics thread may have information that you can use. See how to form packets of data to send that are easy to read and parse reliably.

FiveO:
...State Machine like code is way to go. I know I can't use strings. And I think I'm overthinking or missing something :smiley: I have tried many thing, but they don't work together. Can It be done in this way, and how?

are you able to get the MultiSerial example in the IDE working, between the two Megas?

have you established any sort of Serial communication successfully? (forgetting for now about all the other stuff).

Yes I have established. Sending single character over bluetooth like a,b,c... works. This is the part I tested and it works:

void loop() {

if(Serial.available()>0)
{
char data= Serial.read();
switch(data)
{
case 'a':
 case 1:
break;

case 'b':
 case 2:
break;

case 'c':
 case 3:
break;

case 'd':
 case 4:
break;
}
Serial.println(data);
}
}

But when I add the state cases, they don't work.

void loop() {

if(Serial.available()>0)
{
char data= Serial.read();
switch(data)
{
case 'a':
 case 1:
break;

case 'b':
 case 2:
break;

case 'c':
 case 3:
break;

case 'd':
 case 4:
break;
}
Serial.println(data);
}

static int state = 0
switch(state)
{

case 0:
break;

case 1:
do something
state = 0;
break;

case 2:
do something
state = 0;
break;

case 3:
do something
state = 2;
break;

case 4:
do something
state = 1;
break;

default:
state = 0;
break;
}

}

But when I add the state cases, they don't work.

That code won't even compile, so it is silly to talk about it working, or not.

Post some code that actually compiles. THEN, we can talk about what it actually does, and how that differs from what you want.

"It doesn't work" is the lamest possible thing you can say here.

Sorry about that

This now compiles:

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

void loop() {

if(Serial.available()>0)
{
char data= Serial.read();
switch(data)
{
case 'a':
 case 1:
break;

case 'b':
 case 2:
break;

case 'c':
 case 3:
break;

case 'd':
 case 4:
break;
}
Serial.println(data);
}

static int state = 0;
switch(state)
{

case 0:
break;

case 1:
Serial.println("Case 1");
state = 0;
break;

case 2:
Serial.println("Case 2");
state = 0;
break;

case 3:
Serial.println("Case 3");
state = 2;
break;

case 4:
Serial.println("Case 4");
state = 1;
break;

default:
state = 0;
break;
}

}

what is this supposed to do:

    switch (data)
    {
      case 'a':
      case 1:
        break;

      case 'b':
      case 2:
        break;

      case 'c':
      case 3:
        break;

      case 'd':
      case 4:
        break;
    }

?

switch(data)
{
case 'a':
 case 1:
break;

What do you think that case 1: (and other code lines like it) does in this portion of code ?

Guessing at what you're trying to achieve, try this:

case 'a':
 state=1;
break;

You will need to move the state variable declaration to the top of the function too.

Why 2 switch/cases ?

The character from Serial.read() tells you which state you are in so why not

    char data = Serial.read();
    switch (data)
    {
      case 'a':
        Serial.println("Case 1");
        break;
      case 'b':
        Serial.println("Case 2");
        break;

etc, etc

This now compiles:

But is piss-poorly indented. Use Tools + Auto Format.

You failed to say what the code actually does, or what you expect it to do. It seems like you don't really want help.

wildbill:
Guessing at what you're trying to achieve, try this:

case 'a':

state=1;
break;



You will need to move the state variable declaration to the top of the function too.

Thank You! That was what I wanted.

BulldogLowell:
what is this supposed to do:

    switch (data)

{
     case 'a':
     case 1:
       break;

case 'b':
     case 2:
       break;

case 'c':
     case 3:
       break;

case 'd':
     case 4:
       break;
   }




?

wildbill answered that

UKHeliBob:

switch(data)

{
case 'a':
case 1:
break;



What do you think that case 1: (and other code lines like it) does in this portion of code ?

I didn't know, now I'm smarter.

UKHeliBob:
Why 2 switch/cases ?

The character from Serial.read() tells you which state you are in so why not

    char data = Serial.read();

switch (data)
   {
     case 'a':
       Serial.println("Case 1");
       break;
     case 'b':
       Serial.println("Case 2");
       break;



etc, etc

Because I need changing between states. And I don't know other way to do it from Serial.read. Something like - if I am in Case 'b' , if some button/sensor do what I want, then go to Case 'q', or Case 'd' to Case 'a'.

PaulS:
But is piss-poorly indented. Use Tools + Auto Format.

You failed to say what the code actually does, or what you expect it to do. It seems like you don't really want help.

Now I know, thanks for the Auto Format hint. Half the answer is in the upper Quote, other is in the first post.

Because I need changing between states. And I don't know other way to do it from Serial.read. Something like - if I am in Case 'b' , if some button/sensor do what I want, then go to Case 'q', or Case 'd' to Case 'a'.

Here is the skeleton of a program that you may be able to use ideas from.

boolean thisCondition = false;
boolean thatCondition = false;
char state;

void setup()
{
  Serial.begin(115200);
  Serial.println();
}

void loop()
{
  //put code here executed each time through loop() that may change thisCondition and/or thatCondition
  
  state = Serial.read();  //will be -1 if no Serial data is available

  if (thisCondition == true)
  {
    state = 'x';
  }
  if (thatCondition == true)
  {
    state = 'y';
  }

  switch (state)
  {
    case 'a':
      //do a stuff
      break;
   
    case 'b':
      //do b stuff
      break;
   
    case 'c':
      //do c stuff
      break;
   
    case 'x':
      //do x stuff
      break;
   
    case 'y':
      //do y stuff
      break;
  }
}

As currently written a state set by the program overrides the state set from Serial and it is possible for the user to enter states 'x' or 'y', but it would be easy to change the priority and/or to filer the user input.

UKHeliBob:
Here is the skeleton of a program that you may be able to use ideas from.

boolean thisCondition = false;

boolean thatCondition = false;
char state;

void setup()
{
 Serial.begin(115200);
 Serial.println();
}

void loop()
{
 //put code here executed each time through loop() that may change thisCondition and/or thatCondition
 
 state = Serial.read();  //will be -1 if no Serial data is available

if (thisCondition == true)
 {
   state = 'x';
 }
 if (thatCondition == true)
 {
   state = 'y';
 }

switch (state)
 {
   case 'a':
     //do a stuff
     break;
 
   case 'b':
     //do b stuff
     break;
 
   case 'c':
     //do c stuff
     break;
 
   case 'x':
     //do x stuff
     break;
 
   case 'y':
     //do y stuff
     break;
 }
}




As currently written a state set by the program overrides the state set from Serial and it is possible for the user to enter states 'x' or 'y', but it would be easy to change the priority and/or to filer the user input.

Thank You for your idea

I have now tested my other things separately (sending char from bluetooth to Arduino, sending char from Arduino to another Arduino, stepper motors, sensors, relays, dc motors and mixing it with switch case), and these work.

But... I have problems with this test code for the base of other things. This is simplified base code for testing. Problems with the code are:

*When I send trough bluetooth char a,b or c, it serial prints Program1 Case1, Program2 Case1, Program3 Case1 instead what I want - a goes to Program1, b goes to Program2 and c to Program3.
*If Program1 or 2 or 3 is started, but it doesn't go to case 1 to case 2, case 2 to case 3 and 3 to 4.

void setup() {

  Serial.begin(9600);
  Serial1.begin(9600);
}

void loop() {

  static int Program1 = 1;
  static int Program2 = 1;
  static int Program3 = 1;

  if (Serial.available() > 0) {
    char data = Serial.read();
    switch (data)
    {
      case 'a':
        Program1 = 1;
        break;
      case 'b':
        Program2 = 1;
        break;
      case 'c':
        Program3 = 1;
        break;
    }

    switch (Program1)
    {
      case 0:
        break;
      case 1:
        Serial.println("Program1 Case 1");
        break;
      case 2:
        Serial.println("Program1 Case 2");
        Serial1.print("d");                       ///// Sending to other Arduino through Serial1
        break;
      case 3:
        Serial.println("Program1 Case 3");
        break;
      case 4:
        Serial.println("Program1 Case 4");
        Program1 = 0;
        break;
      default:
        break;
    }

    switch (Program2)
    {
      case 0:
        break;
      case 1:
        Serial.println("Program2 Case 1");
        break;
      case 2:
        Serial.println("Program2 Case 2");
        Serial1.print("e");                       ///// Sending to other Arduino through Serial1
        break;
      case 3:
        Serial.println("Program2 Case 3");
        break;
      case 4:
        Serial.println("Program2 Case 4");
        Program2 = 0;
        break;
      default:
        break;
    }

    switch (Program3)
    {
      case 0:
        break;
      case 1:
        Serial.println("Program3 Case 1");
        break;
      case 2:
        Serial.println("Program3 Case 2");
        Serial1.print("f");                       ///// Sending to other Arduino through Serial1
        break;
      case 3:
        Serial.println("Program3 Case 3");
        break;
      case 4:
        Serial.println("Program3 Case 4");
        Program3 = 0;
        break;
      default:
        break;
    }
  }
}
    switch (Program1)
    {
      case 0:
        break;
      case 1:
        Serial.println("Program1 Case 1");
        break;
      case 2:
        Serial.println("Program1 Case 2");
        Serial1.print("d");                       ///// Sending to other Arduino trough Serial1
        break;
      case 3:
        Serial.println("Program1 Case 3");
        break;
      case 4:
        Serial.println("Program1 Case 4");
        Program1 = 0;
        break;
      default:
        break;

Depending on the value of Program1 different messages will be printed. Where in the program do you ever set Program1 to anything except 0 or 1 ?

You have 3 consecutive switch statements; they all will be executed. As your ProgramX variables are always 1, you will see the behaviour that you mention.

Based on your original requirement, this will be a far less complex solution

void loop()
{
  if (Serial.available() > 0)
  {
    switch(Serial.read())
    {
      case '1':
        Serial1.print("a");
        break;
      case '2':
        Serial2.print("b");
        break;
      case '3':
        Serial3.print("c");
        break;
    }
  }
}

UKHeliBob:

    switch (Program1)

{
     case 0:
       break;
     case 1:
       Serial.println("Program1 Case 1");
       break;
     case 2:
       Serial.println("Program1 Case 2");
       Serial1.print("d");                       ///// Sending to other Arduino trough Serial1
       break;
     case 3:
       Serial.println("Program1 Case 3");
       break;
     case 4:
       Serial.println("Program1 Case 4");
       Program1 = 0;
       break;
     default:
       break;



Depending on the value of Program1 different messages will be printed. Where in the program do you ever set Program1 to anything except 0 or 1 ?

I don't know, I'm new to it. It's hard to explain thing that I don't fully understand.

When I tested serial read char with one switch case, it worked ( char started case what I wanted), but now with multiple switch case it don't work.

Simple explanation what I want is:
receive char a - starts switch case for program 1
receive char b - starts switch case for program 2
receive char c - starts switch case for program 3
and on ...

and when switch case goes to the end, the program is over and waiting for another char.

sterretje:
You have 3 consecutive switch statements; they all will be executed. As your ProgramX variables are always 1, you will see the behaviour that you mention.

Based on your original requirement, this will be a far less complex solution

Yes... But I don't want them all executed, one per time when right char is met.

receive char a - starts switch case for program 1

But in order for the switch/case for program 1 to execute its cases its case variable (Program1) must be changed from one value to the next, which is what you are not doing.

In addition to that, the whole program is wrapped in a Serial.available() test so that once the data char has been read there will not be anything to read so none of the cases for any value of data will be executed.

To help, please explain what should happen when an 'a' is received. What should be printed ? What should be printed when the next 'a' is received, and so on ?