// https://wokwi.com/projects/378031652666068993
// 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
//Varibales for checking pin states
int gateOpen;
int gateClosed;
int openProximity;
int closeProximity;
int keyPad;
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);
setupSimulatedDoor();
runSimulatedDoor();
// 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);
// determine the initial gate position
if (gateOpen == LOW && gateClosed == LOW) {
Serial.println("ERROR - both limit switches activated");
while (1) {
// whatever.
}
}
if (gateOpen == HIGH && gateClosed == HIGH) {
gateState = UNKNOWN;
Serial.println("GATE POSITION UNKNOWN");
}
if (gateOpen == LOW && gateClosed == HIGH) {
Serial.println("GATE OPEN CHECK");
gateState = OPENING;
}
if (gateOpen == HIGH && gateClosed == LOW) {
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;
now = millis();
runSimulatedDoor();
/* 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;
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 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
}
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();
}