programming a state machine

hi,
I'm new to arduino programming.
I've got a schoolproject about a state machine. The function of the machine is explained in the pictures.
The problem is that the states don't work. It looks like the loop is passing the states.

#include <Servo.h>

Servo servojq;

int incomingByte      = 0;
int incomingByteCheck = 0;
int outgoingByte      = 0;
int delayvalue = 1000;
int ledpin = 13;
int button = 2;
int potpin = 0;
int val;

int F;
int S;
int R;
int B;
int W;

const int Rest = 10;
const int Step = 11;
const int Stepwait = 12;
const int Follow = 13;
const int Blink = 14;
const int Switch = 15;



void setup()
{
  Serial.begin(9600);
  pinMode (ledpin, OUTPUT);
  pinMode (button, INPUT);
  servojq.attach(3);

}

void loop()
{
  int button_state = digitalRead(button);
  static int state = Rest;

  switch (state)

  {
    case Rest:
      if (digitalRead(Serial.read()) == S) {
        state = Step;
      }
      if (digitalRead(Serial.read()) == B) {
        state = Blink;
      }

      break;


    case Step:
      servojq.write(30);
      delay(1000);
      servojq.write(60);
      delay(1000);
      servojq.write(30);

      state = Stepwait;

      break;


    case Stepwait:
      if (digitalRead (Serial.read()) == F) {
        state = Follow;
      }
      if (digitalRead(Serial.read()) == R) {
        state = Rest;
      }
      break;


    case Follow:
      val = analogRead(potpin);
      val = map(val, 0, 1023, 0, 180);
      servojq.write(val);
      delay(10);

      if (digitalRead (Serial.read()) == S) {
        state = Step;
      }
      else {
        state = Follow;
      }

      break;


    case Blink:
      digitalWrite(13, HIGH);
      delay(1000);
      digitalWrite(13, LOW);
      delay(1000);

      if (digitalRead (Serial.read()) == R) {
        state = Rest;
      }
      if (digitalRead (Serial.read()) == W) {
        state = Switch;
      }

      else {
        state = Blink;
      }

      break;


    case Switch:
      if (button_state == HIGH) {
        digitalWrite(ledpin, HIGH);
        delayvalue = (1000);
      }
      else
      { digitalWrite(ledpin, LOW);
      }

      if (digitalRead (Serial.read()) == R) {
        state = Rest;
      }
      else {
        state = Switch;
      }

      break;
  }
}

Can someone tell me what's wrong with the code?

Here's OP's pix:

What are the values of F, S, R, etc?

if (digitalRead (Serial.read()) == F) {

I'm not sure what digitalRead (-1) will do, but that's what you'll be doing most of the time.

if (digitalRead(Serial.read()) == S) {

The syntax for digitalRead is digitalRead(pin) What does Serial.read return? Is it a pin number?
I think that you need to study up on how serial communication works. Here is a post that covers receiving and parsing serial data.

I need to put a value into the serial monitor (F, S, R, B, W) and then it should switch from state.

OK, to answer my own question, F,S, R, B, W all have the value zero, aka LOW.

How much Arduino programming experience do you have?

AWOL:
OK, to answer my own question, F,S, R, B, W all have the value zero, aka LOW.

How much Arduino programming experience do you have?

I have no experience in aruidno programming...
I've read a lot information but it's something really new for me.
I can do a little bit of matlab, thats all.

OK, some hints:
Only do a Serial.read() when Serial.available() tells you there's something there to read.

I'm not sure what you're doing with all the digitalRead()s.

juul686:
I have no experience in aruidno programming...

This is not a beginner project.

(Although, I see it builds on previous exercises: have you managed those?)

Funnily enough, while I was out for a long walk yesterday, I was thinking through the steps in coding a state machine of about this complexity and OP's example landed at the right moment for me as a weekend exercise.

I think of particular importance is that the blink and step states need to employ delay()-less, millis()-based thinking so as to catch the next incoming command in real time.

I've got some decent Arduino experience and done a fair bit of programming over the years (but I'm not a Programmer with a capital-P by any means) and found this a reasonably complex project.

I'm keen to hear from OP as to what teaching has actually been provided for this project.

OK to help progress a bit and may be serve as food for thoughts for other starters looking at this thread, here is an empty code structure that would help you get started from the right foot with the loop()

  • reading the Serial input to capture a new command
  • handling state changes based on the new command when they are legit
  • handling timing related activities

there are many ways to skin a cat, so this is not supposed to be THE ultimate solution

and Of course I left many many functions voluntarily empty for you to fill in. won't do your job either :slight_smile:

and following the idea described in his images.

// SERVO RELATED
#include <Servo.h>
Servo servojq; // WHAT A STUPID NAME...
const byte servoPin = 3;
const int restAngle = 30;
int stepAngle;
const int delayServo = 5; // ms for servo to avoid mechanical strain
const unsigned long stepperTickPeriod = 250; // ms between two consecutive steps

// LED RELATED
const byte ledPin = 13;
unsigned long lastBlinkTime;
const unsigned long blinkPeriod = 1000ul;

// BUTTONS RELATED
const byte buttonPin = 2;
const byte potentionmeterPin = A0;

enum {REST, STEP, FOLLOW, BLINK, SWITCH} state;

// -----------------------------------------
// SUPPORT STATE FUNCTIONS
// -----------------------------------------

void setRestState()
{
  state = REST;
  if (stepAngle != restAngle) {
    stepAngle = restAngle;
    servojq.write(restAngle);
    delay(delayServo);
    Serial.print(F("Set Rest Angle = ")); Serial.println(stepAngle);
  }
  digitalWrite(ledPin, LOW);
}

void tickStepState()
{
  static unsigned long lastTickStepTime = 0;

  // handle the case when comming from FOLLOW stage where we can be anywhere
  if (stepAngle > 60) {

  }

  if (millis() - lastTickStepTime >= stepperTickPeriod) {
        // Progress the Servo based on incrementing stepAngle but not going over 60


    lastTickStepTime = millis();
  }
}


void tickBkinkState()
{
  if (millis() - lastBlinkTime >= blinkPeriod) {

    // toggle LED

    lastBlinkTime = millis();
  }
}


void tickFollowState()
{
  int newAngle; 

   // DEFINE newAngle based on mapping analogRead(potentionmeterPin) to an angle between  0° and 180°

  if (newAngle != stepAngle) {

    // SET SERVO TO NEW ANGLE ONLY IF DIFFERENT FORM CURRENT ONE

  }
}

void tickSwitchSate()
{
  // SET LED TO MATCH BUTTON POSITION

}


// -----------------------------------------
// STANDARD SKETCH FUNCTIONS
// -----------------------------------------

void setup()
{
  Serial.begin(115200);
  pinMode (ledPin, OUTPUT);
  pinMode (buttonPin, INPUT);
  servojq.attach(servoPin);
  setRestState();
}

void loop()
{
  char serialCommand;
  static boolean newCommand = false;

  if (Serial.available()) {
    serialCommand = Serial.read();
    newCommand = true;
  }

  if (newCommand) { // handle state change if correct command
    switch (serialCommand) {

      case 'F':
        if (state == STEP) {
          state = FOLLOW;
          Serial.println(F("state = FOLLOW"));
        }
        break;

      case 'S':
        if ((state == REST) || (state == FOLLOW)) {
          state = STEP;
          Serial.println(F("state = STEP"));
        }
        break;

      case 'R':
        if (true) { // I THINK YOU GET THE IDEA, DEFINE THE CORRECT TEST, not just true
          setRestState();
          Serial.println(F("state = REST"));
        }
        break;

      case 'B':
        if (true) { // I THINK YOU GET THE IDEA, DEFINE THE CORRECT TEST, not just true
          state = BLINK;
          Serial.println(F("state = BLINK"));
        }
        break;

      case 'W':
        if (true) { // I THINK YOU GET THE IDEA, DEFINE THE CORRECT TEST, not just true
          state = SWITCH ;
          Serial.println(F("state = SWITCH"));
        }
        break;
    }
  }

  // handle ticks for various stages requiring time related activity
  switch (state) {
    case STEP:
      tickStepState();
      break;
    case FOLLOW:
      tickFollowState();
      break;
    case BLINK:
      tickBkinkState();
      break;
    case SWITCH:
      tickSwitchSate();
      break;
  }

}

I just typed that in so there might be mistakes of course. Don't even know if this is compiling

@manor_royal : Don't know if this will make any sense or helps. But when I do a state machine it typically follows the model of two functions.

In the loop() function is a switch of all the states I can be waiting in, along with what I'm waiting for. Kind of a pass through waiting room. You run in to your state, see if anything needs changing and run out.

Then there is a function setState(newState) that is called when the loop() sees a change and its time to change states. setState(newState) has another switch with all the states, but each one has the steps to get to the newState passed in.

The loop has the bubbles & setState() is the connections on the graph.

I find when I use this model, everything gets really easy to deal with and typically it works. Very handy in machine control.

-jim lee

jimLee:
@manor_royal : Don't know if this will make any sense or helps.

Yep that's pretty much what I did thanks.

In loop() I have 2x functions, manageStates() and getCommand(). getCommand() reads the serial input and kicks out if it's illegal (illegal overall that is, like a Z or a Q), then manageStates() is a switch...case where each enum'd state is a case that does its thing (blink a led, move a servo etc) on entry and over time until it's time to exit with a cleanup() on receipt of a valid incoming command (valid that is, for the state, given the allowable paths).

Blinking and servo sweeping is all delay()-less so that the incoming serial isn't missed or held up.

Not beginner territory, though.

jimLee:
The loop has the bubbles & setState() is the connections on the graph.

I find when I use this model, everything gets really easy to deal with and typically it works. Very handy in machine control.

-jim lee

Maybe I'm misunderstanding you but the loop has the conditions to leave each state. For example after 100ms in this_state, go to that_state, if emergency-stop pressed in this_state, go to stop_state. Those are the connections in the diagram.

setState() seems more like it's setting the outputs correctly for each state. It starts the millisecond timer or whatever. That seems to me more like the description written inside each bubble.

AWOL:

if (digitalRead (Serial.read()) == F) {

I'm not sure what digitalRead (-1) will do, but that's what you'll be doing most of the time.

I want to know what on earth juul read that lead to the creation of that monstrosity.

I wonder if this thread's OP or his presumed classmate have read what forum member Nick Gammon has to say on state machines?

MorganS:
Maybe I'm misunderstanding you but the loop has the conditions to leave each state. For example after 100ms in this_state, go to that_state, if emergency-stop pressed in this_state, go to stop_state. Those are the connections in the diagram.

setState() seems more like it's setting the outputs correctly for each state. It starts the millisecond timer or whatever. That seems to me more like the description written inside each bubble.

Well I did'n't read the text in the bubbles. I just glanced at the pictures. I saw the bubbles as little places to wait for things to change and the lines as making changes. From state(bubble) to state(bubble).

Or maybe we're saying the same thing?

Donno'

-jim lee

jimLee:
Well I did'n't read the text in the bubbles. I just glanced at the pictures. I saw the bubbles as little places to wait for things to change and the lines as making changes. From state(bubble) to state(bubble).

Or maybe we're saying the same thing?

Donno'

-jim lee

Sounds like you're interpreting it correctly.

Which the code I posted above would do basically