// https://wokwi.com/projects/378145039347637249
// https://forum.arduino.cc/t/sliding-game-opener-project/1143078
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
// global current state of the various sensors
bool keyPad; // true if button is being pressed
bool gateOpen; // true if the gate open limit has been reached
bool gateClosed; // true if the gate close limit has been reached
bool openProximity; // true if something is blockage the opening movement
bool closeProximity; // true if something is blockage the closing movement
bool keyEvent; // set on button press
// Variables used for motor speed control
const int maxMotorSpeed = 128; // 50% PWM speed (0-255)
int currentMotorSpeed = 0;
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 = 7777; // 45 seconds someday
//Variables for gate states
enum gateStates {
OPEN,
WAITING_OPEN,
CLOSED,
OPENING,
CLOSING,
JAMMED_OPENING,
JAMMED_CLOSING,
UNKNOWN,
NO_STATE,
} state;
#define IDNAME(name) #name
const char* stateNames[] = {
IDNAME(OPEN),
IDNAME(WAITING_OPEN),
IDNAME(CLOSED),
IDNAME(OPENING),
IDNAME(CLOSING),
IDNAME(JAMMED_OPENING),
IDNAME(JAMMED_CLOSING),
IDNAME(UNKNOWN),
IDNAME(NO_STATE),
};
enum gateStates gateState;
// enum gateStates prevGateState;
void setup() {
// Initialize serial communication
Serial.begin(115200);
Serial.println("limit switches demo demo");
// 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);
setupSimulatedDoor();
runSimulatedDoor();
//Get the current state of the various sensors
readInputs();
// determine the initial gate position
if (gateOpen && gateClosed) {
Serial.println("ERROR - both limit switches activated");
while (1) {
// whatever.
}
}
if (!gateOpen && !gateClosed) {
gateState = UNKNOWN;
Serial.println("GATE POSITION UNKNOWN");
}
if (gateOpen && !gateClosed) {
Serial.println("GATE OPEN CHECK");
gateState = OPENING;
}
if (!gateOpen && gateClosed) {
Serial.println("GATE CLOSED CHECK");
gateState = CLOSING;
}
}
void loop() {
static unsigned long lastTime;
now = millis();
runSimulatedDoor();
// INPUT
readInputs();
// PROCESS - keypad
if (keyEvent)
Serial.println(" ignored that!");
keyEvent = 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
}
// compensate for active LOW or active HIGH sensor signals
# define GATE_SENSE LOW
# define PROX_SENSE LOW
# define BUTTON_SENSE LOW
void readInputs()
{
keyPad = digitalRead(keyPadPin) == BUTTON_SENSE;
gateOpen = digitalRead(gateOpenPin) == GATE_SENSE;
gateClosed = digitalRead(gateClosePin) == GATE_SENSE;
openProximity = digitalRead(openProximityPin) == PROX_SENSE;
closeProximity = digitalRead(closeProximityPin) == PROX_SENSE;
}
void getSignal() {
static bool lastKeyPad;
if (keyPad && !lastKeyPad) {
Serial.println("I see you pressed the button.");
keyEvent = true;
}
lastKeyPad = keyPad;
}
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 = OPENING;
keyEvent = false;
}
break;
case CLOSED:
if (keyEvent) {
gateState = OPENING;
keyEvent = 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;
}
break;
case JAMMED_OPENING:
if (!openProximity) // <- should we wait for clearance or just
gateState = CLOSING;
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;
}
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;
}
break;
case JAMMED_CLOSING:
if (!closeProximity) // <- should we wait for clearance or just
gateState = OPENING;
break;
}
}
void motorControlC()
{
Serial.println("gate is (still) closing");
ledcWrite(openChannel, 0);
ledcWrite(closeChannel, 255);
}
void motorControlO()
{
Serial.println("gate is (still) opening");
ledcWrite(openChannel, 255);
ledcWrite(closeChannel, 0);
}
void motorsOff()
{
Serial.println("motors off");
ledcWrite(openChannel, 0);
ledcWrite(closeChannel, 0);
}
//////////////////////
/*
// library Adafruit_NeoPixel
// setup() +
setupSimulatedDoor();
runSimulatedDoor(); // step the machine so inouts and outpus make sense
// void loop() +
runSimulatedDoor();
*/
// below here finds itself the door simulation machinery
// find five unused I/O pins
# define sDoorDown 0
# define sDoorUp 1
// # define doorX 18
# define sDoorDownMotorPin 4
# define sDoorUpMotorPin 5
# define door
# include <Adafruit_NeoPixel.h>
# define PIN 18 // the pin
# define NPIXELS 17 // number of LEDs on strip
Adafruit_NeoPixel doorBar(NPIXELS, PIN, NEO_GRB + NEO_KHZ800);
int lPosition = 6; // sim door starts at 6
unsigned char dir = 1; // moving up. or is true down?
void setupSimulatedDoor()
{
pinMode(sDoorDown, OUTPUT);
pinMode(sDoorUp, OUTPUT);
pinMode(sDoorDownMotorPin, INPUT);
pinMode(sDoorUpMotorPin, INPUT);
doorBar.begin();
doorBar.setPixelColor(2, 0xff0000);
doorBar.show();
delay(777);
}
void runSimulatedDoor()
{
// int doorPosition = analogRead(A0);
// delay(20);
// digitalWrite(sDoorDown, doorPosition < 100 ? LOW : HIGH); // active low
// digitalWrite(sDoorUp, doorPosition > 923 ? LOW : HIGH);
static unsigned long lastTime;
if (millis() - lastTime < 333) return;
lastTime = millis();
// this did work checking in this case. may not always
if (0) { // test - door will just cycle up and down all by itself
lPosition += dir ? -1 : 1;
if ((lPosition == -1) || (lPosition == NPIXELS)) dir = !dir;
}
else { // read the main code outputs and run the door if
if (digitalRead(sDoorDownMotorPin)) lPosition--;
if (digitalRead(sDoorUpMotorPin)) lPosition++;
}
if (lPosition == -1) lPosition = 0;
if (lPosition == NPIXELS) lPosition = NPIXELS - 1;
// argh! Serial.println(lPosition); delay(777);
digitalWrite(sDoorDown, lPosition == 0 ? LOW : HIGH);
digitalWrite(sDoorUp, lPosition == NPIXELS - 1 ? LOW : HIGH);
// moved the output section here to keep everything in phase:
doorBar.clear();
doorBar.setPixelColor(lPosition, 0xff0000);
doorBar.show();
}
Loading
esp32-c3-devkitm-1
esp32-c3-devkitm-1
UP LIMIT
DOWN LIMIT
MOTOR UP
MOTOR DOWN
Hall /OPEN
Hall /CLOSED