Sliding game opener project

I've gotten the handoffs between open and closed working pretty well (I think) using the switch statement on Version 2. Gate Opener v2 - Wokwi ESP32, STM32, Arduino Simulator

But it is not checking the proximity sensors while the motor is moving. Do you have any suggestions on what to do for this issue?

Thank you.

Wise ass answer, check the proximity switches while the motor is moving…

Srsly, though, this work in progress needs to be posted here as a it works we can compile and run it ourselves sketch.

In your next post.

Especially because you are changing over to a better programming model.It is not clear how far you've gotten with that and whether your code is still having some things in it that make adding or fixing features harder.

You might have a state wherein the motors are moving, pending the arrival of inputs that would make it stop (limit switches). That would be the logical,place to check the inputs you read from the proximity switches in the input phase of you IPO.

I'm in transit, then at the beach. I can look but perhaps not be able to help right away.

a7

The best kind :wink:


/*

  Program reqs:

  When signal to operate is received detmine if gate is opened or closed.
  Can receive signal from keyPin or phone.
  If open close the gate.
  Operate the PWM motor at 50% until the close hall effect sensor gets signal.
  If closed open the gate.
  Operate the PWM motor at 50% until the open hall effect sensor gets signal.
  If neither open or close close the gate.
  Check the close proximity sensor while closing, if object detected go back to open position.
  Check the open proximity sensor while opening, if object detected go back to close position.
  Automatically close the gate after open for a set ammount of time

*/

// Define PINs for gate control, proximity sensors, and motor control
const int gateOpenPin = 10;         // GPIO2 for gate open
const int gateClosePin = 3;        // GPIO3 for gate close
const int openProximityPin = 19;    // GPIO4 for open proximity sensor
const int closeProximityPin = 2;   // GPIO5 for close proximity sensor
const uint8_t motorControlPinR = 7;    // GPIO6 for right motor control (PWM)
const uint8_t motorControlPinL = 6;    // GPIO7 for left motor control (PWM)
const int keyPadPin = 9;           // GPIO8 for keypad signal

//Varibales for checking pin states
int gateOpen;
int gateClose;
int openProximity;
int closeProximity;
uint8_t motorControlR;
uint8_t motorControlL;
int keyPad;
int prevKeyPad;

// Variables used for motor speed control
const int maxMotorSpeed = 128;        // 50% PWM speed (0-255)
int currentMotorSpeed = 0;
int openChannel = 1;
int closeChannel = 2;

// Variables used for timing
const unsigned long gateOperationDuration = 45000; // 45 seconds
unsigned long gateOperationStartTime = 0;

//Variables for gate states
enum gateStates {
  OPEN,
  CLOSED,
  OPENING,
  CLOSING,
  JAMMED_OPENING,
  JAMMED_CLOSING,
  UNKNOWN
}state;

#define IDNAME(name) #name
const char* stateNames[] = {
  IDNAME(OPEN), 
  IDNAME(CLOSED),
  IDNAME(OPENING), 
  IDNAME(CLOSING), 
  IDNAME(JAMMED_OPENING), 
  IDNAME(JAMMED_CLOSING), 
  IDNAME(UNKNOWN)
};

enum gateStates gateState;
enum gateStates prevGateState;

void setup() {
  // Initialize serial communication
  Serial.begin(115200);

  // Initialize pins
  pinMode(gateOpenPin, INPUT_PULLUP);
  pinMode(gateClosePin, INPUT_PULLUP);
  pinMode(openProximityPin, INPUT_PULLUP);
  pinMode(closeProximityPin, INPUT_PULLUP);
  pinMode(keyPadPin, INPUT_PULLUP);
  ledcAttachPin(motorControlPinL, closeChannel);
  ledcAttachPin(motorControlPinR, openChannel);
  ledcSetup(closeChannel, 1000, 8);
  ledcSetup(openChannel, 1000, 8);
  ledcWrite(closeChannel, 0);

  //Get the current state of the various sensors
  keyPad = digitalRead( keyPadPin );
  gateOpen = digitalRead( gateOpenPin );
  gateClose = digitalRead( gateClosePin );
  openProximity = digitalRead( openProximityPin );
  closeProximity = digitalRead( closeProximityPin );

  // determine the initial gate position
  if ( gateOpen == LOW && gateClose == LOW ) {
    Serial.println(F("ERROR - both limit switches activated"));
    while (1);
  }
  else if ( gateOpen == HIGH && gateClose == HIGH ) {
    prevGateState = gateState;
    gateState = UNKNOWN;
    Serial.println(F("GATE POSITION UNKNOWN"));
  }
  else if ( gateOpen == LOW ) {
    Serial.println(F("GATE OPEN"));
    prevGateState = gateState;
    gateState = OPEN;
  }
  else if ( openProximity == LOW ) {
    Serial.println(F("GATE BLOCKED WHILE OPENING"));
    prevGateState = gateState;
    gateState = JAMMED_OPENING;
  }
  else if ( closeProximity == LOW ) {
    Serial.println(F("GATE BLOCKED WHILE OPENING"));
    prevGateState = gateState;
    gateState = JAMMED_CLOSING;
  }
  else {
    Serial.println(F("GATE CLOSED"));
    prevGateState = gateState;
    gateState = CLOSED;
  }
}

void loop() {
  // Check for keypad input{
  Serial.print("Open pin state: ");
  Serial.println(digitalRead(gateOpenPin));
  Serial.print("Close pin state: ");
  Serial.println(digitalRead(gateClosePin));
  Serial.print("Keypad pin state: ");
  Serial.println(digitalRead(keyPadPin));
  Serial.print("Open proximity pin state: ");
  Serial.println(digitalRead(openProximityPin));
  Serial.print("Close proximity pin state: ");
  Serial.println(digitalRead(closeProximityPin));
  Serial.print("State of the gate: ");
  Serial.println(stateNames[gateState]);
  delay(500);
  getSignal();
}

void getSignal() {
  keyPad = digitalRead(keyPadPin);
  if (keyPad == 0) {
    gateCommand();
  }
}

void gateCommand() {
  switch ( gateState ) {

    case UNKNOWN:
      gateOpen = digitalRead( gateOpenPin );
      gateClose = digitalRead( gateClosePin );
      if ( gateOpen == LOW ) {
        
        Serial.println(F("GATE OPEN"));
        prevGateState = gateState;
        gateState = OPEN;
      }
      else if ( gateClose == LOW ) {        
        Serial.println(F("GATE CLOSED"));
        prevGateState = gateState;
        gateState = CLOSED;
      }
      break;

    case CLOSED:
      gateClose = digitalRead( gateClosePin );
      if ( gateClose == LOW ) {
        prevGateState = gateState;
        gateState = OPENING;
        Serial.println(F("GATE OPENING"));
        motorControlO();
      }
      break;

    case OPENING:
      gateOpen = digitalRead( gateOpenPin );
      openProximity = digitalRead( openProximityPin );
      if ( gateOpen == LOW ) {        
        Serial.println(F("GATE OPEN"));
        prevGateState = gateState;
        gateState = OPEN;
      }
      else if (openProximity == LOW) {
        prevGateState = gateState;
        gateState = JAMMED_OPENING;
      }
      break;

    case JAMMED_OPENING:
      openProximity = digitalRead( openProximityPin );
      if ( openProximity == LOW ) {
        Serial.println(F("OBJECT FOUND WHILE OPENING"));
        prevGateState = gateState;
        gateState = CLOSING;
        motorControlC();
      }
      break;

    case OPEN:
      gateOpen = digitalRead( gateOpenPin );
      if ( gateOpen == LOW ) {
        prevGateState = gateState;
        gateState = CLOSING;
        Serial.println(F("GATE CLOSING"));
        motorControlC();
      }
      break;

    case CLOSING:
      gateClose = digitalRead( gateClosePin );
      closeProximity = digitalRead( closeProximityPin );
      if ( gateClose == LOW ) {
        Serial.println(F("GATE CLOSED"));
        prevGateState = gateState;
        gateState = CLOSED;
      }
      else if (closeProximity == LOW) {
        prevGateState = gateState;
        gateState = JAMMED_CLOSING;
      }
      break;

    case JAMMED_CLOSING:
      closeProximity = digitalRead( closeProximityPin );
      if ( closeProximity == LOW ) {
        Serial.println(F("OBJECT FOUND WHILE CLOSING"));
        prevGateState = gateState;
        gateState = OPENING;
        motorControlO();
      }
      break;
  }
}

void motorControlC() {
  Serial.println("Turn LEDs on.");
  // If your RGB LED turns off instead of on here you should check if the LED is common anode or cathode.
  // If it doesn't fully turn off and is common anode try using 256.
  if (digitalRead(gateOpenPin) == 0) {
    ledcWrite(openChannel, 0);
    ledcWrite(openChannel, maxMotorSpeed);
    Serial.println("Gate Closing");
    prevGateState = gateState;
    gateState = CLOSING;
    Serial.print("State of the gate: ");
    Serial.println(stateNames[gateState]);
    delay(5000);
    Serial.println("Gate Closed");
    prevGateState = gateState;
    gateState = CLOSED;
    Serial.print("State of the gate: ");
    Serial.println(stateNames[gateState]);
  }
  else {
    ledcWrite(openChannel, 255);
    ledcWrite(closeChannel, 255);
  }
  Serial.println("Turn LEDs off.");
  ledcWrite(openChannel, 0);
  ledcWrite(closeChannel, 0);
  delay(1000);
}

void motorControlO() {
  Serial.println("Turn LEDs on.");
  // If your RGB LED turns off instead of on here you should check if the LED is common anode or cathode.
  // If it doesn't fully turn off and is common anode try using 256.
  if (digitalRead(gateClosePin) == 0) {
    ledcWrite(closeChannel, 0);
    ledcWrite(closeChannel, maxMotorSpeed);
    Serial.println("Gate Opening");
    delay(5000);
    Serial.println("Gate Open");
    prevGateState = gateState;
    gateState = OPEN;
    Serial.print("State of the gate: ");
    Serial.println(stateNames[gateState]);
  }
  else {
    ledcWrite(openChannel, 255);
    ledcWrite(closeChannel, 255);
  }
  Serial.println("Turn LEDs off.");
  ledcWrite(openChannel, 0);
  ledcWrite(closeChannel, 0);
  delay(1000);
}

This may be the issue, I'm currently using an if statement with a delay to simulate the transit time.

This gives me an idea, I'll see if I can get a full thought out of it.

Have fun at the beach.

OK, a glance through the tiny window.

It may help you focus your efforts to search for and justify or eliminate all uses of delay().

Most of my sketches make zero use of delay(). And those that do use it find all that done and over with at the exit of setup(). 99.44 percent.

Also, I did not look closely, but a sketch of this type needs and should use only one call to millis().

Because everything that gets done in the rest of the loop should be referencing the same time stamp.

I use a global variable now and set it to millis() as the first step in the loop() function. Ppl like to make it more impressive sounding with names like currentMillis. Whatever.

Lastly, I see sigitalRead() sprinkled all over your code… the IPO model would rather you read all inputs into variables, again this is something an IPO model loop would do as the very next things in the loop.

It just makes things easier if you are adjusting and playing with how the sensors are read and so forth.

JAMMED, or whatever, isn't the state that detects a jam. It is the state that is entered into,when another state CLOSING for example, sees from the proximity sensor that there is a jam.

JAMMED is where you take steps to handle the fact that the door is in a JAMMED state,

a7

I haven't added the timer yet. for the most part the delays are temporary so I can read what's going on in the loop.

So you're saying I can just read the sensors once per cycle and store the values, rather than rereading each time I need the value...

I believe I'm checking for objects and setting the JAMMED state in the OPENING and CLOSING case sections.

Currently in the JAMMED sections I am reversing the gate.

Oooo, I was using 4 lines of code with 1 word different for tracking the different state changes. I created a function that I could pass the 1 word difference in to and cleaned up the code a lot. First time ever creating a function with a variable passed to it.

OK, that's the trouble with the tiny window. I saw

    case JAMMED_OPENING:
      openProximity = digitalRead( openProximityPin );
      if ( openProximity == LOW ) {

and misunderstood. If you think about it (I see) the only way to get into JAMMED_OPENING is if the proximity switch caused it to in state OPENING. Therefore checking that again, no matter if you digitalRead() the pin or use a variable, is redundant. You immediately exit the state for CLOSING, so it's not like you need to refresh.

In fact what would mean finding the proximity switch no longer complaining? You'd be stuck in that state… until?

I believe a similar point could be raised for JAMMED_CLOSING.

It may not matter, but a good thing to do is to ferret out things like this, which can unnecessarily increase the amount of code. Code your readers must read, code you yourself might wonder one day. I was mislead, thinking you had some reason to check the switch and came to a bad conclusion. The more the code makes sense through the tiny window (!) the better you are doing.

You have made good work with this, I dare to say before I have taken a low level flight about the latest.

You may like to relax with another thread where the idea of a state diagram is brought out and explored:

Post #12 has a diagram and shows how the state bubbles work and how the transistions are labeled.

As for functions, nice! Please post your latest working version when you feel it is ready, that is what I will spend any time I have. Beach yes, and just now up to my elbows in paint doing some work on the crib. A different kind of fun.

a7

I think I've gotten the motor controls working properly. Here is a link to my sketch: Gate Opener v3 - Wokwi ESP32, STM32, Arduino Simulator.


/*

  Program reqs:

  When signal to operate is received detmine if gate is opened or closed.
  Can receive signal from keyPin or phone.
  If open close the gate.
  Operate the PWM motor at 50% until the close hall effect sensor gets signal.
  If closed open the gate.
  Operate the PWM motor at 50% until the open hall effect sensor gets signal.
  If neither open or close close the gate.
  Check the close proximity sensor while closing, if object detected go back to open position.
  Check the open proximity sensor while opening, if object detected go back to close position.
  Automatically close the gate after open for a set ammount of time

*/

// Define PINs for gate control, proximity sensors, and motor control
const int gateOpenPin = 10;         // GPIO2 for gate open
const int gateClosePin = 3;        // GPIO3 for gate close
const int openProximityPin = 19;    // GPIO4 for open proximity sensor
const int closeProximityPin = 2;   // GPIO5 for close proximity sensor
const uint8_t motorControlPinR = 7;    // GPIO6 for right motor control (PWM)
const uint8_t motorControlPinL = 6;    // GPIO7 for left motor control (PWM)
const int keyPadPin = 9;           // GPIO8 for keypad signal

//Varibales for checking pin states
int gateOpen;
int gateClose;
int openProximity;
int closeProximity;
uint8_t motorControlR;
uint8_t motorControlL;
int keyPad;
int prevKeyPad;

// Variables used for motor speed control
const int maxMotorSpeed = 125; 
const int minMotorSpeed = 25;       
int currentMotorSpeed = 25;
int openChannel = 1;
int closeChannel = 2;
const int speedStep = 25;

// Variables used for timing
const unsigned long gateOpenDuration = 5000; // 45 seconds
unsigned long gateOpenStartTime = 0;

//Variables for gate states
enum gateStates {
  OPEN,
  CLOSED,
  OPENING,
  CLOSING,
  JAMMED_OPENING,
  JAMMED_CLOSING,
  UNKNOWN
}state;

#define IDNAME(name) #name
const char* stateNames[] = {
  IDNAME(OPEN), 
  IDNAME(CLOSED),
  IDNAME(OPENING), 
  IDNAME(CLOSING), 
  IDNAME(JAMMED_OPENING), 
  IDNAME(JAMMED_CLOSING), 
  IDNAME(UNKNOWN)
};

enum gateStates gateState;
enum gateStates prevGateState;

void setup() {
  // Initialize serial communication
  Serial.begin(115200);

  // Initialize pins
  pinMode(gateOpenPin, INPUT_PULLUP);
  pinMode(gateClosePin, INPUT_PULLUP);
  pinMode(openProximityPin, INPUT_PULLUP);
  pinMode(closeProximityPin, INPUT_PULLUP);
  pinMode(keyPadPin, INPUT_PULLUP);
  ledcAttachPin(motorControlPinL, closeChannel);
  ledcAttachPin(motorControlPinR, openChannel);
  ledcSetup(closeChannel, 1000, 8);
  ledcSetup(openChannel, 1000, 8);
  ledcWrite(closeChannel, 0);

  //Get the current state of the various sensors
  keyPad = digitalRead( keyPadPin );
  gateOpen = digitalRead( gateOpenPin );
  gateClose = digitalRead( gateClosePin );
  openProximity = digitalRead( openProximityPin );
  closeProximity = digitalRead( closeProximityPin );

  // determine the initial gate position
  if ( gateOpen == LOW && gateClose == LOW ) {
    Serial.println(F("ERROR - both limit switches activated"));
    while (1);
  }
  else if ( gateOpen == HIGH && gateClose == HIGH ) {
    prevGateState = gateState;
    gateState = UNKNOWN;
    Serial.println(F("GATE POSITION UNKNOWN"));
  }
  else if ( gateOpen == LOW ) {
    Serial.println(F("GATE OPEN"));
    prevGateState = gateState;
    stateChange(OPEN);
  }
  else if ( openProximity == LOW ) {
    Serial.println(F("GATE BLOCKED WHILE OPENING"));
    prevGateState = gateState;
    stateChange(JAMMED_OPENING);
  }
  else if ( closeProximity == LOW ) {
    Serial.println(F("GATE BLOCKED WHILE OPENING"));
    prevGateState = gateState;
    stateChange(JAMMED_CLOSING);
  }
  else {
    Serial.println(F("GATE CLOSED"));
    prevGateState = gateState;
    stateChange(CLOSED);
  }
}

void loop() {
  //Check the sensors
  gateOpen = digitalRead( gateOpenPin );
  gateClose = digitalRead( gateClosePin );
  openProximity = digitalRead( openProximityPin );
  closeProximity = digitalRead( closeProximityPin );
  keyPad = digitalRead(keyPadPin);

  Serial.print("Open pin state: ");
  Serial.println(gateOpen);
  Serial.print("Close pin state: ");
  Serial.println(gateClose);
  Serial.print("Keypad pin state: ");
  Serial.println(digitalRead(keyPadPin));
  Serial.print("Open proximity pin state: ");
  Serial.println(openProximity);
  Serial.print("Close proximity pin state: ");
  Serial.println(closeProximity);
  Serial.print("State of the gate: ");
  Serial.println(stateNames[gateState]);
  
  switch ( gateState ) {
    case UNKNOWN:
      if ( gateOpen == LOW ) {        
        Serial.println(F("GATE OPEN"));
        stateChange(OPEN);
      }
      else if ( gateClose == LOW ) {        
        Serial.println(F("GATE CLOSED"));
        stateChange(CLOSED);
      }
      break;

    case CLOSED:
      if ( gateClose == LOW && keyPad == LOW) {
        stateChange(OPENING);
        Serial.println(F("GATE OPENING"));
        motorControl(closeChannel, 0);
        motorControl(openChannel,minMotorSpeed);
      }
      break;

    case OPENING:
      if (gateOpen == LOW) {
        motorControl(openChannel, 0);
        stateChange(OPEN);
      }
      else {
          if(openProximity == LOW) {
            stateChange(JAMMED_OPENING);
          }   
          else if (currentMotorSpeed < maxMotorSpeed) {              
              //currentMotorSpeed += speedStep;
              currentMotorSpeed++;
              Serial.println(currentMotorSpeed);
              Serial.println("OPENING");
              motorControl(openChannel, currentMotorSpeed);
          }
          else {
              motorControl(openChannel, currentMotorSpeed);
              Serial.println(currentMotorSpeed);
              Serial.println("OPENING");
          }
        }    
      break;

    case JAMMED_OPENING:
      Serial.println(F("OBJECT FOUND WHILE OPENING"));
      currentMotorSpeed = minMotorSpeed;
      stateChange(CLOSING);
      motorControl(openChannel, 0);
      motorControl(closeChannel,currentMotorSpeed);
      break;

    case OPEN:
      if ( gateOpen == LOW ) {
        motorControl(openChannel, 0);
        if (prevGateState == OPENING) delay(gateOpenDuration);
        currentMotorSpeed = minMotorSpeed;
        stateChange(CLOSING);
        Serial.println(F("GATE CLOSING"));
        motorControl(closeChannel,currentMotorSpeed);
        
      }
      break;

    case CLOSING:
      if (gateClose == LOW) {
        currentMotorSpeed = 0;
        motorControl(closeChannel, currentMotorSpeed);
        motorControl(openChannel, currentMotorSpeed);
        stateChange(CLOSED);
      }
      else {
          if(closeProximity == LOW) {
            stateChange(JAMMED_CLOSING);
          }   
          else if (currentMotorSpeed < maxMotorSpeed) {
              //currentMotorSpeed += speedStep;
              currentMotorSpeed++;
              Serial.println(currentMotorSpeed);
              Serial.println("CLOSING");
              motorControl(closeChannel, currentMotorSpeed);
          }
          else {
              motorControl(closeChannel, currentMotorSpeed);
              Serial.println(currentMotorSpeed);
              Serial.println("CLOSING");
          }
        }    
      break;

    case JAMMED_CLOSING:
      Serial.println(F("OBJECT FOUND WHILE CLOSING"));
      currentMotorSpeed = minMotorSpeed;
      stateChange(OPENING);
      motorControl(closeChannel, 0);
      motorControl(openChannel,currentMotorSpeed);     
      break;
  }
}

void stateChange(enum gateStates x){
  prevGateState = gateState;
  gateState = x;
  Serial.print("State of the gate: ");
  Serial.println(stateNames[gateState]);
}

void motorControl(int direction, int speed) {
  if (direction == openChannel) {
    Serial.println("New");
    ledcWrite(direction, speed);
    //delay(1000);
  }

  if (direction == closeChannel) {
    Serial.println("New");
    ledcWrite(direction, speed);
    //delay(1000);
  }

  ledcWrite(direction, speed);
  if(direction == 1)Serial.println("Open");
  else Serial.println("Closed");
  Serial.println(speed);
  //delay(1000);
}

I wasn't able to get a timer using millis() to work for the time to remain open, so I put in a delay. I currently have the delay set for 5 seconds.

Now I think I need to figure out how to send a command from the phone.

I completely missed the "stays open for awhile" requirement. That's what I get for never reading comments. :expressionless:

You have chosen to use a delay, and I assume that works, as I will assume does the rest of the sketch to your satisfaction.

If I were to add to the version below that functionality, it would be with an additional state or maybe two, WAITING_OPEN or something like that, and use millis() to leave that state for CLOSING.

The problem with delay() is that for that long (45 seconds), the sketch will be blind to, say, closing sooner because you want to, or taking a command to stay open and so forth. The delay would also mena nothing else could be stood up beside the control code. This may never matter, but it is fun to end up with a non-blovking sketch that uses maybe ten percent of the resources available, leaving the same microprocessor available to walk and chew some gum on the side.

I coded the blockages to leave the gate motionless until the blockage was removed, at which point I drive the gate the opposite way. See those states, and how it would be clear and simple to just head for the other direction as was your design choice.

I had no need for prevGateState, and handled the printing in a different manner. No large deals. There are a few tricks that are just to cut down the spam. In a real deployment, you might really want to keep turning on motors or LEDs or whatever that were already. On.

I suggest your scrutinize you code and removal anything that no longer is used or contributes to the sketch. In the version I hacked at, there were a number of orpans, again the point is to use the least amount of ink. If a variable is declared and never used, or such use as it is out to is redundant or unnecessary it should be cut.


Wokwi_badge Umbrella Academy Version


You might take time to read and play with this version. Or not. I think you are going to be able to get into all your own kind of trouble from here on, and I wish you good luck doing!

a7

Thanks I'll look at yours. Cleaner more extensible code is always good. There's always the what if factor to take into account.

One thing I think I want to try to do is have a command from the phone that will open the gate and leave it open, because there may be times where not have it auto close can be useful.

I've just been through the setup() logic and changed the last sketch on the wokwi. Why I did not publish it here, a work in progress.

The logic only needs to see

both limit switches - > error
neither limit switch -> UNKNOWN

and now we know it is either open or closed, amIRight?

By making the initial gateState set the gate in motion, i.e. enter OPENING or CLOSING, we get an automatic transition to the resting states. And here, by simply swapping the order of the tests that make a state transition, we can get a free check of the proximity sensors.

Now there's still two copies of the input reading, I hadn't even noticed it in setup(). Time for a function that sets the global variables; there are worse thing in a tiny program.

I don't like changing the state vriable outside the FSM, but it is convenient, again something that works better in a small sketch.

I changed the button to just set a flag and the FSM handles starting the gate moving if that is appropriate, otherwise it is not handled and says so.

In small sketch, all the ways of doing this amount to the same approximate pain. In a larger sketch, some formal way of "influencing" an FSM would best be figured out. There are a few ways, all have their adherents. I'm not so dogmatic about global variables in a tiny program like this. It is often the clearest expression of what the intent is. Yes IMO YMMV &c.

a7

By changing the pushbutton state change detection logic to set a flag, rather than do one thing or another, we have the ability to use that flag in every state to do something sensible.

For example, if the gate is neither open nor closed, we might just wait until the button is pressed to open it, viz:

  case UNKNOWN:
    if (keyEvent) {
      gateState = OPENING;

      keyEvent = false;  // handled
    }

    break;

It makes adding a remote equivalent to a button press simple, too.

Pseudecode:

setup
...
  initiailise remote communication

loop
...
  see if we have a signal and
    set the keyEvent flag

Step by step.

a7

Yeah I added it when checking the state of the sensors, probably not needed since it's not a sensor.

This makes sense, the FSM gets inputs then decides what to do with them, so all the logic is in one place.

So with this setup each state the gate is in could have a different effect if the button is pressed, like if it's opening or closing pressing the button could switch the direction, or if it's open it could close now rather than wait for the timer, if I can get the timer to work?

Going off the various timers I've found online I came up with this:

void timer()
{
  unsigned long currentMillis = millis();
  Serial.println("Waiting to close");
  Serial.println(currentMillis);
  Serial.println(startMillis);
  Serial.println(currentMillis - startMillis);
  if (currentMillis - startMillis >= gateOpenDuration) {
    startMillis = currentMillis;
    gateState = CLOSING;
  }
}

I added the println stuff since it wasn't behaving how I expected. I have startMillis =millis(); in setup and on the first run it works, but the on subsequent runs the difference is too big so it closes immediately. I f I put startMillis anywhere in the loop currentMillis and startMillis are equal. I've also tried moving startMillis = currentMillis; around and not getting anything useful. IDK maybe the ESP32 does timing differently...

Yes, exact. I can think of many things that a button press might mean in the various states. For example, a button press whilst opning or when open and waiting out the time to automatically close might mean "stay open".


I would need to see that in context. Do you have a wokwi simulation where you could keep a current work in progress, even when it isn't working?

It would be better to separate the meddling with the state variable away from the function.

It is odd to restart the timer when it expires - the timer should be reset when the gate becomes open, like in OPENING just as you set it to transition to OPEN.

So show us your timer() function in the context of a complete sketch even if it is not doing what you tinhk it should. Yet.

And TBH, the timer logic is so idiomatic and simple, it may not call for a separate function, especially one what handles only one timer... you may find you want to time other things as you refine your specifications.

a7

I was playing around with adding a OPEN_WAITING state in you FSM program. I've added the timer logic to the new state instead of a separate function.

I've moved the restart around a bit, and haven't found a spot that works for it.

I took a look. You grabbed my version before some changes unrelated. That's fine, but the revised setup() function you might want to look at and grab for yourself.

Also I redid the wiring so that those annoying slide switches would be in the open position when you load the web page. Otherwise you have to run the sketch, set those switches and then restart the sketch. I just put the wire on the other pin on the switch where necesssary. You could look and see that and grab my digram.json for yourself as well. Or re-wire your own.

Now the timer for staying open has a flaw that was not too hard to spot. It is easily solved.

At the top of your loop, make the one and only call to millis() to set the global currentMillis. Then, the two states involved in leaving the gate up for a period would read

  case OPEN:
    motors = OFF;
    startMillis = currentMillis;
    gateState = WATING_OPEN;

    break;

  case WATING_OPEN:
    if (currentMillis - startMillis >= gateOpenDuration) {
      gateState = CLOSING;
    }
 
    break;

You were not getting a meaningful value when you assigned to *startMillis*, you may have meant to set it to *millis*() , which would have worked, but following the "one time for all" is my always recommendation.

Now what should the button do in WAITING_OPEN? Opinion here is split between having it mean "close right now" and having it mean "just stay open".

a7

LoL, I was following this guide: Using millis() for timing. A beginners guide . You're description makes total sense, and explains why I was seeing not at all what I was expecting.

I'll have to look at the rest later, don't have much time this morning.

Thanks.

I think I'd have it mean close now, I think I only want the stay open a command from the phone.

Because I have no life. Because I could. Because it was cold and rainy this day:

Wokwi_badge Umbrella Academy Version w/ doorDoor Simulator


It needs the motors to be 255 fully on.

a7

Very nice, merging with the doordoor, so it's all automated like the gate should actually work with actual sensors.

For blue tooth I have decided to use dabble, the phone app is the same on both iPhone and Android, and it's simple. I think it won't be too difficult to adapt the light control signal to work for opening and closing the gate.

I am working on adapting the DoorDoor adaption to working with a second button to emulate the bluetooth signal. I've gotten so that if the blue button is pressed again it'll stop the gate, but it doesn't restart with a third press.

Here's a link to my copy of DoorDoor: a7DoorDoor2 Copy - Wokwi ESP32, STM32, Arduino Simulator

Here are the sections of code I've changed, not including the variable delcarations and stuff since I don't think that is what's causing the problem, pretty sure it's one of the if statements.

// PROCESS - keypad
  if (keyEvent || blueEvent) {
    if (keyEvent)
        Serial.println("         ignored red button!");
    
    if (blueEvent){
        Serial.println("Blue signal received holding gate");
        gateState = HOLDING_OPEN;
        }
  }

  keyEvent = false;  // unless the button got pressed:
  blueEvent = false;  // unless the button got pressed:

  getSignal();

void getSignal() {
  static bool lastKeyPad;
  static bool lastBtSignal;
  if ((keyPad && !lastKeyPad) || (btSignal && !lastBtSignal)) {
    if (keyPad){
      Serial.println("I see you pressed the red button.");

      keyEvent = true;
    }
    else if (btSignal) {
      Serial.println("I see you pressed the blue button.");

      blueEvent = true;
    }
  }

  lastKeyPad = keyPad;
  lastBtSignal = btSignal;
}

Pretty sure the problem is in this last section, but I'm not sure

  case HOLDING_OPEN:
    motors = OFF;
    Serial.println("waiting for blue botton to continue.");
    if (blueEvent) {
      Serial.println("Command received gate resuming.");
      gateState = CLOSING;
    }

    break;

Thanks for any help.