I am trying to create a controller for a package delivery vault and created the code that I will put below. I have numerous items I wanted timed like blinking LED's and lock, unlock and lockdown timeout operations. The entire project is going to be open source... if I can ever get the controller stable. What it is supposed to do:
Power it up and it will run the unlock solenoid for 6 seconds. (Startup) It will sit and wait for the lid to be opened and light up a green LED. (Ready). Once the lid is opened, it will blink a blue LED and wait for the lid to close. If during that time, the lid is left open for more than 2 minutes, it will send an MQTT message. (Lid Open). Once the lid has been closed, it will turn on a red LED and run a timer for 15 seconds. (Lockdown Timer). Once timed out, it runs the lock solenoid for 6 seconds and then blinks the red LED while waiting for a reset from MQTT. Pretty simple and done in an FSM.
What it DOESN'T do:
It starts up and the timer runs for 6 seconds and moves to ready state and the green LED works. It waits for the lid to open and when it opens, the blink_blue and ajar timers function properly. It sees the lid close, turns on the red light and moves to the lockdown timer state where it stays stuck in that loop and it appears that the timer does NOT increment... I think. I have put in a serial print to print that specific timer and it always stays zero. I have tried MANY MANY times to get that state to work but can't sort it out. It's set up the exact same way to the ones that work but doesn't here. I am out of ideas. Any suggestions you have would be GREATLY appreciated...
- /*
- Package Vault Controller for ESP32
- Copyright 2019 theguruofnothing
- Board: ESP32 Devkit V1 (DoIt)
- This controller is the brain for "The Package Vault Project", an open source high(er) security package
- reception device. It was designed with the sole purpose of curtailing package theft. I want others to take
- this design and make it their own. However, I would hope that you would honor my efforts for the greater
- good and not use it for unbridaled commercial gain. Custom designed controller units are available at
- www.superhouse.tv and I hope you come by and check out the community on the Discourse channel there.
- Questions? You can reach me at TheGuruOfNothing@yahoo.com
- */
- #include <WiFi.h>
- #include <PubSubClient.h>
- WiFiClient Vault1;
- PubSubClient client(Vault1);
- //Timer Intervals
- #define BLINK_INTERVAL 1000 // 800MS BLINK (RED OR BLUE LED)
- #define BLINK_INTERVAL_TOGGLE 400 // EMERGENCY INDICATOR (R,G,B)
- #define LID_OPEN_TIME_OUT 120000 // 2 MINUTES BEFORE LID_OPEN MESSAGE TO MQTT
- #define LOCKDOWN_TIMER 5000 // 15 SECOND LOCKDOWN TIMER
- #define RELAY_TIMER 6000 // TIME ACTUATOR RELAY SHOULD BE ACTIVE IN ANY DIRECTION
- #define DEBOUNCE_DELAY 400 // MAG SWITCH DEBOUNCE TIMER
- //States
- #define STATE_STARTUP 0
- #define STATE_READY 1
- #define STATE_LID_OPEN 2
- #define STATE_TIMER_RUNNING 3
- #define STATE_LOCKING_DOWN 4
- #define STATE_SECURED 5
- //Pin Definitions
- #define mag_sw 5 //Lid switch
- #define relay_1 18 //Unlock
- #define relay_2 19 //Lock
- #define int_lights 21 //Interior light strip
- #define pir 2 //Passive infrared sensor, internal to box (Panic/Safety)
- #define panic_button 4 //Lit button, internal to box (Panic/Safety)
- #define ledG 12 //Green LED
- #define ledB 13 //Blue LED
- #define ledR 14 //Red LED
- bool lid_state = 0;
- bool last_lid_state = 0;
- unsigned long time_now;
- unsigned long last_debounce_time;
- unsigned long one;
- unsigned long two;
- unsigned long three;
- unsigned long blink_time = 0;
- unsigned long ajar = 0;
- uint16_t last_lid_time =0;
- //Your Wifi and MQTT info here
- int current_state = STATE_STARTUP;
- void setup() {
- Serial.begin(115200); //Change this if using Arduino board
- // We set up the pinmode() and starting conditions
- pinMode(relay_1, OUTPUT);
- pinMode(relay_2, OUTPUT);
- pinMode(mag_sw, INPUT_PULLUP);
- pinMode(pir, INPUT_PULLUP);
- pinMode(panic_button, INPUT_PULLUP);
- pinMode(int_lights, OUTPUT);
- pinMode(ledG, OUTPUT);
- pinMode(ledB, OUTPUT);
- pinMode(ledR, OUTPUT);
- //Set the relay's to off to begin with or they float partially active...
- digitalWrite(relay_1, HIGH); //Relay board requires these HIGH to be off
- digitalWrite(relay_2, HIGH); //Relay board requires these HIGH to be off
- digitalWrite(int_lights, HIGH); //Relay board requires these HIGH to be off
- //Start up Wifi connection
- //Connecting to MQTT server and set callback
- }
- void read_lid()
- {
- lid_state = digitalRead(mag_sw);
- if (millis() - last_debounce_time > DEBOUNCE_DELAY) { //Debounce Timer
- last_debounce_time = millis();
- }
- if (lid_state != last_lid_state) { //check if the button is pressed or released
- last_lid_state = lid_state;
- }
- }
- void panic_check() { //This is a kill switch for the box. Forces an unlock and then locks the processor in an infinite loop
- // to prevent any possibility of resetting and relocking
- int PANIC1 = digitalRead(pir);
- int PANIC2 = digitalRead(panic_button);
- if (PANIC1 || PANIC2 == LOW) // Something has gone very wrong and box needs to be unlocked NOW!!!
- {
- unlock();
- Serial.println("Emergency Protocol Activated");
- Serial.println("A failure has occurred and vault has been disabled. Power must be cycled to reset");
- client.publish("vault/state", "Panic");
- while (true) {} //I want to kill the process so there is no way to restart the main loop unless power cycled
- //so I created an infinite while() loop to do that. Probably a more elegant way to do it
- //but let's face it, if this protocol has to be enacted, shit HAS hit the fan...
- }
- }
- void unlock()
- {
- time_now = millis();
- digitalWrite(relay_2, LOW); //Starts actuator power
- // start up the relay run timer
- if ((time_now - one >= RELAY_TIMER) && (RELAY_TIMER >0))
- {
- one = time_now;
- Serial.println(one);
- digitalWrite(relay_2, HIGH); //Stops actuator power
- digitalWrite(ledG, HIGH);
- }
- }
- void lock()
- {
- time_now = millis();
- digitalWrite (int_lights, HIGH);
- digitalWrite (relay_1, LOW); //Starts actuator power
- // start up the relay run timer
- if ((time_now - two > RELAY_TIMER) && (RELAY_TIMER >0))
- {
- two = time_now;
- digitalWrite (relay_1, HIGH); //Stops actuator power
- }
- }
- void lockout_timer()
- {
- time_now = millis();
- if ((time_now - three > LOCKDOWN_TIMER) && (LOCKDOWN_TIMER >0))
- {
- three = time_now;
- Serial.print("Timer has timed out now...");
- }
- }
- void blink_blue()
- {
- //Blink the BLUE LED at once per second...until the reset request is received
- if(millis() - blink_time > BLINK_INTERVAL)
- {
- blink_time = millis();// Update the time for this activity:
- digitalWrite(ledB, !digitalRead(ledB));
- }
- }
- void blink_red()
- {
- //Blink the RED LED at once per second...until the reset request is received
- if(millis() - blink_time > BLINK_INTERVAL)
- {
- blink_time = millis();// Update the time for this activity:
- digitalWrite(ledR, !digitalRead(ledR));
- }
- }
- void lid_ajar()
- {
- if (millis() - ajar > LID_OPEN_TIME_OUT) {
- ajar = millis();// Update the time for this activity:
- client.publish("vault/lid_ajar", "TRUE");
- }
- }
- void loop() {
- time_now = millis(); //Not sure if this is needed here, but it's there...
- read_lid(); // Update the lid status
- Serial.println (lid_state);
- client.loop(); //Loops connection with MQTT subscriptions
- switch (current_state) {
- case STATE_STARTUP:
- Serial.println("Startup In Progress...");
- Serial.println("______________________");
- unlock();
- if (one > RELAY_TIMER){
- current_state = STATE_READY;
- }
- break;
- case STATE_READY:
- Serial.println("Ready For Packages...");
- Serial.println("______________________");
- if (lid_state == 1)
- {
- current_state = STATE_LID_OPEN;
- }
- break;
- case STATE_LID_OPEN:
- Serial.println("The Lid Has Been Opened...");
- Serial.println("______________________");
- digitalWrite(ledG, LOW);
- if (lid_state == 1)
- {
- blink_blue();
- lid_ajar();
- }
- else
- {
- digitalWrite(ledB, LOW);
- digitalWrite (int_lights, LOW);
- digitalWrite(ledR, HIGH);
- current_state = STATE_TIMER_RUNNING;
- }
- break;
- case STATE_TIMER_RUNNING:
- Serial.println ("Timer Operation Has Begun...");
- Serial.println ("______________________");
- if (three >= LOCKDOWN_TIMER){
- current_state = STATE_LOCKING_DOWN;
- }
- break;
- case STATE_LOCKING_DOWN:
- Serial.println("Locking The Box Now...");
- Serial.println("______________________");
- lock();
- if (two > RELAY_TIMER){
- current_state = STATE_SECURED;
- }
- break;
- case STATE_SECURED:
- Serial.println("The captain has extinguished the NO SMOKING sign...");
- Serial.println("You may now roam freely about the cabin...");
- Serial.println("Vault Secured...");
- Serial.println("______________________");
- blink_red();
- panic_check(); //Check if the panic indicators have been tripped after the lockdown has occurred
- break;
- }
- }