Trying a "state machine"

Hey I am trying to replicate something a BIOS POS boot sequence.

I want to check if different switches and values have different states and everything the state check passed a variable here WandStartUpID should get incremented to check the next state.

Every time the check fails the WandStartUpID should get set to "ERROR".

then when the function gets called a again in the loop there should be differentiated if WandStartUpID=ERROR, then the function is used without delay. if the previous check succedded so that WandStartUpID != ERROR the function should only be executed after amount of time. with this i want to let the function to a led blinking.

if all test got passed WandStartUpID = passed and the function will no longer be called again.

i defined the variable as an enum:

enum SelfTestSequence{
    coldStart, // sets to 0
    isVentPoti_off,
    isBarGraph_SW_off,
    isStartUp_SW_off,
    passed,
    ERROR
};

the function i am building and try to test is this here with serial prints for checking if the right states get executed but i am stuck for hour now and i cannot get it working. I did a much bigger function but this didn't work either so i started from scratch with this one that is also not working as i want:

SelfTestSequence WandStartUpID = coldStart;

void PROGRAM_TEST(SelfTestSequence &currentTestID){
  static unsigned long cycle_Time_TEST = 0;

  Serial.println("Test excecuted");
  Serial.println(currentTestID);
  Serial.println(millis() - cycle_Time_TEST);
  Serial.println();


  if(currentTestID == ERROR){
    Serial.println("Startet with ERROR");
    currentTestID = coldStart;
    delay(1000);
  }

  if(currentTestID != ERROR && (millis() - cycle_Time_TEST) > PERIOD_SELFTEST){
    Serial.println("Startet of Time");
  }

  if(currentTestID == ERROR || (currentTestID != ERROR && (millis() - cycle_Time_TEST) > PERIOD_SELFTEST)){
    Serial.println("Function real start");


    if(currentTestID == coldStart){
      currentTestID = isVentPoti_off;
      Serial.print(SW_Vent.read());
      Serial.println("  ->isVentPoti_off");
    }
    else currentTestID = ERROR;

    if(currentTestID == isVentPoti_off){
      Serial.println(Poti_adjusted);
      if(Poti_adjusted < 1){
      currentTestID = isBarGraph_SW_off;
      Serial.println("  ->isBarGraph_SW_off");
      }
    }
    else currentTestID = ERROR;


    delay(2500);
    updateProgramCallTimer(cycle_Time_TEST);
  }
  Serial.println("Test exited !!");
}
void loop() {
  // put your main code here, to run repeatedly:

  Poti_val = analogRead(Poti_Pin);
  Poti_adjusted = constrain(Poti_val, 15, 1005);
  Poti_adjusted = map(Poti_adjusted, 14, 1005, 0, 254);


  PROGRAM_TEST(WandStartUpID);


  Serial.print(" |");
  Serial.println(Poti_adjusted);
  delay(100);

  
  current_Program_Run_Time = millis();
}

Additional Code:

unsigned long current_Program_Run_Time = 0;
const int PERIOD_SELFTEST = 7500;
void updateProgramCallTimer(unsigned long& update_program_cycle){
  update_program_cycle = current_Program_Run_Time;
}

Quick - format your code in the IDE with the Auto Format tool.

Then use the IDE Copy for Forum tool to put your sketch on the clipboard, and come back here and edit your post so you code

void setupz() {

// looks like code

}

void loop() {

// yes it does 
}

Just paste it over the code you posted incorrectly.

Then I will delete this post, and no one will know you didn't in the first place. :expressionless:

a7

1 Like

Okay I spotted the first mistake, I have to move

currentTestID = coldStart; from 

to

if(currentTestID == ERROR || (currentTestID != ERROR && (millis() - cycle_Time_TEST) > PERIOD_SELFTEST)){
if(currentTestID = ERROR) currentTestID = coldStart;

Is that it?

Srsly, more eyes will be on this if you list thevcomplete sketch all at a go. Do not make us all work reassembling your sketch, which I would bungle, for sure, and I would wonder what you still are leaving out, if anyhting.

When you make progress, post a complete sketch again, not notes on how we can play along at home. or on the beach as it may be for some of us.

TIA

a7

1 Like

The code is too much entangled, it is hard to maintain and it is hard to check what is going on, for us and for yourself.

If you use a real Finite State Machine: The Finite State Machine | Majenko Technologies
then you have something that we are familiar with.
It combines well with millis timers.

Do you want to create a startup sequence with delay, then you don't need to use millis(). Perhaps if you are waiting from a response, but I don't see that in the sketch.
Or do you want something that can run while other code (for example checking buttons) can also run ? Then you need millis() and you should get rid of the delays.

They are two completely different thing, and you have a combination of both.

hey thx i didn't know that the ide has an auto format tool:

here is the code but with many additional stuff that isn't used here, i'll ad the other stuff later when i get the state thing working:

#include <FastLED.h>
#include <Button.h>

//DM13A classes
#include "DM13A_LedSegment.h"
#include "DM13A.h"

//Speaker
#include <SoftwareSerial.h>
#include <Arduino.h>
#include "DYPlayerArduino.h"



//Cyclotron LEDs
#define NUM_LEDS_CYCLOTRON 28
#define CYCLOTRON_LED_PIN A1
CRGB cyclotron_leds[NUM_LEDS_CYCLOTRON];

const int PERIOD_CYCLOTRON = 5;
unsigned long current_Program_Run_Time = 0;



//Poti test
#define Poti_Pin A6
#define NUM_LEDS_VENT 28
#define LED_PIN A0
int Poti_val = 0;
int Poti_adjusted = 0;

CRGB vent_leds[NUM_LEDS_VENT];



//Buttons
Button SW_WandTip(10);
Button SW_StartUp1(8);
Button SW_StartUp2(7);
Button SW_Intensify(4);
Button SW_BarGraph(3);
Button SW_Vent(2);



//DM13A
constexpr byte LATCH_PIN = 12;  // ST_CP    // LAT   -> for LED-Shiftregisters
constexpr byte CLOCK_PIN = 11;  // SH_CP    // SCLK  -> for LED-Shiftregisters
constexpr byte DATA_PIN = 6;    // DS       // SIN   -> for LED-Shiftregisters
constexpr byte CHAINED_CHIPS = 2;
DM13A DM13ALeds(LATCH_PIN, CLOCK_PIN, DATA_PIN, (2 * CHAINED_CHIPS));


const int PERIOD_POWERCELL_IDLE = 70;
const int PERIOD_BARGRAPH_IDLE = 90;
const int PERIOD_BARGRAPH_FIRING_IDLE = 50;
const int PERIOD_SELFTEST = 7500;


constexpr byte Power_Cell_Leds[][2] = { { 1, 7 }, { 0, 0 }, { 0, 1 }, { 0, 2 }, { 0, 3 }, { 0, 4 }, { 0, 5 }, { 0, 6 }, { 0, 7 }, { 1, 1 }, { 1, 2 }, { 1, 3 }, { 1, 4 }, { 1, 5 }, { 1, 6 } };
const DM13A_LedSegment Power_Cell_Segment(Power_Cell_Leds);

constexpr byte BarG_Element_Leds[][2] = { { 2, 6 }, { 2, 5 }, { 2, 4 }, { 2, 3 }, { 2, 2 }, { 2, 1 }, { 3, 7 }, { 3, 6 }, { 3, 5 }, { 3, 4 }, { 3, 3 }, { 3, 2 } };
const DM13A_LedSegment BarG_Element_Segment(BarG_Element_Leds);


constexpr byte n_Led[][2] = { 2, 7 };
const DM13A_LedSegment n_Segment(n_Led);

constexpr byte k_Led[][2] = { 3, 1 };
const DM13A_LedSegment k_Segment(k_Led);

constexpr byte l_Led[][2] = { 3, 0 };
const DM13A_LedSegment l_Segment(l_Led);


const byte SlowBlow_Led[][2] = { 2, 0 };

enum SelfTestSequence {
  coldStart,  // sets to 0
  isVentPoti_off,
  isBarGraph_SW_off,
  isStartUp_SW_off,
  passed,
  ERROR
};

//Speaker 0
#define rxPin0 A4
#define txPin0 A5

SoftwareSerial mySerial0 = SoftwareSerial(rxPin0, txPin0);
DY::Player playerSoftware0(&mySerial0);


const int L_SlowBlow = 5;


//Cyclotron LEDs cycle
void setRedColor(int index, int brightness) {
  cyclotron_leds[index] = CRGB(brightness, 0, 0);
  //FastLED.show();
}


void updateProgramCallTimer(unsigned long& update_program_cycle) {
  update_program_cycle = current_Program_Run_Time;
}


static byte active_LED = 0;
static bool cycleUp = true;
static int current_brightness = 0;

void PROGRAM_Cyclotron() {
  static unsigned long cycle_Time_Cyclotron = 0;
  static int max_brightness = 100;

  if (millis() - cycle_Time_Cyclotron > PERIOD_CYCLOTRON) {
    if (cycleUp) {
      if (current_brightness < max_brightness) {
        current_brightness++;
      }
      setRedColor(active_LED, current_brightness);
      if (current_brightness == max_brightness) cycleUp = false;
    }

    if (!cycleUp) {
      if (current_brightness > 0) {
        current_brightness--;
        setRedColor(active_LED, current_brightness);
      }
      if (current_brightness == 0) {
        cycleUp = true;
        if (active_LED == 21) active_LED = 0;
        else active_LED += 7;
      }
    }
    updateProgramCallTimer(cycle_Time_Cyclotron);
  }
}


int current_BarGraph_LED = 0;

void PROGRAM_BarGraph_idle_const() {  // kann für power cell kopiert werden inhaltlich
  static unsigned long cycle_Time_BarGraph_idle_const = 0;
  static int max_BarGraph_LEDs = BarG_Element_Segment.ledCount;

  if (millis() - cycle_Time_BarGraph_idle_const > PERIOD_BARGRAPH_IDLE) {
    DM13ALeds.setOneBit(BarG_Element_Segment, current_BarGraph_LED);
    current_BarGraph_LED++;
    if (current_BarGraph_LED == max_BarGraph_LEDs + 1) {
      DM13ALeds.clearSegment(BarG_Element_Segment);
      current_BarGraph_LED = 0;
    }
    DM13ALeds.needsToUpdate();
    DM13ALeds.outputRefresh_direct();
    updateProgramCallTimer(cycle_Time_BarGraph_idle_const);
  }
}

int firing_help = 0;
void PROGRAM_Firing() {
  static unsigned long cycle_Time_Firing_idle_const = 0;

  if (millis() - cycle_Time_Firing_idle_const > PERIOD_BARGRAPH_FIRING_IDLE) {
    if (firing_help <= 6) {
      DM13ALeds.clearSegment(BarG_Element_Segment);
      DM13ALeds.setOneBit(BarG_Element_Segment, firing_help);
      DM13ALeds.setOneBit(BarG_Element_Segment, 12 - firing_help);
      DM13ALeds.needsToUpdate();
      DM13ALeds.outputRefresh_direct();
    }
    firing_help = (firing_help + 1) % 7;
    updateProgramCallTimer(cycle_Time_Firing_idle_const);
  }
}



int current_PowerCell_LED = 0;
void PROGRAM_PowerCell_idle_const() {  // kann für power cell kopiert werden inhaltlich
  static unsigned long cycle_Time_PowerCell_idle_const = 0;
  static int max_PowerCell_LEDs = Power_Cell_Segment.ledCount;

  if (millis() - cycle_Time_PowerCell_idle_const > PERIOD_POWERCELL_IDLE) {
    DM13ALeds.setOneBit(Power_Cell_Segment, current_PowerCell_LED);
    current_PowerCell_LED++;
    if (current_PowerCell_LED == max_PowerCell_LEDs + 1) {
      DM13ALeds.clearSegment(Power_Cell_Segment);
      current_PowerCell_LED = 0;
    }
    DM13ALeds.needsToUpdate();
    DM13ALeds.outputRefresh_direct();
    updateProgramCallTimer(cycle_Time_PowerCell_idle_const);
  }
}


/*
  a= cubicwave8(Poti_adjusted);
  //a = sin8(Poti_adjusted);
*/

void PROGRAM_VENT_LIGHT(int value = 0, int hue = 0, int saturation = 0) {  //with setting everything to 0 -> calling PROGRAM_VENT_LIGHT() without any input sets all to 0 -> hence off
  vent_leds[0] = CHSV(hue, saturation, value);
  vent_leds[1] = CHSV(hue, saturation, value);
  vent_leds[2] = CHSV(hue, saturation, value);
  vent_leds[3] = CHSV(hue, saturation, value);
  vent_leds[4] = CHSV(hue, saturation, value);
  vent_leds[5] = CHSV(hue, saturation, value);
  vent_leds[6] = CHSV(hue, saturation, value);
}

/*
void SlowBlowLed_Boot(){
  for (int brightness = 0; brightness <= 128; brightness ++) {
    analogWrite(L_SlowBlow, brightness); // Write the current brightness to the LED pin
    delay(5000/128); // Wait for (2000ms/128) = ~15.625ms before moving to the next brightness level
  }
}
*/



const int brightnessMax = 180;  // % brightness = x%+254/100
const int interval = 3500;      // 5 seconds

void SlowBlowLed_Boot() {
  for (int i = 0; i <= brightnessMax; i++) {
    int pwmValue = pow(2, i / 32.0) - 1;  // Logarithmic scale
    analogWrite(L_SlowBlow, pwmValue);
    delay(interval / brightnessMax);
  }
}






void setup() {
  // put your setup code here, to run once:
  //Cyclotron LEDs
  FastLED.addLeds<WS2812, CYCLOTRON_LED_PIN, GRB>(cyclotron_leds, NUM_LEDS_CYCLOTRON);

  //Vent LEDs
  FastLED.addLeds<WS2812, LED_PIN, GRB>(vent_leds, NUM_LEDS_VENT);

  //Slow Blow PMW
  pinMode(L_SlowBlow, OUTPUT);


  // Buttons
  SW_WandTip.begin();
  SW_StartUp1.begin();
  SW_StartUp2.begin();
  SW_Intensify.begin();
  SW_BarGraph.begin();
  SW_Vent.begin();

  //DM13A
  DM13ALeds.begin();
  DM13ALeds.clearSegment(BarG_Element_Segment);
  DM13ALeds.clearSegment(Power_Cell_Segment);
  DM13ALeds.clearSegment(n_Segment);
  DM13ALeds.clearSegment(k_Segment);
  DM13ALeds.clearSegment(l_Segment);

  //Software|Hardware Serial Stuff
  playerSoftware0.begin();
  playerSoftware0.setVolume(10);
  playerSoftware0.pause();

  Serial.begin(9600);
}

bool selt_test = false;

SelfTestSequence WandStartUpID = coldStart;

void PROGRAM_TEST(SelfTestSequence& currentTestID) {
  static unsigned long cycle_Time_TEST = 0;

  Serial.println("Test excecuted");
  Serial.println(currentTestID);
  Serial.println(millis() - cycle_Time_TEST);
  Serial.println();


  if (currentTestID == ERROR) {
    Serial.println("Startet with ERROR");
    currentTestID = coldStart;
    delay(1000);
  }

  if (currentTestID != ERROR && (millis() - cycle_Time_TEST) > PERIOD_SELFTEST) {
    Serial.println("Startet of Time");
  }

  if (currentTestID == ERROR || (currentTestID != ERROR && (millis() - cycle_Time_TEST) > PERIOD_SELFTEST)) {
    Serial.println("Function real start");

    if (currentTestID == ERROR) {
      currentTestID = coldStart;
    }


    if (currentTestID == coldStart) {
      currentTestID = isVentPoti_off;
      Serial.print(SW_Vent.read());
      Serial.println("  ->isVentPoti_off");
    } else currentTestID = ERROR;

    if (currentTestID == isVentPoti_off) {
      Serial.println(Poti_adjusted);
      if (Poti_adjusted < 1) {
        currentTestID = isBarGraph_SW_off;
        Serial.println("  ->isBarGraph_SW_off");
      }
    } else currentTestID = ERROR;


    delay(2500);
    updateProgramCallTimer(cycle_Time_TEST);
  }
  Serial.println("Test exited !!");
}




void loop() {
  // put your main code here, to run repeatedly:

  Poti_val = analogRead(Poti_Pin);
  Poti_adjusted = constrain(Poti_val, 15, 1005);
  Poti_adjusted = map(Poti_adjusted, 14, 1005, 0, 254);


  PROGRAM_TEST(WandStartUpID);


  Serial.print(" |");
  Serial.println(Poti_adjusted);
  delay(100);


  current_Program_Run_Time = millis();
}

I'm not going to reassemble all of that, you have to post it as @alto777 indicates to get eyes on it.
First hopefully helpful thought though, if it's a BIOSesque sequence, you would probably want to do the initial stuff as a series of checks in void setup() so it only runs after starting up.
Then if you want access to certain features of that BIOS after setup has finished, you might monitor for Serial char inputs to access discrete functions and leave these functions when and only when some other condition is met.

it's not a bios sequence. it should be like. it's for a ghostbusters proton pack prop i am going to make that when turned on check if every switch is pressed in the correct order and only then turns the pack on.

So ONLY when first turned on? Then set up what you need in setup() as a series of tripwires, that is conditions that you will only enter once the one right before it has been satisfied. At least that's how I'd approach it.

What shall happen if the switching order is wrong.
Absolutely nothing?
or any kind of feedback?

a real state-machine uses the switch-case-break-statement.

The break; is the most important part !
because the break is that one detail that makes code-execution of each "case" mutually exclusive

basic principle

  switch (currentTestID) 
  
    case coldStart:
	// code of cold start
	break;
	
    case isVentPoti_off:
      if (Poti_adjusted < 1) {
        currentTestID = isBarGraph_SW_off;
        Serial.println("  ->isBarGraph_SW_off");
	  }	
	break;

    case isBarGraph_SW_off:
	// code of isBarGraph_SW_off
	break;

    case isStartUp_SW_off:
	// code of isStartUp_SW_off
	break;

    case passed:
	// code of passed
	break;

    case ERROR:
	// code of ERROR
	break;

here is a demo-code of a state-machine that includes non-blocking timing
For the non-blocking timing you need additional states

hey thanks i'll check this.

the sequence should go 0->1->2->3 ...
if one condition is not met it should go to ERROR (enum) and start from new.

i think when the functions get called again and the state is error it has to reset it to in my example "coldStart" so that it can go through the sequence checking progress again:

I cleaned up my code so that it only contains the checking stuff here:

#include <Button.h>

//DM13A classes
#include "DM13A_LedSegment.h"
#include "DM13A.h"

//Speaker
#include <SoftwareSerial.h>
#include <Arduino.h>


//Poti test
#define Poti_Pin A6
#define NUM_LEDS_VENT 28
#define LED_PIN A0
int Poti_val = 0;
int Poti_adjusted = 0;


//Buttons
Button SW_WandTip(10);
Button SW_StartUp1(8);
Button SW_StartUp2(7);
Button SW_Intensify(4);
Button SW_BarGraph(3);
Button SW_Vent(2);



//DM13A
constexpr byte LATCH_PIN = 12;  // ST_CP    // LAT   -> for LED-Shiftregisters
constexpr byte CLOCK_PIN = 11;  // SH_CP    // SCLK  -> for LED-Shiftregisters
constexpr byte DATA_PIN = 6;    // DS       // SIN   -> for LED-Shiftregisters
constexpr byte CHAINED_CHIPS = 2;
DM13A DM13ALeds(LATCH_PIN, CLOCK_PIN, DATA_PIN, (2 * CHAINED_CHIPS));


const int PERIOD_SELFTEST = 7500;




constexpr byte n_Led[][2] = { 2, 7 };
const DM13A_LedSegment n_Segment(n_Led);

constexpr byte k_Led[][2] = { 3, 1 };
const DM13A_LedSegment k_Segment(k_Led);

constexpr byte l_Led[][2] = { 3, 0 };
const DM13A_LedSegment l_Segment(l_Led);


const byte SlowBlow_Led[][2] = { 2, 0 };

enum SelfTestSequence {
  coldStart,  // sets to 0
  isVentPoti_off,
  isBarGraph_SW_off,
  isStartUp_SW_off,
  passed,
  ERROR
};



const int L_SlowBlow = 5;
unsigned long current_Program_Run_Time = 0;

void updateProgramCallTimer(unsigned long& update_program_cycle) {
  update_program_cycle = current_Program_Run_Time;
}



const int brightnessMax = 180;  // % brightness = x%+254/100
const int interval = 3500;      // 5 seconds

void SlowBlowLed_Boot() {
  for (int i = 0; i <= brightnessMax; i++) {
    int pwmValue = pow(2, i / 32.0) - 1;  // Logarithmic scale
    analogWrite(L_SlowBlow, pwmValue);
    delay(interval / brightnessMax);
  }
}




void setup() {
  // put your setup code here, to run once:

  //Slow Blow PMW
  pinMode(L_SlowBlow, OUTPUT);


  // Buttons
  SW_WandTip.begin();
  SW_StartUp1.begin();
  SW_StartUp2.begin();
  SW_Intensify.begin();
  SW_BarGraph.begin();
  SW_Vent.begin();

  //DM13A
  DM13ALeds.begin();
  DM13ALeds.clearSegment(n_Segment);
  DM13ALeds.clearSegment(k_Segment);
  DM13ALeds.clearSegment(l_Segment);

  //Software|Hardware Serial Stuff

  Serial.begin(9600);
}



SelfTestSequence WandStartUpID = coldStart;

void PROGRAM_TEST(SelfTestSequence& currentTestID) {
  static unsigned long cycle_Time_TEST = 0;

  Serial.println("Test excecuted");
  Serial.println(currentTestID);
  Serial.println(millis() - cycle_Time_TEST);
  Serial.println();


  if (currentTestID == ERROR) {
    Serial.println("Startet with ERROR");
    currentTestID = coldStart;
    delay(1000);
  }

  if (currentTestID != ERROR && (millis() - cycle_Time_TEST) > PERIOD_SELFTEST) {
    Serial.println("Startet of Time");
  }

  if (currentTestID == ERROR || (currentTestID != ERROR && (millis() - cycle_Time_TEST) > PERIOD_SELFTEST)) {
    Serial.println("Function real start");

    if (currentTestID == ERROR) {
      currentTestID = coldStart;
    }


    if (currentTestID == coldStart) {
      currentTestID = isVentPoti_off;
      Serial.print(SW_Vent.read());
      Serial.println("  ->isVentPoti_off");
    } else currentTestID = ERROR;

    if (currentTestID == isVentPoti_off) {
      Serial.println(Poti_adjusted);
      if (Poti_adjusted < 1) {
        currentTestID = isBarGraph_SW_off;
        Serial.println("  ->isBarGraph_SW_off");
      }
    } else currentTestID = ERROR;


    delay(2500);
    updateProgramCallTimer(cycle_Time_TEST);
  }
  Serial.println("Test exited !!");
}




void loop() {
  // put your main code here, to run repeatedly:

  Poti_val = analogRead(Poti_Pin);
  Poti_adjusted = constrain(Poti_val, 15, 1005);
  Poti_adjusted = map(Poti_adjusted, 14, 1005, 0, 254);


  PROGRAM_TEST(WandStartUpID);


  Serial.print(" |");
  Serial.println(Poti_adjusted);
  delay(100);


  current_Program_Run_Time = millis();
}

is there an option to do debugging on code so that i can after every line of code got excetuted what the content of the variables are?

Sure
depending on the conditions that can go wrong simply set the state-variable to "ERROR"

  switch (currentTestID) 
  
    case coldStart:
	// code of cold start
	break;
	
    case isVentPoti_off:
	  if (wrong switch flipped) {
	    currentTestID = ERROR;
	  }

      if (Poti_adjusted < 1) {
        currentTestID = isBarGraph_SW_off;
        Serial.println("  ->isBarGraph_SW_off");
	  }		  	  
	break;

    case isBarGraph_SW_off:
	  if (wrong switch flipped) {
	    currentTestID = ERROR;
	  }
	// code of isBarGraph_SW_off
	break;

    case isStartUp_SW_off:
	  if (wrong switch flipped) {
	    currentTestID = ERROR;
	  }
	// code of isStartUp_SW_off
	break;

    case passed:
	// code of passed
	break;

    case ERROR:
	// code of ERROR
	break;
1 Like

Sort of - use print statements in Serial Monitor to check what you need to check

so like this?
that should set the variable and if it is error break out and restart when the loop uses a new cycle.
if a state has been changed the next time the function is excecuted is when the time in "PERIOD_SELFTEST" has been reached.

SelfTestSequence WandStartUpID = coldStart;

void PROGRAM_TEST(SelfTestSequence& currentTestID) {
  static unsigned long cycle_Time_TEST = 0;

  Serial.println("Test excecuted");
  Serial.println(currentTestID);
  Serial.println(millis() - cycle_Time_TEST);
  Serial.println();


  if (currentTestID == ERROR) {
    Serial.println("Startet with ERROR");
    currentTestID = coldStart;
    delay(1000);
  }

  if (currentTestID != ERROR && (millis() - cycle_Time_TEST) > PERIOD_SELFTEST) {
    Serial.println("Startet of Time");
  }

  if (currentTestID == ERROR || (currentTestID != ERROR && (millis() - cycle_Time_TEST) > PERIOD_SELFTEST)) {
    Serial.println("Function real start");


    switch(currentTestID){
      case ERROR:
        currentTestID = coldStart;

      case coldStart:
        if(SW_Vent.read()){
          currentTestID = isVentPoti_off;
          Serial.print(SW_Vent.read());
          Serial.println("  ->isVentPoti_off");
        }
        break;

      case isVentPoti_off:
        Serial.println(Poti_adjusted);
        if (Poti_adjusted < 1) {
          currentTestID = isBarGraph_SW_off;
          Serial.println("  ->isBarGraph_SW_off");
        }
        break;

      case passed:
        break;

        default: currentTestID = ERROR;
    }
    delay(2500);
    updateProgramCallTimer(cycle_Time_TEST);
  }
  Serial.println("Test exited !!");
}

I do not understand what you want to say with that.
write a description with all the detail-steps that are done

Your delay(2500) is blocking
Your delay(2500) will stop all checking for 2,5 seconds.

I guess if somebody is very familiar with the start-sequence he will be able to do the steps faster than each 2,5 seconds and will be scratching his head why does it not react?

If you need timing do the timing non-blocking.

If you want more detailed help you will have to write a very very detailed description in normal words that explain what shall happen.

For other users it will be much easier to understand your code if you write down a functional description in normal words.

The trick behind state machines is that here isn't any need to "break out". They run through the checklist (or set of checklists) and act almost instantly by remembering "state" for you.

===

Because it runs though the checklist so fast and so often (1000s of times per second), this form of debugging is almost useless because it would print out so much output:

For tracking the progress of a state machine, one might periodically print out the state with a call to a function like this in the rapidly executing loop():

void report(void) {
  const uint32_t interval = 250;
  static uint32_t lastMs = 0;
  uint32_t now = millis();
  if (now - lastMs >= interval) {
    lastMs = now;
    Serial.print("State:");
    Serial.print(WandStartUpID);
    Serial.print(" ");
    Serial.print(stateNames[WandStartUpID]);
    Serial.println();
  }
}

...and perhaps print out some debugging information when the state changes, as used in this simplified version of your code from #11:

#include <Button.h> // https://github.com/madleech/Button

//DM13A classes
//#include "DM13A_LedSegment.h"
//#include "DM13A.h"

//Speaker
//#include <SoftwareSerial.h>
//#include <Arduino.h>


//Poti test
#define Poti_Pin A6
#define NUM_LEDS_VENT 28
#define LED_PIN A0
int Poti_val = 0;
int Poti_adjusted = 0;


//Buttons
Button SW_WandTip(10);
Button SW_StartUp1(8);
Button SW_StartUp2(7);
Button SW_Intensify(4);
Button SW_BarGraph(3);
Button SW_Vent(2);



//DM13A
constexpr byte LATCH_PIN = 12;  // ST_CP    // LAT   -> for LED-Shiftregisters
constexpr byte CLOCK_PIN = 11;  // SH_CP    // SCLK  -> for LED-Shiftregisters
constexpr byte DATA_PIN = 6;    // DS       // SIN   -> for LED-Shiftregisters
constexpr byte CHAINED_CHIPS = 2;
//DM13A DM13ALeds(LATCH_PIN, CLOCK_PIN, DATA_PIN, (2 * CHAINED_CHIPS));


const int PERIOD_SELFTEST = 7500;




constexpr byte n_Led[][2] = { 2, 7 };
//const DM13A_LedSegment n_Segment(n_Led);

constexpr byte k_Led[][2] = { 3, 1 };
//const DM13A_LedSegment k_Segment(k_Led);

constexpr byte l_Led[][2] = { 3, 0 };
//const DM13A_LedSegment l_Segment(l_Led);


const byte SlowBlow_Led[][2] = { 2, 0 };

enum SelfTestSequence {
  coldStart,  // sets to 0
  isVentPoti_off,
  isBarGraph_SW_off,
  isStartUp_SW_off,
  passed,
  ERROR
};

char *stateNames[6] = {
  "coldStart",
  "isVentPoti_off",
  "isBarGraph_SW_off",
  "isStartUp_SW_off",
  "passed",
  "ERROR"
};

const int L_SlowBlow = 5;
unsigned long current_Program_Run_Time = 0;

void updateProgramCallTimer(unsigned long& update_program_cycle) {
  update_program_cycle = current_Program_Run_Time;
}



const int brightnessMax = 180;  // % brightness = x%+254/100
const int interval = 3500;      // 5 seconds

void SlowBlowLed_Boot() {
  for (int i = 0; i <= brightnessMax; i++) {
    int pwmValue = pow(2, i / 32.0) - 1;  // Logarithmic scale
    analogWrite(L_SlowBlow, pwmValue);
    delay(interval / brightnessMax);
  }
}




void setup() {
  // put your setup code here, to run once:

  //Slow Blow PMW
  pinMode(L_SlowBlow, OUTPUT);


  // Buttons
  SW_WandTip.begin();
  SW_StartUp1.begin();
  SW_StartUp2.begin();
  SW_Intensify.begin();
  SW_BarGraph.begin();
  SW_Vent.begin();

  //DM13A
  //DM13ALeds.begin();
  //DM13ALeds.clearSegment(n_Segment);
  //DM13ALeds.clearSegment(k_Segment);
  //DM13ALeds.clearSegment(l_Segment);

  //Software|Hardware Serial Stuff

  Serial.begin(9600);
}



SelfTestSequence WandStartUpID = coldStart;

void PROGRAM_TEST(SelfTestSequence& currentTestID) {
  static unsigned long cycle_Time_TEST = 0;

  //Serial.println("Test excecuted");
  //Serial.println(currentTestID);
  //Serial.println(millis() - cycle_Time_TEST);
  //Serial.println();


  if (currentTestID == ERROR) {
    //Serial.println("Startet with ERROR");
    currentTestID = coldStart;
    delay(1000);
  }

  if (currentTestID != ERROR && (millis() - cycle_Time_TEST) > PERIOD_SELFTEST) {
    Serial.println("Startet of Time");
  }

  if (currentTestID == ERROR || (currentTestID != ERROR && (millis() - cycle_Time_TEST) > PERIOD_SELFTEST)) {
    Serial.println("Function real start");

    if (currentTestID == ERROR) {
      currentTestID = coldStart;
    }


    if (currentTestID == coldStart) {
      currentTestID = isVentPoti_off;
      Serial.print(SW_Vent.read());
      Serial.println("  ->isVentPoti_off");
    } else currentTestID = ERROR;

    if (currentTestID == isVentPoti_off) {
      Serial.println(Poti_adjusted);
      if (Poti_adjusted < 1) {
        currentTestID = isBarGraph_SW_off;
        Serial.println("  ->isBarGraph_SW_off");
      }
    } else currentTestID = ERROR;


    delay(2500);
    updateProgramCallTimer(cycle_Time_TEST);
  }
  //Serial.println("Test exited !!");
}




void loop() {
  // put your main code here, to run repeatedly:

  Poti_val = analogRead(Poti_Pin);
  Poti_adjusted = constrain(Poti_val, 15, 1005);
  Poti_adjusted = map(Poti_adjusted, 14, 1005, 0, 254);


  PROGRAM_TEST(WandStartUpID);


  //Serial.print(" |");
  //Serial.println(Poti_adjusted);
  delay(100);


  current_Program_Run_Time = millis();
  report();
}

void report(void) {
  const uint32_t interval = 250;
  static uint32_t lastMs = 0;
  uint32_t now = millis();
  if (now - lastMs >= interval) {
    lastMs = now;
    Serial.print("State:");
    Serial.print(WandStartUpID);
    Serial.print(" ");
    Serial.print(stateNames[WandStartUpID]);

    Serial.println();
  }
}

I hacked out the display and library I don't have, but I don't think I changed any of the logic you had in #11. The periodic report() instead of interstitial prints make it easier to track the progress through the states.

The switch-case statement, as in #15 often makes the logic between a set of mutually-exclusive states much easier to understand and manage as compared to a set of nested if-else statements.

If you do use a switch/case, or even if you don't, here's a way to control the flood of printing:

  static int lastPrintedState = -1;  // no such state
  if (currentTestID != lastPrintedState) {

    Serial.print(" state is now ");
    Serial.println(currentTestID);

    lastPrintedState = currentTestID:
  }

// then

  switch (currentTestID) 
  
    case coldStart:

You can make some nice names print if you create a parallel array of strings, viz:

char *niceName[] = {
  "name for state zero",
  "name for state one", 
  "name for state two",
  "name for state three",
};
  

and later, with the same only if the state has changed logic write

    Serial.print(" state is now ");
    Serial.println( niceName[currentTestID]);

HTH

a7

1 Like

Often in state machines, I've found the most useful output is from a function called changestate(n), where the function just changes the state to n, but also prints "state change from X to N". You'd be surprised how often those messages result in questioning 'how did I get into state X, and why are we going to N???'

State machines are wunnerful, once you get the hang of them. For a novice, those first few are hair-reducing, but the learning curve is worth it.

1 Like

hey thank to you it seems as if this code now works, even it i don't know if need the default operator in this code.
Now i have to clean it up and delete all the delays and print statements.

now my second question is: if one state has passed the check, one led should turn on and then after one second turn of and then the next state should be checked, i know i just could use the digital write, delay(1000) and digital write again, but then my code would stuck in the "state machine" btw that's what i want to build -> https://www.youtube.com/watch?v=TORrnIvXLBA

and if the switches are not pressed in the right order the proton pack should not boot up.

this is the code that seems to work correctly:

void PROGRAM_TEST(SelfTestSequence& currentTestID) {
  static unsigned long cycle_Time_TEST = 0;

  Serial.println("Test excecuted");
  Serial.println(currentTestID);
  Serial.println(millis() - cycle_Time_TEST);
  Serial.println();


  if (currentTestID == ERROR) {
    Serial.println("Startet with ERROR");
    delay(1000);
  }

  if (currentTestID != ERROR && (millis() - cycle_Time_TEST) > PERIOD_SELFTEST) {
    Serial.println("Startet of Time");
  }

  if (currentTestID == ERROR || (currentTestID != ERROR && (millis() - cycle_Time_TEST) > PERIOD_SELFTEST)) {
    Serial.println("Function real start");
    if (currentTestID == ERROR) {
      Serial.println("START ERROR");
      currentTestID = coldStart;
    }


    switch (currentTestID) {
      case ERROR:
        currentTestID = coldStart;

      case coldStart:
        if (SW_Vent.read()) {
          currentTestID = isVentPoti_off;
          Serial.print(SW_Vent.read());
          Serial.println("  ->passed: Vent_off");
          delay(1500);
        } else currentTestID = ERROR;
        break;

      case isVentPoti_off:
        Serial.println(Poti_adjusted);
        if (Poti_adjusted < 1) {
          currentTestID = isBarGraph_SW_off;
          Serial.println("  ->passed: Poti_adjusted < 1");
          delay(1500);
        } else currentTestID = ERROR;
        break;

      case isBarGraph_SW_off:
        if (SW_BarGraph.read()) {
          currentTestID = isStartUp_SW_off;
          Serial.println("->passed: SW_BarGraph_off");
          delay(1500);
        } else currentTestID = ERROR;


      case passed:
        break;

      default: currentTestID = ERROR;
    }
    updateProgramCallTimer(cycle_Time_TEST);
  }
  Serial.println("Test exited !!");
}