Help needed to get this state machine to work

The following code, measures two load cells and then depending on the weight difference, prints out a case number and plays a specific audio file.

I am not able to figure out why this won't loop. I see only the first reading and then it stops with nothing on the screen. What am I missing? I tried the same logic with if-else-if loops and that works flawlessly.

#include <HX711.h>
#include <DFRobotDFPlayerMini.h>
#include <SoftwareSerial.h>

#define SOFTWARE_SERIAL_BAUD 9600
#define SERIAL_BAUD 9600
#define MIN_WT 45
#define MAX_WT 130
#define BUSY LOW
#define VOLUME 10

const uint8_t dataPin1 = 7;
const uint8_t clockPin1 = 8;
const uint8_t dataPin2 = 2;
const uint8_t clockPin2 = 3;

const uint8_t green2 = A5;
const uint8_t blue2 = A4;
const uint8_t red2 = A3;
const uint8_t green1 = A1;
const uint8_t blue1 = A2;
const uint8_t red1 = A0;

const uint8_t busyPin = 6;

const uint8_t rxPin = 5;
const uint8_t txPin = 4;

int wt1, wt2, wt_diff;
int prev_wt1 = 0, prev_wt2 = 0;
bool measureComplete;

// Define the state enumeration
enum State {
  IDLE,
  MEASURE,
  COMPARE,
  ACTION,
  UPDATE
};

State currentState = IDLE;

// Define colors as hex values
const uint32_t red = 0xFF0000;
const uint32_t green = 0x00FF00;
const uint32_t blue = 0x0000FF;
const uint32_t cyan = 0x00FFFF;       //Green and Blue at full brightness
const uint32_t magenta = 0xFF00FF;    //Red and Blue at full brightness
const uint32_t lightBlue = 0x4CCCFF;  //Red at around 30 percent of brightness, green up high at 80 percent, and blue at full brightness
const uint32_t purple = 0xFF99FF;     //Red and blue are all the way up, with green at 60 percent
const uint32_t white = 0xFFFFFF;      //All colors at full brightness
const uint32_t off = 0x000000;

// Calibration constants for the load cells
const float calibrationCell1 = 1932.124511;
const float calibrationCell2 = 1997.294799;
const long offsetCell1 = 169060;
const long offsetCell2 = 72511;

HX711 scale1, scale2;
SoftwareSerial mySoftwareSerial(rxPin, txPin);
DFRobotDFPlayerMini myDFPlayer;

// Function to set the color of the RGB LED
void setColor(uint32_t color) {
  uint8_t redValue = (color >> 16) & 0xFF;
  uint8_t greenValue = (color >> 8) & 0xFF;
  uint8_t blueValue = color & 0xFF;

  analogWrite(red1, redValue);
  analogWrite(red2, redValue);
  analogWrite(green1, greenValue);
  analogWrite(green2, greenValue);
  analogWrite(blue1, blueValue);
  analogWrite(blue2, blueValue);
}

// Play DFR audio
void playAudio(uint8_t fileNumber) {
  myDFPlayer.playMp3Folder(fileNumber);
  // delay(delayInSeconds * 1000);
  while (digitalRead(busyPin) != BUSY) {
    ;  // // This loop waits for the DFR player to get BUSY.
  }
  while (digitalRead(busyPin) == BUSY) {
    ;  // This loop executes while the DFR BUSY pin is LOW indicating playback in progress.
  }
}

void setup() {
  //Initialize serial and wait for port to open
  Serial.begin(SERIAL_BAUD);
  mySoftwareSerial.begin(SOFTWARE_SERIAL_BAUD);
  delay(100);

  Serial.println("Starting up...");

  pinMode(red1, OUTPUT);
  pinMode(green1, OUTPUT);
  pinMode(blue1, OUTPUT);
  pinMode(red2, OUTPUT);
  pinMode(green2, OUTPUT);
  pinMode(blue2, OUTPUT);
  
  pinMode(busyPin, INPUT);

  scale1.begin(dataPin1, clockPin1);
  scale2.begin(dataPin2, clockPin2);

  scale1.set_offset(offsetCell1);
  scale1.set_scale(calibrationCell1);
  scale2.set_offset(offsetCell2);
  scale2.set_scale(calibrationCell2);

  // // reset the scale to zero = 0
  scale1.tare(20);
  scale2.tare(20);

  // Initialize the DFPlayer Mini
  if (!myDFPlayer.begin(mySoftwareSerial)) {
    Serial.println("DFPlayer Mini not detected. Please check connections and if SD card is inserted.");
    while (true) {
      ;
    }
  }
  myDFPlayer.volume(VOLUME);
  delay(250);
  setColor(blue);
  playAudio(1);

  Serial.println("Start up complete!");
}

void loop() {
  switch (currentState) {
    case IDLE:
      if (scale1.is_ready() && scale2.is_ready()) {
        currentState = MEASURE;
      }
      break;

    case MEASURE:
      wt1 = int(scale1.get_units(10));
      wt1 = abs(wt1);
      wt2 = int(scale2.get_units(10));
      wt2 = abs(wt2);
      if ((wt1 > MAX_WT) || (wt1 < MIN_WT)) {
        wt1 = 0;
      }
      if ((wt2 > MAX_WT) || (wt2 < MIN_WT)) {
        wt2 = 0;
      }
      Serial.println("");
      Serial.print("Wt1 = ");
      Serial.println(wt1);
      Serial.print("Wt2 = ");
      Serial.println(wt2);
      currentState = COMPARE;
      break;

    case COMPARE:
      int wt1_curr_prev_diff = wt1 - prev_wt1;
      int wt2_curr_prev_diff = wt2 - prev_wt2;
      if ((wt1 > 0) && (wt2 > 0) && (wt1_curr_prev_diff == 0) && (wt2_curr_prev_diff == 0)) {
        measureComplete = true;
        wt_diff = wt2 - wt1;
        wt_diff = abs(wt_diff);
      } else {
        measureComplete = false;
      }
      Serial.print("measureComplete = ");
      Serial.println(measureComplete);
      currentState = ACTION;
      break;

    case ACTION:
      if (measureComplete) {
        if (wt1 > 0 && wt2 > 0) {
          if (wt_diff > 3 && wt_diff < 9) {
            Serial.println("Case 1");
            setColor(green);
            playAudio(2);
          } else if (wt_diff > 9 && wt_diff < 14) {
            Serial.println("Case 2");
            setColor(blue);
            playAudio(3);
          } else if (wt_diff > 14 && wt_diff < 19) {
            Serial.println("Case 3");
            setColor(purple);
            playAudio(4);
          } else if (wt_diff > 19 && wt_diff < 26) {
            Serial.println("Case 4");
            setColor(magenta);
            playAudio(5);
          } else if (wt_diff > 26 && wt_diff < 32) {
            Serial.println("Case 5");
            setColor(white);
            playAudio(6);
          } else {
            setColor(lightBlue);
          }
        }
      }
      currentState = UPDATE;
      break;

    case UPDATE:
      prev_wt1 = wt1;
      prev_wt2 = wt2;
      currentState = IDLE;  // Go back to the initial state
      break;

    default:
      currentState = IDLE;
      break;
  }
}

// -- END OF FILE --

On running this code, I see the following and this does not repeat as it should.

Starting up...
Start up complete!

wt1 = 0
wt2 = 0
measureComplete = 0

Add a serial.println just after each case statement printing the name of that case. This way you know where you are in the code. After that, add more prints to zero in. I think you will find measurecomplete is false.
I made a small scale, and most of your code looks foreign to me.

The compiler is, as always, doing exactly what you told it to do.

Which is not always the same as what you thought it should do.

This, for example, does not do what you thought it does. And it's likely the cause of your problem. You cannot, cannot, cannot declare variables in a case without creating a scope for the variables by enclosing the statements in the case in { and }. This is C 101 stuff.

    case COMPARE:
      int wt1_curr_prev_diff = wt1 - prev_wt1;
      int wt2_curr_prev_diff = wt2 - prev_wt2;

And compiling your sketch with the warning level set to ALL reveals that the compiler had lots to tell you if you'd let it:

arduino-cli compile -b arduino:avr:uno --warnings all --output-dir ~/tmp --no-color (in directory: /home/me/Documents/sketchbook/Uno_R3/test)
/home/me/Documents/sketchbook/Uno_R3/test/test.ino: In function 'void loop()':
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:177:10: warning: jump to case label [-fpermissive]
     case ACTION:
          ^~~~~~
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:164:11: note:   crosses initialization of 'int wt2_curr_prev_diff'
       int wt2_curr_prev_diff = wt2 - prev_wt2;
           ^~~~~~~~~~~~~~~~~~
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:163:11: note:   crosses initialization of 'int wt1_curr_prev_diff'
       int wt1_curr_prev_diff = wt1 - prev_wt1;
           ^~~~~~~~~~~~~~~~~~
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:208:10: warning: jump to case label [-fpermissive]
     case UPDATE:
          ^~~~~~
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:164:11: note:   crosses initialization of 'int wt2_curr_prev_diff'
       int wt2_curr_prev_diff = wt2 - prev_wt2;
           ^~~~~~~~~~~~~~~~~~
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:163:11: note:   crosses initialization of 'int wt1_curr_prev_diff'
       int wt1_curr_prev_diff = wt1 - prev_wt1;
           ^~~~~~~~~~~~~~~~~~
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:214:5: warning: jump to case label [-fpermissive]
     default:
     ^~~~~~~
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:164:11: note:   crosses initialization of 'int wt2_curr_prev_diff'
       int wt2_curr_prev_diff = wt2 - prev_wt2;
           ^~~~~~~~~~~~~~~~~~
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:163:11: note:   crosses initialization of 'int wt1_curr_prev_diff'
       int wt1_curr_prev_diff = wt1 - prev_wt1;
           ^~~~~~~~~~~~~~~~~~
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:136:10: warning: enumeration value 'ACTION' not handled in switch [-Wswitch]
   switch (currentState) {
          ^
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:136:10: warning: enumeration value 'UPDATE' not handled in switch [-Wswitch]
Sketch uses 8208 bytes (25%) of program storage space. Maximum is 32256 bytes.
Global variables use 550 bytes (26%) of dynamic memory, leaving 1498 bytes for local variables. Maximum is 2048 bytes.
Used library        Version Path
HX711               0.5.2   /home/me/Documents/sketchbook/libraries/HX711
DFRobotDFPlayerMini 1.0.6   /home/me/Documents/sketchbook/libraries/DFRobotDFPlayerMini
SoftwareSerial      1.0     /home/me/.arduino15/packages/arduino/hardware/avr/1.8.3/libraries/SoftwareSerial
Used platform Version Path
arduino:avr   1.8.3   /home/me/.arduino15/packages/arduino/hardware/avr/1.8.3
Compilation finished successfully.

By failing to create a scope, your ACTION, UPDATE and default cases all look like errors jumping into the middle of the COMPARE case and don't get handled in the switch.

Properly enclosing the case in { and } to create a scope for the local variables looks like this:

    case COMPARE:
      {
         int wt1_curr_prev_diff = wt1 - prev_wt1;
         int wt2_curr_prev_diff = wt2 - prev_wt2;
         if ((wt1 > 0) && (wt2 > 0) && (wt1_curr_prev_diff == 0) && (wt2_curr_prev_diff == 0)) {
           measureComplete = true;
           wt_diff = wt2 - wt1;
           wt_diff = abs(wt_diff);
         } else {
           measureComplete = false;
         }
         Serial.print("measureComplete = ");
         Serial.println(measureComplete);
         currentState = ACTION;
      }
      break;

resulting in a compile that looks like this:

arduino-cli compile -b arduino:avr:uno --warnings all --output-dir ~/tmp --no-color (in directory: /home/me/Documents/sketchbook/Uno_R3/test)
already nofloat
Sketch uses 8700 bytes (26%) of program storage space. Maximum is 32256 bytes.
Global variables use 590 bytes (28%) of dynamic memory, leaving 1458 bytes for local variables. Maximum is 2048 bytes.
Used library        Version Path
HX711               0.5.2   /home/me/Documents/sketchbook/libraries/HX711
DFRobotDFPlayerMini 1.0.6   /home/me/Documents/sketchbook/libraries/DFRobotDFPlayerMini
SoftwareSerial      1.0     /home/me/.arduino15/packages/arduino/hardware/avr/1.8.3/libraries/SoftwareSerial
Used platform Version Path
arduino:avr   1.8.3   /home/me/.arduino15/packages/arduino/hardware/avr/1.8.3
Compilation finished successfully.

Complaining about the program "not doing what it should" is a waste of time.

The program always does exactly you tell it to do.

The fact that it doesn't do what you wanted it to is your fault, not its. Make use of the tools you have available to you to allow the compiler to point out when you're doing something dumb. Not allowing it to do so is entirely counterproductive.

2 Likes

Thank you. I was unaware of this. I will modify my code keeping this in mind and report back.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.