I've got my hardware assembled, except the keypad, since I need to install the opener to the gate before getting that hooked up and in place, so I'm testing on real hardware now and running into some weird issues.
It doesn't seem to ever go to the closing step. I've got my steam deck hooked up to it, since I don't have a laptop, so I can get the serial output.
in FSM, state is CLOSED
making the motor(s) opening the gate
gate is (still) opening
GATE OPEN
stop the motor(s)
motors off
in FSM, state is OPEN
in FSM, state is WAITING_OPEN
time's up! Closing gate.
in FSM, state is OPENING
From the output everything seems to go well but after it says closing gate it moves to opening.
Here is the current state of the FSM code:
void gateFSM() {
static gateStates lastReport;
if (gateState != lastReport) {
Serial.print(" in FSM, state is ");
Serial.println(stateNames[gateState]);
lastReport = gateState;
}
switch (gateState) {
case UNKNOWN:
if (keyEvent) {
gateState = CLOSING;
keyEvent = false;
}
if (blueEvent) {
gateState = CLOSING;
blueEvent = false;
}
break;
case CLOSED:
motors = OFF;
if (keyEvent) {
gateState = OPENING;
keyEvent = false; // handled
}
if (blueEvent) {
gateState = OPENING;
blueEvent = false; // handled
}
break;
case OPENING:
motors = TO_OPEN;
if (gateOpen) {
Serial.println("GATE OPEN");
motors = OFF;
gateState = OPEN;
}
if (openProximity) {
Serial.println("OBJECT FOUND WHILE OPENING");
gateState = JAMMED_OPENING;
}
if (blueEvent || keyEvent) {
Serial.println("Command received gate holding.");
gateState = HOLDING_OPEN;
}
break;
case JAMMED_OPENING:
motors = OFF;
alarm();
if (!openProximity) // <- should we wait for clearance or just
gateState = OPENING;
break;
case OPEN:
motors = OFF;
startTime = now;
gateState = WAITING_OPEN;
break;
case WAITING_OPEN:
if (now - startTime >= gateOpenDuration) {
Serial.println("time's up! Closing gate.");
gateState = CLOSING;
}
if (blueEvent || keyEvent) {
Serial.println("Command received gate holding.");
gateState = HOLDING_OPEN;
}
break;
case HOLDING_OPEN:
motors = OFF;
if (blueEvent || keyEvent) {
Serial.println("Command received gate resuming.");
gateState = OPENING;
}
break;
case CLOSING:
motors = TO_CLOSE;
if (gateClosed) {
Serial.println("GATE CLOSED");
motors = OFF;
gateState = CLOSED;
}
if (closeProximity) {
Serial.println("OBJECT FOUND WHILE CLOSING");
gateState = JAMMED_CLOSING;
}
if (blueEvent || keyEvent) {
Serial.println("Command received gate holding.");
gateState = HOLDING_CLOSE;
}
case HOLDING_CLOSE:
motors = OFF;
if (blueEvent || keyEvent) {
Serial.println("Command received gate resuming.");
gateState = CLOSING;
}
break;
break;
case JAMMED_CLOSING:
motors = OFF;
alarm();
if (!closeProximity) // <- should we wait for clearance or just
gateState = CLOSING;
break;
}
}
Here is the full code:
// https://wokwi.com/projects/378031652666068993
// https://forum.arduino.cc/t/sliding-game-opener-project/1143078
#include "pitches.h"
const int gateOpenPin = 2; // GPIO2 for gate open
const int gateClosePin = 15; // GPIO3 for gate close
const int openProximityPin = 17; // GPIO4 for open proximity sensor
const int closeProximityPin = 4; // GPIO5 for close proximity sensor
const uint8_t motorControlPinR = 27; // GPIO6 for right motor control (PWM)
const uint8_t motorControlPinL = 14; // GPIO7 for left motor control (PWM)
const int keyPadPin = 21; // GPIO8 for keypad signal
const int speakerPin = 22;
//Varibales for checking pin states
int gateOpen;
int gateClosed;
int openProximity;
int closeProximity;
int keyPad;
int btSignal;
bool keyEvent; // set on red button press
bool blueEvent; // set on blue button press
// notes in the melody:
int melody[] = {
NOTE_C4, NOTE_G3, NOTE_A3, NOTE_B3
};
// Variables used for motor speed control
const int maxMotorSpeed = 128; // 50% PWM speed (0-255)
int currentMotorSpeed = 128;
int openChannel = 1;
int closeChannel = 2;
enum motorStates { TO_OPEN,
OFF,
TO_CLOSE,
NONE }; // should we be closing, off or opening with da motor(s)
unsigned char motors; // the output variable from the P phase
// Variables used for timing
unsigned long now;
unsigned long startTime = 0;
const unsigned long gateOpenDuration = 5000; // 45 seconds someday
//Variables for gate states
enum gateStates {
OPEN,
WAITING_OPEN,
HOLDING_OPEN,
CLOSED,
OPENING,
CLOSING,
HOLDING_CLOSE,
JAMMED_OPENING,
JAMMED_CLOSING,
UNKNOWN,
NO_STATE,
} state;
#define IDNAME(name) #name
const char* stateNames[] = {
IDNAME(OPEN),
IDNAME(WAITING_OPEN),
IDNAME(HOLDING_OPEN),
IDNAME(HOLDING_CLOSE),
IDNAME(CLOSED),
IDNAME(OPENING),
IDNAME(CLOSING),
IDNAME(JAMMED_OPENING),
IDNAME(JAMMED_CLOSING),
IDNAME(UNKNOWN),
IDNAME(NO_STATE),
};
enum gateStates gateState;
#define CUSTOM_SETTINGS
#define INCLUDE_LEDCONTROL_MODULE
#include <DabbleESP32.h>
void setup() {
// Initialize serial communication
Serial.begin(115200);
Dabble.begin("Gate");
// 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);
gateClosed = digitalRead(gateClosePin);
openProximity = digitalRead(openProximityPin);
closeProximity = digitalRead(closeProximityPin);
Serial.print("gateOpen status: ");
Serial.println(gateOpen);
Serial.print("gateClosed status: ");
Serial.println(gateClosed);
// determine the initial gate position
if (gateOpen == LOW && gateClosed == LOW) {
gateState = UNKNOWN;
Serial.println("GATE POSITION UNKNOWN");
gateState = CLOSING;
}
if (gateOpen == HIGH && gateClosed == HIGH) {
gateState = UNKNOWN;
Serial.println("GATE POSITION UNKNOWN");
gateState = CLOSING;
}
if (gateOpen == HIGH && gateClosed == LOW) {
Serial.println("GATE OPEN CHECK");
gateState = OPENING;
}
if (gateOpen == LOW && gateClosed == HIGH) {
Serial.println("GATE CLOSED CHECK");
gateState = CLOSING;
}
}
// compensate for active LOW or active HIGH sensor signals
#define GATE_SENSE LOW
#define PROX_SENSE LOW
#define BUTTON_SENSE LOW
void loop() {
static unsigned long lastTime;
Dabble.processInput();
now = millis();
/* Get the current state of the various sensors
keyPad true if button is being pressed
gateOpen true if the gate open limit has been reached
gateClose true if the gate close limit has been reached
openProximity true if something is blockage the opening movement
closeProximity true if something is blockage the closing movement
*/
// INPUT
keyPad = digitalRead(keyPadPin) == BUTTON_SENSE;
btSignal = LedControl.getpinState()/* == BUTTON_SENSE*/;
gateOpen = digitalRead(gateOpenPin) == GATE_SENSE;
gateClosed = digitalRead(gateClosePin) == GATE_SENSE;
openProximity = digitalRead(openProximityPin) == PROX_SENSE;
closeProximity = digitalRead(closeProximityPin) == PROX_SENSE;
// PROCESS - keypad
if (keyEvent)
Serial.println(" ignored red button!");
if (blueEvent)
Serial.println("Blue signal received holding gate");
keyEvent = false; // unless the button got pressed:
blueEvent = false; // unless the button got pressed:
getSignal();
// PROCESS limit and jam switches
static unsigned char lastMotors = NONE;
motors = OFF; // unless told otherwise by the process phase
gateFSM(); // process the inputs, develop motors control variable
// OUTPUT - set the motor(s) to the task
// Serial.println("now set the motor(s) opening, closing or off");
// if you need to, probably really should always in real life but
// we can cut the spam to printing only when a change is made
if (motors != lastMotors) {
lastMotors = motors;
if (motors == TO_OPEN) {
Serial.println("making the motor(s) opening the gate");
motorControlO();
}
if (motors == TO_CLOSE) {
Serial.println("making the motor(s) closing the gate");
motorControlC();
}
if (motors == OFF) {
Serial.println("stop the motor(s)");
motorsOff();
}
}
delay(50); // poor man's loop throttle, debounce and spam mitigation
}
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;
}
void gateFSM() {
static gateStates lastReport;
if (gateState != lastReport) {
Serial.print(" in FSM, state is ");
Serial.println(stateNames[gateState]);
lastReport = gateState;
}
switch (gateState) {
case UNKNOWN:
if (keyEvent) {
gateState = CLOSING;
keyEvent = false;
}
if (blueEvent) {
gateState = CLOSING;
blueEvent = false;
}
break;
case CLOSED:
motors = OFF;
if (keyEvent) {
gateState = OPENING;
keyEvent = false; // handled
}
if (blueEvent) {
gateState = OPENING;
blueEvent = false; // handled
}
break;
case OPENING:
motors = TO_OPEN;
if (gateOpen) {
Serial.println("GATE OPEN");
motors = OFF;
gateState = OPEN;
}
if (openProximity) {
Serial.println("OBJECT FOUND WHILE OPENING");
gateState = JAMMED_OPENING;
}
if (blueEvent || keyEvent) {
Serial.println("Command received gate holding.");
gateState = HOLDING_OPEN;
}
break;
case JAMMED_OPENING:
motors = OFF;
alarm();
if (!openProximity) // <- should we wait for clearance or just
gateState = OPENING;
break;
case OPEN:
motors = OFF;
startTime = now;
gateState = WAITING_OPEN;
break;
case WAITING_OPEN:
if (now - startTime >= gateOpenDuration) {
Serial.println("time's up! Closing gate.");
gateState = CLOSING;
}
if (blueEvent || keyEvent) {
Serial.println("Command received gate holding.");
gateState = HOLDING_OPEN;
}
break;
case HOLDING_OPEN:
motors = OFF;
if (blueEvent || keyEvent) {
Serial.println("Command received gate resuming.");
gateState = OPENING;
}
break;
case CLOSING:
motors = TO_CLOSE;
if (gateClosed) {
Serial.println("GATE CLOSED");
motors = OFF;
gateState = CLOSED;
}
if (closeProximity) {
Serial.println("OBJECT FOUND WHILE CLOSING");
gateState = JAMMED_CLOSING;
}
if (blueEvent || keyEvent) {
Serial.println("Command received gate holding.");
gateState = HOLDING_CLOSE;
}
case HOLDING_CLOSE:
motors = OFF;
if (blueEvent || keyEvent) {
Serial.println("Command received gate resuming.");
gateState = CLOSING;
}
break;
break;
case JAMMED_CLOSING:
motors = OFF;
alarm();
if (!closeProximity) // <- should we wait for clearance or just
gateState = CLOSING;
break;
}
}
void motorControlC() {
Serial.println("gate is (still) closing");
ledcWrite(openChannel, 0);
ledcWrite(closeChannel, currentMotorSpeed);
}
void motorControlO() {
Serial.println("gate is (still) opening");
ledcWrite(closeChannel, 0);
ledcWrite(openChannel, currentMotorSpeed);
}
void motorsOff() {
Serial.println("motors off");
ledcWrite(openChannel, 0);
ledcWrite(closeChannel, 0);
}
void alarm() {
int currentNote = random(1,4);
tone(speakerPin, melody[currentNote], 1000/4);
}
Thanks for any help.