Cleanly formatted code is must for debugging! You can set the Arduino IDE to use tabs instead of spaces. See a guide found in this post. And set the indentation size.
This is just my preference so that the indentation is quite visible:
(use tabs and set equal to 4 spaces in size)
editor.tabs.expand=false
editor.tabs.size=4
I’ve formatted the code as such. And used ‘snake_case’ for variables and ‘camelCase’ for functions so that the two are easily differentiated.
I’ve refactored what I initially posted. Couldn’t sleep and was thinking over how to simplify the code. As you can only be in one state at a time, I’ve created a shared timer variable that resets between states.
Wasn’t sure when you wanted the interior lights on/off so I left that out. Panic doesn’t lock up but resets to unlocking state - presumably waits another 15secs and relocks.
// Timer Intervals
#define BLINK_INTERVAL 1000
#define LID_OPEN_INTERVAL 120000
#define LOCKDOWN_INTERVAL 15000
#define RELAY_INTERVAL 6000
#define DEBOUNCE_INTERVAL 400
// States
#define STATE_UNLOCKING 0
#define STATE_LOCKING 1
#define STATE_OPENED 2
#define STATE_CLOSED 3
#define STATE_LOCKED 4
// I/O
#define MAG 5
#define REPLAY1 18
#define REPLAY2 19
#define LIGHTS 21
#define PIR 2
#define PANIC 4
#define LEDG 12
#define LEDB 13
#define LEDR 14
unsigned long timer = 0;
unsigned long debounce_timer = 0;
unsigned long blink_timer = 0;
int current_state = STATE_UNLOCKING;
bool package = false;
void setup()
{
Serial.begin(115200);
pinMode(REPLAY1, OUTPUT);
pinMode(REPLAY2, OUTPUT);
pinMode(MAG, INPUT_PULLUP);
pinMode(PIR, INPUT_PULLUP);
pinMode(PANIC, INPUT_PULLUP);
pinMode(LIGHTS, OUTPUT);
pinMode(LEDG, OUTPUT);
pinMode(LEDB, OUTPUT);
pinMode(LEDR, OUTPUT);
// Set the relays to off to begin with or they float partially active
digitalWrite(REPLAY1, HIGH); // HIGH to be off
digitalWrite(REPLAY2, HIGH); // HIGH to be off
digitalWrite(LIGHTS, HIGH); // HIGH to be off
/*** Wifi and MQTT setup stuff removed from here for brevity ***/
}
void loop()
{
switch (current_state)
{
case STATE_UNLOCKING:
if (getTimer(timer, RELAY_INTERVAL))
{
// Stops actuator power
digitalWrite(REPLAY2, HIGH);
current_state = STATE_CLOSED;
}
else
{
// Starts actuator power
digitalWrite(REPLAY2, LOW);
Serial.println("Unlocking");
}
// Flash while unlocking
if (getTimer(blink_timer, BLINK_INTERVAL))
{
digitalWrite(LEDG, !digitalRead(LEDG));
}
break;
case STATE_LOCKING:
if (getTimer(timer, RELAY_INTERVAL))
{
// Stops actuator power
digitalWrite(REPLAY2, HIGH);
current_state = STATE_LOCKED;
}
else
{
// Starts actuator power
digitalWrite (REPLAY1, LOW);
Serial.println("Locking");
}
// Flash while locking
if (getTimer(blink_timer, BLINK_INTERVAL))
{
digitalWrite(LEDG, !digitalRead(LEDG));
}
break;
case STATE_CLOSED:
// Just opened and debounced
if (digitalRead(MAG) && getTimer(debounce_timer, DEBOUNCE_INTERVAL))
{
current_state = STATE_OPENED;
timer = 0;
debounce_timer = 0;
Serial.println("Opened!");
}
// Package arrived and lockout timer expired
else if (package == true && getTimer(timer, LOCKDOWN_INTERVAL))
{
current_state = STATE_LOCKING;
}
// Indicate ready
digitalWrite(LEDG, HIGH);
break;
case STATE_OPENED:
// Just closed and debounced
if (!digitalRead(MAG) && getTimer(debounce_timer, DEBOUNCE_INTERVAL))
{
current_state = STATE_CLOSED;
timer = 0;
debounce_timer = 0;
Serial.println("Closed!");
}
// Lid open for too long
else if (getTimer(timer, LID_OPEN_INTERVAL))
{
Serial.println("AJAR");
//client.publish("vault/lid_ajar", "TRUE");
}
// Flash while open
if (getTimer(blink_timer, BLINK_INTERVAL))
{
digitalWrite(LEDB, !digitalRead(LEDB));
}
// Assume package arrived
package = true;
digitalWrite(LEDG, LOW);
break;
case STATE_LOCKED:
// Unlock incase of panic tripped
if (digitalRead(PIR) == HIGH || digitalRead(PANIC) == LOW)
{
current_state = STATE_UNLOCKING;
timer = 0;
debounce_timer = 0;
Serial.println("Panic!");
}
// Flash while locked
if (getTimer(blink_timer, BLINK_INTERVAL))
{
digitalWrite(LEDR, !digitalRead(LEDR));
}
Serial.println("Locked!");
break;
}
}
/**
* Determine if time has reached given interval
*
* @param unisnged long time The current timer
* @param int interval Length of time to run timer
* @return bool True when timer is complete
* @return bool False when timer is counting
*/
bool getTimer(unsigned long &timer, int interval)
{
if (timer < 1)
{
timer = millis();
}
if (millis() - timer >= interval)
{
timer = 0;
return true;
}
return false;
}