Why does adding an 'if' effect my state machine?

Your device does a certain sequence of steps in a state-machine each step is called a state

Your device can be in the "state" of driving, lowering the winch, waiting with lowered winch etc.

Some steps require

  • to switch on /off something to start an action

or

  • wait until an event happens where "event" can be
    a switch is closed (winchbutton is closed)
    or a certain interval of time has passed by wait with lowered winch for some time

For your device these states could be named

enum myStates {
  StartDriving, 
  driveUntilStopForLowering,
  StartLowering, 
  Lowering,
  WaitAtLowPosition,
  StartWindup,
  CheckWinchButton,
  ResumeDriving  
  };
enum myStates myStateVar;

These are names that fit best to my brain.
But every brain has different curled connections. So if you think some names should be different just write it in a posting and we can adapt the names

If you have any questions just post the questions. In the long run answering your questions will speed up walking up the learning curve

best regards Stefan

EDIT:

The forum-software does not allow more than 3 consecutive posts.
So I append the whole text to this posting:

So after a long walk with the family and the dog and dinner
I finished the code-version that uses a switch-case state-machine.
The code does compile.
Writing down such a code without any real testing means there is a good chance that still some bugs are inside this code-version.
Pretty normal situation for me.
I prefer testing the real thing (if I can) over thinking through every detail to the max.
And if the behaviour is not as expected taking a look at the code again.

The names of the functions constants and variables explain themself.
I kept some variables from you with your original names but only as comment. This shall simplify understanding the code.

I added some functions to have function-names that explain what the single lines of code do "functional"
from two lines of code like

  digitalWrite (driveMotorpin1, HIGH); 
  digitalWrite (driveMotorpin2, LOW); 

you have to lookup some comments or even the wiring or doing a real test to understand that these two lines do

void driveTowardsBumper() {
  digitalWrite (driveMotorpin1, HIGH); 
  digitalWrite (driveMotorpin2, LOW); 
  CWdriveDirection = towardsBumper;  
  walk = true;
}  

variable-names should reflect as much as possible what their purpose is
example

      drivingStarted = currentMillis; 

the name "drivingStarted" brings it to the point: it is a snapshot of time where - guess what? drivingStarted

This means reading the if-condition
if (currentMillis - drivingStarted >= driveInterval) {

in conjunction with the rest of the code

    case driveUntilStopForLowering:
      if (currentMillis - drivingStarted >= driveInterval) {
        stopDriverMotor();

is almost story telling what is happening.

code that is written in this way is very easy to maintain. You read it and understand most of it very quick.

So here is the code. I'm very curious what bugs will occur. I haven't thought about special situations yet. This is best done after some first testing.

//Short internal battery loop follows:
long readVcc() {
  long result;
  // Read 1.1V reference against AVcc.  Charge threshold: 2850 (3.5v) - 3000 (3.9v)
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA, ADSC));
  result = ADCL;
  result |= ADCH << 8;
  result = 1126400L / result; // Back-calculate AVcc in mV
  return result;
}
// end of internal battery setup code


int BumpButtonPin = 3;     // the number of the pushbutton pin
int ChargeButtonPin = 4;     // the number of the pushbutton pin
int MiniWinchPin = 12;  //number of the winch pin button
int eyes = 9;
int driveMotorpin1 = 6;
int driveMotorpin2 = 7;
int winchMotorpin1 = 10;
int winchMotorpin2 = 11;

byte CWdriveDirection;
const byte towardsBumper  = 0;
const byte towardsCharger = 1;

enum myStates {
  StartDriving, 
  driveUntilStopForLowering,
  StartLowering, 
  Lowering,
  WaitAtLowPosition,
  StartWindup,
  CheckWinchButton,
  HangOutAtTop,
  ResumeDriving  
  };
enum myStates myStateVar;

// variables will change:
boolean walk = false; //variable to pause the walk routine

int eyebright = 0; //how bright the eyes are
int eyefade = 5;
int battVolts;   // made global for wider avaliblity throughout a sketch if needed, example for a low voltage alarm, etc
// value is volts X 100, 5 vdc = 500

unsigned long currentMillis = 0;


//unsigned long previousMillisT = 0; //stores last time time interval was updated.
unsigned long drivingStarted  = 0;

//unsigned long previousMillisD = 0; //stores last time drop interval was updated.
unsigned long loweringStarted = 0;

//unsigned long previousMillisW = 0; //stores last time bottom dwell interval was updated.
unsigned long waitLowStarted  = 0;

//unsigned long previousMillisTW = 0; //stores last time  top dwell interval was updated.
unsigned long HangOutStarted = 0;

//unsigned long intervalT = 8000; //interval for walk stop & climbdown routine init
unsigned long driveInterval = 8000;

//unsigned long intervalD = 3000; // period for climbing down
unsigned long climbDownInterval = 3000;

//unsigned long dwellD = 2000; // dwell to hang out at bottom
unsigned long dwellDownInterval = 2000;

//unsigned long dwellT = 1500; //hang out at top before proceeding
unsigned long HangOutInterval = 1500;

void stopDriverMotor() {
  digitalWrite (driveMotorpin1, LOW); 
  digitalWrite (driveMotorpin2, LOW); 
  walk = false;    
}

void driveTowardsBumper() {
  digitalWrite (driveMotorpin1, HIGH); 
  digitalWrite (driveMotorpin2, LOW); 
  CWdriveDirection = towardsBumper;  
  walk = true;
}  

void driveTowardsCharger() {
  digitalWrite (driveMotorpin1, LOW); 
  digitalWrite (driveMotorpin2, HIGH);   
  CWdriveDirection = towardsCharger;  
  walk = true;
}  

void stopWinchMotor() {
  digitalWrite (winchMotorpin1, LOW);
  digitalWrite (winchMotorpin2, HIGH);  
}
  
void climbDown() {
  digitalWrite (winchMotorpin1, LOW);
  digitalWrite (winchMotorpin2, HIGH);  
}

void climbUp() {
  digitalWrite (winchMotorpin1, HIGH);
  digitalWrite (winchMotorpin2, LOW);  
}

void myStepChain() {
  currentMillis = millis();

  if (walk && BumpButtonPin == HIGH) {
    driveTowardsCharger();
  }

  if (walk && ChargeButtonPin == HIGH) {
    driveTowardsCharger();
  }
      
  switch (myStateVar) {

    case StartDriving:
      if (ChargeButtonPin == HIGH) {
        driveTowardsBumper();
      }
      else {
        driveTowardsCharger();
      }
      drivingStarted = currentMillis; 
      myStateVar = driveUntilStopForLowering;      
      break; // break; is nescessary to make the execution mutually exclusive


    case driveUntilStopForLowering:
      if (currentMillis - drivingStarted >= driveInterval) {
        stopDriverMotor();
        myStateVar = StartLowering;         
      }
      break; 


    case StartLowering: 
      climbDown(); 
      loweringStarted = currentMillis;
      myStateVar = Lowering;         
      break;


    case Lowering:
      if (currentMillis - loweringStarted >= climbDownInterval) {
        waitLowStarted = currentMillis;
        myStateVar = WaitAtLowPosition;          
      }
      break;


     case WaitAtLowPosition:
       stopWinchMotor();
       if (currentMillis - waitLowStarted >= dwellDownInterval) {
         myStateVar =StartWindup;
       }
       break;


     case StartWindup:
       climbUp();
       myStateVar = CheckWinchButton;       
       break;


     case CheckWinchButton:
       if (MiniWinchPin == HIGH) {
         stopWinchMotor();
         HangOutStarted = currentMillis;          
         myStateVar = HangOutAtTop;
       }
       break;


     case HangOutAtTop:
       if (currentMillis - HangOutStarted >= HangOutInterval) {
         myStateVar = ResumeDriving;
       }
       break;

     
     case ResumeDriving:
       if (CWdriveDirection == towardsBumper) {
         driveTowardsBumper(); 
       }

       if (CWdriveDirection == towardsCharger) {
         driveTowardsCharger(); 
       }
       drivingStarted = currentMillis;
       myStateVar = driveUntilStopForLowering;
       break;
  } // switch       
}


void setup() {
  Serial.begin(115200);
  // initialize the pushbutton pins as inputs:
  pinMode (BumpButtonPin, INPUT);
  pinMode (ChargeButtonPin, INPUT);
  pinMode (MiniWinchPin, INPUT);
  pinMode (driveMotorpin1, OUTPUT);
  pinMode (driveMotorpin2, OUTPUT);
  pinMode (winchMotorpin1, OUTPUT);
  pinMode (winchMotorpin2, OUTPUT);
  Serial.print("volts X 100");
  Serial.println( "\r\n\r\n" );

  while (digitalRead (BumpButtonPin) == LOW) {
    stopDriverMotor();     
    stopWinchMotor();   
  };
  //pause everything and wait for bump button to initialize sequence.
  driveTowardsBumper(); //initiate movement away from charger
  walk = true;
}

void loop() {
  myStepChain();
}

best regards Stefan

1 Like