pslockout/firmware/firmware.ino

257 lines
6.5 KiB
C++

#include <Arduino.h>
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
const char* WIFI_SSID = "Protospace";
const char* WIFI_PASS = "yycmakers";
char wifiMACAddr[18];
const String API_ROUTE = String("http://tools-api.tannercollin.com/api/lockout/");
#define RELAY_PIN D1
#define ON_BUTTON_PIN D3
#define OFF_BUTTON_PIN D4
#define ARMED_LED_PIN D5
#define ON_LED_PIN D6
#define OFF_LED_PIN D7
#define RELAY_CLOSED HIGH
#define RELAY_OPEN !RELAY_CLOSED
#define BUTTON_CLOSED LOW
#define BUTTON_OPEN !BUTTON_CLOSED
#define LED_ON HIGH
#define LED_OFF !LED_ON
#define LOGGING true
#define DELAY_TIME 10
#define COMM_IDLE_TIME 500 / DELAY_TIME
enum wifiStates
{
WIFI_DISCONNECTED,
WIFI_CONNECTING,
WIFI_CONNECTED,
} wifiState = WIFI_DISCONNECTED;
enum lockStates
{
LOCK_OFF,
LOCK_ARMED,
LOCK_ON_PRESSED,
LOCK_ON,
LOCK_OFF_PRESSED,
} lockState = LOCK_OFF;
enum commStates
{
COMM_INIT,
COMM_IDLE,
COMM_SEND,
} commState = COMM_INIT;
void setup()
{
if (LOGGING) Serial.begin(115200);
if (LOGGING) Serial.println("[INFO] Serial started.");
pinMode(RELAY_PIN, OUTPUT);
pinMode(ON_BUTTON_PIN, INPUT_PULLUP);
pinMode(OFF_BUTTON_PIN, INPUT_PULLUP);
pinMode(ARMED_LED_PIN, OUTPUT);
pinMode(ON_LED_PIN, OUTPUT);
pinMode(OFF_LED_PIN, OUTPUT);
}
void loop()
{
processWifiState();
processLockState();
processCommState();
delay(DELAY_TIME);
}
void processWifiState()
{
switch(wifiState) {
case WIFI_DISCONNECTED:
lockState = LOCK_OFF;
commState = COMM_INIT;
if (LOGGING) Serial.println("[INFO] Wifi is disconnected. Attempting to connect...");
WiFi.begin(WIFI_SSID, WIFI_PASS);
wifiState = WIFI_CONNECTING;
break;
case WIFI_CONNECTING:
lockState = LOCK_OFF;
commState = COMM_INIT;
if (WiFi.status() == WL_CONNECTED) {
if (LOGGING) Serial.println("[INFO] Wifi is connected.");
if (LOGGING) Serial.print("[INFO] Wifi IP Address: ");
if (LOGGING) Serial.println(WiFi.localIP());
byte ar[6];
WiFi.macAddress(ar);
sprintf(wifiMACAddr, "%02X%02X%02X%02X%02X%02X", ar[0], ar[1], ar[2], ar[3], ar[4], ar[5]);
if (LOGGING) Serial.print("[INFO] Wifi MAC Address: ");
if (LOGGING) Serial.println(wifiMACAddr);
wifiState = WIFI_CONNECTED;
}
break;
case WIFI_CONNECTED:
if (WiFi.status() != WL_CONNECTED) {
wifiState = WIFI_DISCONNECTED;
}
break;
default:
if (LOGGING) Serial.println("[ERROR] Invalid wifi state.");
wifiState = WIFI_DISCONNECTED;
break;
}
}
void processLockState()
{
switch (lockState) {
case LOCK_OFF:
digitalWrite(ARMED_LED_PIN, LED_OFF);
digitalWrite(ON_LED_PIN, LED_OFF);
digitalWrite(OFF_LED_PIN, LED_ON);
digitalWrite(RELAY_PIN, RELAY_OPEN);
break;
case LOCK_ARMED:
digitalWrite(ARMED_LED_PIN, LED_ON);
digitalWrite(ON_LED_PIN, LED_OFF);
digitalWrite(OFF_LED_PIN, LED_ON);
digitalWrite(RELAY_PIN, RELAY_OPEN);
if (digitalRead(OFF_BUTTON_PIN) == BUTTON_CLOSED) {
if (LOGGING) Serial.println("[INFO] Unarming interlock.");
lockState = LOCK_OFF_PRESSED;
} else if (digitalRead(ON_BUTTON_PIN) == BUTTON_CLOSED) {
if (LOGGING) Serial.println("[INFO] On button pressed.");
lockState = LOCK_ON_PRESSED;
}
break;
case LOCK_ON_PRESSED:
if (digitalRead(OFF_BUTTON_PIN) == BUTTON_CLOSED) {
if (LOGGING) Serial.println("[ERROR] Both buttons pressed, aborting.");
lockState = LOCK_OFF_PRESSED;
} else if (digitalRead(ON_BUTTON_PIN) == BUTTON_OPEN) {
if (LOGGING) Serial.println("[INFO] Turning machine on.");
lockState = LOCK_ON;
}
break;
case LOCK_ON:
digitalWrite(ARMED_LED_PIN, LED_ON);
digitalWrite(ON_LED_PIN, LED_ON);
digitalWrite(OFF_LED_PIN, LED_OFF);
digitalWrite(RELAY_PIN, RELAY_CLOSED);
if (digitalRead(OFF_BUTTON_PIN) == BUTTON_CLOSED) {
if (LOGGING) Serial.println("[INFO] Off button pressed.");
lockState = LOCK_OFF_PRESSED;
}
break;
case LOCK_OFF_PRESSED:
if (digitalRead(OFF_BUTTON_PIN) == BUTTON_OPEN) {
if (LOGGING) Serial.println("[INFO] Turning machine off.");
lockState = LOCK_OFF;
}
break;
default:
if (LOGGING) Serial.println("[ERROR] Invalid lock state.");
lockState = LOCK_OFF;
break;
}
}
// JSON functions to prevent memory leaking
String serializeJson(int lockState) {
// Generated with: https://arduinojson.org/assistant/
const size_t bufferSize = JSON_OBJECT_SIZE(1) + 50;
DynamicJsonBuffer jsonBuffer(bufferSize);
JsonObject& root = jsonBuffer.createObject();
root["lockState"] = (int) lockState;
String postData = String();
root.printTo(postData);
return postData;
}
String deserializeJson(String input) {
// Generated with: https://arduinojson.org/assistant/
const size_t bufferSize = JSON_OBJECT_SIZE(1) + 50;
DynamicJsonBuffer jsonBuffer(bufferSize);
JsonObject& root = jsonBuffer.parseObject(input);
String action = root["action"];
return action;
}
void processCommState()
{
static int commIdleCount = 0;
switch (commState) {
case COMM_INIT:
commIdleCount = 0;
commState = COMM_IDLE;
break;
case COMM_IDLE:
commIdleCount++;
if (commIdleCount >= COMM_IDLE_TIME) {
commState = COMM_SEND;
}
break;
case COMM_SEND:
if (LOGGING) Serial.println("[INFO] HTTP begin.");
HTTPClient http;
//http.begin("https://url", "7a 9c f4 db 40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS
http.begin(API_ROUTE + wifiMACAddr);
http.addHeader("Content-Type", "application/json");
if (LOGGING) Serial.print("[INFO] HTTP POST: ");
String postData = serializeJson(lockState);
if (LOGGING) Serial.println(postData);
int httpCode = http.POST(postData);
if (httpCode > 0) {
if (LOGGING) Serial.printf("[INFO] POST success, code: %d\n", httpCode);
if (httpCode == HTTP_CODE_OK) {
if (LOGGING) Serial.print("[INFO] Resource found, parsing response: ");
String payload = http.getString();
if (LOGGING) Serial.println(payload);
String action = deserializeJson(payload);
if (action == "arm" && lockState == LOCK_OFF && digitalRead(ON_BUTTON_PIN) == BUTTON_OPEN) {
lockState = LOCK_ARMED;
} else if (action == "disarm" && lockState == LOCK_ARMED) {
lockState = LOCK_OFF;
} else if (action == "disarm" && lockState == LOCK_ON) {
lockState = LOCK_OFF;
}
if (LOGGING) Serial.println("[INFO] action: " + action);
} else {
if (LOGGING) Serial.println("[ERROR] Resource not found.");
}
} else {
if (LOGGING) Serial.printf("[ERROR] POST failed, error: %s\n", http.errorToString(httpCode).c_str());
}
commState = COMM_INIT;
break;
}
}