diff --git a/firmware/comm.cpp b/firmware/comm.cpp new file mode 100644 index 0000000..d8df0de --- /dev/null +++ b/firmware/comm.cpp @@ -0,0 +1,230 @@ +#include "comm.h" + +//TODO: abstract http functions + +void postState() +{ + // Don't log more than one error at a time + static bool logErrors = true; + + HTTPClient lockHTTP; + + //lockHTTP.begin("https://url", "7a 9c f4 db 40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS + lockHTTP.begin(SOCKET_URL + wifiMACAddr); + lockHTTP.addHeader("Content-Type", "application/json"); + + if (SERIAL_LOGGING) Serial.println("[INFO] Lock state HTTP begin."); + + if (SERIAL_LOGGING) Serial.print("[INFO] HTTP POST: "); + String postData = serializeLockJson(lockState); + if (SERIAL_LOGGING) Serial.println(postData); + int16_t lockHTTPCode = lockHTTP.POST(postData); + String lockHTTPCodeStr = String(lockHTTPCode); + + if (lockHTTPCode > 0) { + if (SERIAL_LOGGING) Serial.printf("[INFO] POST success, code: %d\n", lockHTTPCode); + + if (lockHTTPCode == HTTP_CODE_OK) { + logErrors = true; + + if (SERIAL_LOGGING) Serial.print("[INFO] Resource found, parsing response: "); + String lockPayload = lockHTTP.getString(); + if (SERIAL_LOGGING) Serial.println(lockPayload); + String action = deserializeLockJson(lockPayload); + + if (action == "arm" && lockState == LOCK_OFF && LEDState == LED_OFF) { + logEvent(LOG_COMM_LOCK_ARM); + lockState = LOCK_PREARM; + } else if (action == "disarm" && lockState != LOCK_ON) { + logEvent(LOG_COMM_LOCK_DISARM); + if (SERIAL_LOGGING) Serial.println("[INFO] Unarming interlock."); + logEvent(LOG_LOCK_DISARM); + lockState = LOCK_OFF; + } + + if (SERIAL_LOGGING) Serial.println("[INFO] action: " + action); + } else { + if (SERIAL_LOGGING) Serial.println("[ERROR] Resource not found."); + if (logErrors) logEvent(LOG_COMM_LOCK_FAIL, lockHTTPCodeStr.c_str(), lockHTTPCodeStr.length()); + logErrors = false; + } + } else { + if (SERIAL_LOGGING) Serial.printf("[ERROR] POST failed, error: %s\n", lockHTTP.errorToString(lockHTTPCode).c_str()); + if (logErrors) logEvent(LOG_COMM_LOCK_FAIL, lockHTTPCodeStr.c_str(), lockHTTPCodeStr.length()); + logErrors = false; + } + + lockHTTP.end(); +} + +void getCards() +{ + // Don't log more than one error at a time + static bool logErrors = true; + + HTTPClient cardHTTP; + + //cardHTTP.begin("https://url", "7a 9c f4 db 40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS + cardHTTP.begin(CARD_URL + wifiMACAddr + "/"); + cardHTTP.addHeader("Content-Type", "application/json"); + + if (SERIAL_LOGGING) Serial.println("[INFO] Card state HTTP begin."); + + if (SERIAL_LOGGING) Serial.println("[INFO] HTTP GET"); + int16_t cardHTTPCode = cardHTTP.GET(); + String cardHTTPCodeStr = String(cardHTTPCode); + + if (cardHTTPCode > 0) { + if (SERIAL_LOGGING) Serial.printf("[INFO] GET success, code: %d\n", cardHTTPCode); + + if (cardHTTPCode == HTTP_CODE_OK) { + logErrors = true; + + if (SERIAL_LOGGING) Serial.print("[INFO] Resource found, parsing response: "); + String cardPayload = cardHTTP.getString(); + cardPayload += String(EEPROM_END_MARKER); + if (SERIAL_LOGGING) Serial.println(cardPayload); + + noInterrupts(); // commit() disables interrupts, but we want an atomic EEPROM buffer write + for (int i = 0; i < cardPayload.length(); i++) { + if (i >= EEPROM_SIZE) break; + EEPROM.write(i, cardPayload.charAt(i)); + } + EEPROM.commit(); + interrupts(); + + if (SERIAL_LOGGING) Serial.println("[INFO] Finished getting card data."); + } else { + if (SERIAL_LOGGING) Serial.println("[ERROR] Resource not found."); + if (logErrors) logEvent(LOG_COMM_CARD_FAIL, cardHTTPCodeStr.c_str(), cardHTTPCodeStr.length()); + logErrors = false; + } + } else { + if (SERIAL_LOGGING) Serial.printf("[ERROR] POST failed, error: %s\n", cardHTTP.errorToString(cardHTTPCode).c_str()); + if (logErrors) logEvent(LOG_COMM_CARD_FAIL, cardHTTPCodeStr.c_str(), cardHTTPCodeStr.length()); + logErrors = false; + } + + cardHTTP.end(); +} + +void postInfolog() +{ + // Don't log more than one error at a time + static bool logErrors = true; + + HTTPClient infoHTTP; + + //infoHTTP.begin("https://url", "7a 9c f4 db 40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS + infoHTTP.begin(INFOLOG_URL + wifiMACAddr + "/"); + infoHTTP.addHeader("Content-Type", "application/json"); + + if (SERIAL_LOGGING) Serial.println("[INFO] Info state HTTP begin."); + + if (SERIAL_LOGGING) Serial.println("[INFO] HTTP POST."); + String postData = serializeLog(); + int16_t infoHTTPCode = infoHTTP.POST(postData); + String infoHTTPCodeStr = String(infoHTTPCode); + + if (infoHTTPCode > 0) { + if (SERIAL_LOGGING) Serial.printf("[INFO] POST success, code: %d\n", infoHTTPCode); + + if (infoHTTPCode == HTTP_CODE_OK) { + logErrors = true; + + if (SERIAL_LOGGING) Serial.print("[INFO] Resource found, parsing response: "); + String infoPayload = infoHTTP.getString(); + if (SERIAL_LOGGING) Serial.println(infoPayload); + uint8_t processed; + uint32_t unixTime; + String version = String(); + deserializeInfoJson(infoPayload, &processed, &unixTime, &version); + + struct timeval tv = { .tv_sec = unixTime, .tv_usec = 0 }; + struct timezone tz = { .tz_minuteswest = 0, .tz_dsttime = 0 }; + settimeofday(&tv, &tz); + + removeLogRecords(processed); + + if (version != LOCKOUT_FIRMWARE_VERSION && lockState == LOCK_OFF) { + noInterrupts(); + if (SERIAL_LOGGING) Serial.println("[INFO] Firmware out of date. Updating..."); + WiFiClient client; + int16_t update_response = ESPhttpUpdate.update(client, UPDATE_URL + wifiMACAddr + "/"); + interrupts(); + + if (SERIAL_LOGGING) printf("[ERROR] %s\n", ESPhttpUpdate.getLastErrorString().c_str()); + String lastErrorNum = String(ESPhttpUpdate.getLastError()); + logEvent(LOG_UPDATE_FAILED, lastErrorNum.c_str(), lastErrorNum.length()); + } + + if (SERIAL_LOGGING) Serial.print("[INFO] Set system time to: "); + if (SERIAL_LOGGING) Serial.println(unixTime); + } else { + if (SERIAL_LOGGING) Serial.println("[ERROR] Resource not found."); + if (logErrors) logEvent(LOG_COMM_INFO_FAIL, infoHTTPCodeStr.c_str(), infoHTTPCodeStr.length()); + logErrors = false; + } + } else { + if (SERIAL_LOGGING) Serial.printf("[ERROR] POST failed, error: %s\n", infoHTTP.errorToString(infoHTTPCode).c_str()); + if (logErrors) logEvent(LOG_COMM_INFO_FAIL, infoHTTPCodeStr.c_str(), infoHTTPCodeStr.length()); + logErrors = false; + } + + infoHTTP.end(); +} + +void processCommState() +{ + static uint16_t commLockIdleCount, commCardIdleCount, commInfoIdleCount; + + switch (commState) { + case COMM_INIT: + commLockIdleCount = 0; + commCardIdleCount = 0; + commInfoIdleCount = 0; + + commState = COMM_IDLE; + break; + case COMM_IDLE: + commLockIdleCount++; + commCardIdleCount++; + commInfoIdleCount++; + + if (commLockIdleCount >= COMM_LOCK_IDLE_TIME) { + commState = COMM_LOCK; + } else if (commCardIdleCount >= COMM_CARD_IDLE_TIME) { + commState = COMM_CARD; + } else if (commInfoIdleCount >= COMM_INFO_IDLE_TIME) { + commState = COMM_INFO; + } + break; + case COMM_LOCK: + { + postState(); + + commLockIdleCount = 0; + + commState = COMM_IDLE; + } + break; + case COMM_CARD: + { + getCards(); + + commCardIdleCount = 0; + + commState = COMM_IDLE; + } + break; + case COMM_INFO: + { + postInfolog(); + + commInfoIdleCount = 0; + + commState = COMM_IDLE; + } + break; + } +} diff --git a/firmware/comm.h b/firmware/comm.h new file mode 100644 index 0000000..125868f --- /dev/null +++ b/firmware/comm.h @@ -0,0 +1,34 @@ +#ifndef COMM_H +#define COMM_H + +#include "firmware.h" + +// times below are multiplied by DELAY_TIME, in ms +#define COMM_LOCK_IDLE_TIME 50 +#define COMM_CARD_IDLE_TIME 1000 +#define COMM_INFO_IDLE_TIME 3000 + +enum commStates +{ + COMM_INIT, + COMM_IDLE, + COMM_LOCK, + COMM_CARD, + COMM_INFO, +}; + +extern enum commStates commState; +extern char wifiMACAddr[20]; +extern const char *LOCKOUT_FIRMWARE_VERSION; +extern const String SOCKET_URL; +extern const String CARD_URL; +extern const String INFOLOG_URL; +extern const String UPDATE_URL; + +void postState(); +void getCards(); +void postInfolog(); +void processCommState(); + +#endif + diff --git a/firmware/firmware.h b/firmware/firmware.h new file mode 100644 index 0000000..100457c --- /dev/null +++ b/firmware/firmware.h @@ -0,0 +1,43 @@ +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(ARDUINO_ESP8266_RELEASE_2_5_0) || ARDUINOJSON_VERSION_MAJOR != 5 || ARDUINOJSON_VERSION_MINOR != 13 || ARDUINOJSON_VERSION_REVISION != 3 +#error Incorrect library version detected. See README. +#endif + +#include "utils.h" +#include "logging.h" +#include "lock.h" +#include "leds.h" +#include "wifi.h" +#include "comm.h" + +#define DELAY_TIME 10 + +#define RELAY_PIN D1 +#define GREEN_BUTTON_PIN D3 +#define RED_BUTTON_PIN D4 +#define GREEN_LED_PIN D6 +#define RED_LED_PIN D7 + +#define RELAY_CLOSED HIGH +#define RELAY_OPEN !RELAY_CLOSED +#define BUTTON_CLOSED LOW +#define BUTTON_OPEN !BUTTON_CLOSED +#define LED_PIN_ON HIGH +#define LED_PIN_OFF !LED_PIN_ON + +#define EEPROM_SIZE 4095 +#define EEPROM_END_MARKER '\0' + +#endif diff --git a/firmware/firmware.ino b/firmware/firmware.ino index 831474b..4e3431a 100644 --- a/firmware/firmware.ino +++ b/firmware/firmware.ino @@ -1,22 +1,8 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "firmware.h" -#if !defined(ARDUINO_ESP8266_RELEASE_2_5_0) || ARDUINOJSON_VERSION_MAJOR != 5 || ARDUINOJSON_VERSION_MINOR != 13 || ARDUINOJSON_VERSION_REVISION != 3 -#error Incorrect library version detected. See README. -#endif - -const char *FIRMWARE_VERSION = "MRWIZARD 0010 MRWIZARD"; - -const char *WIFI_SSID PROGMEM = "Protospace"; -const char *WIFI_PASS PROGMEM = "yycmakers"; -char wifiMACAddr[20] = ""; +const char *LOCKOUT_FIRMWARE_VERSION = "MRWIZARD 0010 MRWIZARD"; +const char *LOCKOUT_WIFI_SSID PROGMEM = "Protospace"; +const char *LOCKOUT_WIFI_PASS PROGMEM = "yycmakers"; const String SOCKET_URL = String("http://tools-socket.protospace.ca/api/lockout/"); const String CARD_URL = String("http://tools-auth.protospace.ca/cards/"); const String INFOLOG_URL = String("http://tools-auth.protospace.ca/infolog/"); @@ -24,153 +10,15 @@ const String UPDATE_URL = String("http://tools-auth.protospace.ca/update/"); Ticker ticker; -#define CARD_BUFFER_LENGTH 14 +char wifiMACAddr[20] = ""; char cardBuffer[CARD_BUFFER_LENGTH]; - -#define CARD_DATA_LENGTH 10 -#define CARD_CHECK_LENGTH 2 -#define CARD_HEAD_BYTE 0x2 -#define CARD_TAIL_BYTE 0x3 -struct __attribute__((packed)) cardData { - char head; - char data[CARD_DATA_LENGTH]; - char checksum[CARD_CHECK_LENGTH]; - char tail; -}; - -#define RELAY_PIN D1 -#define GREEN_BUTTON_PIN D3 -#define RED_BUTTON_PIN D4 -#define GREEN_LED_PIN D6 -#define RED_LED_PIN D7 - -#define RELAY_CLOSED HIGH -#define RELAY_OPEN !RELAY_CLOSED -#define BUTTON_CLOSED LOW -#define BUTTON_OPEN !BUTTON_CLOSED -#define LED_PIN_ON HIGH -#define LED_PIN_OFF !LED_PIN_ON - -#define DELAY_TIME 10 -// times below are multiplied by DELAY_TIME, in ms -#define LOCK_ARMED_TIMEOUT 1000 -#define COMM_LOCK_IDLE_TIME 50 -#define COMM_CARD_IDLE_TIME 1000 -#define COMM_INFO_IDLE_TIME 3000 -#define LED_ARMED_BLINK_TIME 50 -#define LED_ERROR_BLINK_TIME 50 - -#define EEPROM_SIZE 4095 -#define EEPROM_END_MARKER '\0' - -enum wifiStates -{ - WIFI_DISCONNECTED, - WIFI_CONNECTING, - WIFI_CONNECTED, -} wifiState = WIFI_DISCONNECTED; - -enum LEDStates -{ - LED_OFF, - LED_ARMED, - LED_ON, - LED_ERROR, -} LEDState = LED_OFF; - -enum lockStates -{ - LOCK_OFF, - LOCK_PREARM, // prevent arming while buttons held - LOCK_ARMED, - LOCK_ON_PRESSED, // to wait until button is released - LOCK_ON, -} lockState = LOCK_OFF; - -enum commStates -{ - COMM_INIT, - COMM_IDLE, - COMM_LOCK, - COMM_CARD, - COMM_INFO, -} commState = COMM_INIT; - -#define SERIAL_LOGGING true - -#define LOG_SIZE 90 // 100 blew up the stack -#define LOG_DATA_LENGTH CARD_DATA_LENGTH - -enum eventCodes -{ - LOG_BOOT_UP, - LOG_INIT_COMPLETE, - LOG_WIFI_CONNECTED, - LOG_WIFI_DISCONNECTED, - LOG_COMM_LOCK_ARM, - LOG_COMM_LOCK_DISARM, - LOG_COMM_LOCK_FAIL, - LOG_COMM_CARD_FAIL, - LOG_COMM_INFO_FAIL, - LOG_LOCK_OFF, - LOG_LOCK_ARMED, - LOG_LOCK_TIMEOUT, - LOG_LOCK_ON, - LOG_LOCK_DISARM, - LOG_LOCK_ERROR, - LOG_CARD_GOOD_READ, - LOG_CARD_ACCEPTED, - LOG_CARD_DENIED, - LOG_UPDATE_FAILED, - LOG_TEST, -}; - -struct __attribute__((packed)) logData { - uint32_t unixTime; - uint8_t eventCode; - char data[LOG_DATA_LENGTH]; -}; - struct logData eventLog[LOG_SIZE]; uint16_t logPosition = 0; -void logEvent(uint8_t eventCode, const char *data = nullptr, size_t num = 0) -{ - struct logData event; - - noInterrupts(); - - event.unixTime = time(nullptr); - event.eventCode = eventCode; - - memset(event.data, 0, LOG_DATA_LENGTH); - for (uint8_t i = 0; i < LOG_DATA_LENGTH; i++) { - if (i >= num) break; - event.data[i] = data[i]; - } - - if (logPosition < LOG_SIZE) { - eventLog[logPosition++] = event; - } - - interrupts(); -} - -void removeLogRecords(uint8_t num) { - // shift records down by num because they've been sent - - if (num > logPosition) return; - - noInterrupts(); - - for (int i = 0; i < num; i++) { - eventLog[i] = eventLog[i + num]; - } - logPosition -= num; - - interrupts(); -} - +enum wifiStates wifiState = WIFI_DISCONNECTED; +enum LEDStates LEDState = LED_OFF; +enum lockStates lockState = LOCK_OFF; +enum commStates commState = COMM_INIT; void setup() { @@ -182,9 +30,9 @@ void setup() settimeofday(&tv, &tz); if (SERIAL_LOGGING) Serial.println("[INFO] Set system time to 0."); - logEvent(LOG_BOOT_UP, &FIRMWARE_VERSION[9], 4); + logEvent(LOG_BOOT_UP, &LOCKOUT_FIRMWARE_VERSION[9], 4); if (SERIAL_LOGGING) Serial.print("[INFO] Booting firmware version: "); - if (SERIAL_LOGGING) Serial.println(FIRMWARE_VERSION); + if (SERIAL_LOGGING) Serial.println(LOCKOUT_FIRMWARE_VERSION); pinMode(RELAY_PIN, OUTPUT); pinMode(GREEN_BUTTON_PIN, INPUT_PULLUP); @@ -244,523 +92,3 @@ void loop() delay(DELAY_TIME); } - -int8_t charToNum(char input) -{ - return String("0123456789ABCDEF").indexOf(input); -} - -bool checksum(struct cardData *cardData) -{ - // checksum is each hex data byte xord'd together. - // each char is a hex nibble, so we have to work in pairs. - - int8_t even = 0, odd = 0; - - for (int8_t i = 0; i < CARD_DATA_LENGTH; i++) { - int8_t num = charToNum(cardData->data[i]); - - if (num == -1) return false; - if (i % 2 == 0) even ^= num; - if (i % 2 == 1) odd ^= num; - } - - int8_t checksum_even = charToNum(cardData->checksum[0]); - int8_t checksum_odd = charToNum(cardData->checksum[1]); - - if (even == checksum_even && odd == checksum_odd) { - return true; - } else { - return false; - } -} - -void checkCard() -{ - struct cardData *cardData = (struct cardData *) cardBuffer; - - if (cardData->head == CARD_HEAD_BYTE && - cardData->tail == CARD_TAIL_BYTE && - checksum(cardData)) { - - String cardStr = String(); - String authorizedCards = String(); - - for (uint8_t i = 0; i < CARD_DATA_LENGTH; i++) { - cardStr += cardData->data[i]; - } - - for (uint16_t i = 0; i < EEPROM_SIZE; i++) { - char tmp = EEPROM.read(i); - authorizedCards += tmp; - if (tmp == EEPROM_END_MARKER) break; - } - - if (SERIAL_LOGGING) Serial.println("[INFO] Good scan from card: " + cardStr); - logEvent(LOG_CARD_GOOD_READ, cardStr.c_str(), cardStr.length()); - - if (authorizedCards.indexOf(cardStr) >= 0) { - if (SERIAL_LOGGING) Serial.println("[INFO] Card is authorized on machine."); - if (lockState == LOCK_OFF) { - lockState = LOCK_PREARM; - } - logEvent(LOG_CARD_ACCEPTED, cardStr.c_str(), cardStr.length()); - } else { - if (SERIAL_LOGGING) Serial.println("[INFO] Card not authorized on machine."); - LEDState = LED_ERROR; - logEvent(LOG_CARD_DENIED, cardStr.c_str(), cardStr.length()); - } - } -} - -void processWifiState() -{ - switch(wifiState) { - case WIFI_DISCONNECTED: - commState = COMM_INIT; - - if (SERIAL_LOGGING) Serial.println("[INFO] Wifi attempting to connect..."); - - WiFi.begin(WIFI_SSID, WIFI_PASS); - - wifiState = WIFI_CONNECTING; - break; - case WIFI_CONNECTING: - commState = COMM_INIT; - - if (WiFi.status() == WL_CONNECTED) { - if (SERIAL_LOGGING) Serial.println("[INFO] Wifi is connected."); - logEvent(LOG_WIFI_CONNECTED); - - if (SERIAL_LOGGING) Serial.print("[INFO] Wifi IP Address: "); - if (SERIAL_LOGGING) Serial.println(WiFi.localIP()); - - wifiState = WIFI_CONNECTED; - } - break; - case WIFI_CONNECTED: - if (WiFi.status() != WL_CONNECTED) { - if (SERIAL_LOGGING) Serial.println("[INFO] Wifi disconnected."); - logEvent(LOG_WIFI_DISCONNECTED); - wifiState = WIFI_DISCONNECTED; - } - break; - default: - if (SERIAL_LOGGING) Serial.println("[ERROR] Invalid wifi state."); - wifiState = WIFI_DISCONNECTED; - break; - } -} - -bool greenButton() { return digitalRead(GREEN_BUTTON_PIN) == BUTTON_CLOSED; } -bool redButton() { return digitalRead(RED_BUTTON_PIN) == BUTTON_CLOSED; } - -void relayOn() { digitalWrite(RELAY_PIN, RELAY_CLOSED); } -void relayOff() { digitalWrite(RELAY_PIN, RELAY_OPEN); } - -void processLockState() -{ - static uint16_t lockArmedTimeoutCount; - - if (lockState != LOCK_ARMED) lockArmedTimeoutCount = 0; - - switch (lockState) { - case LOCK_OFF: - if (LEDState != LED_ERROR) LEDState = LED_OFF; - - relayOff(); - break; - case LOCK_PREARM: - if (!greenButton() && !redButton()) { - if (SERIAL_LOGGING) Serial.println("[INFO] Arming interlock."); - logEvent(LOG_LOCK_ARMED); - lockState = LOCK_ARMED; - } else { - if (SERIAL_LOGGING) Serial.println("[ERROR] Buttons held, aborting."); - logEvent(LOG_LOCK_ERROR); - LEDState = LED_ERROR; - lockState = LOCK_OFF; - } - break; - case LOCK_ARMED: - if (LEDState != LED_ERROR) LEDState = LED_ARMED; - - relayOff(); - lockArmedTimeoutCount++; - - if (redButton()) { - if (SERIAL_LOGGING) Serial.println("[INFO] Unarming interlock."); - logEvent(LOG_LOCK_DISARM); - lockState = LOCK_OFF; - } else if (greenButton()) { - if (SERIAL_LOGGING) Serial.println("[INFO] On button pressed."); - lockState = LOCK_ON_PRESSED; - } - - if (lockArmedTimeoutCount > LOCK_ARMED_TIMEOUT) { - if (SERIAL_LOGGING) Serial.println("[INFO] Arming timed out, disarming."); - logEvent(LOG_LOCK_TIMEOUT); - lockState = LOCK_OFF; - LEDState = LED_ERROR; - } - break; - case LOCK_ON_PRESSED: - if (redButton()) { - if (SERIAL_LOGGING) Serial.println("[ERROR] Off button pressed, aborting."); - logEvent(LOG_LOCK_ERROR); - lockState = LOCK_OFF; - } else if (!greenButton()) { - if (SERIAL_LOGGING) Serial.println("[INFO] Turning machine on."); - logEvent(LOG_LOCK_ON); - lockState = LOCK_ON; - } - break; - case LOCK_ON: - if (LEDState != LED_ERROR) LEDState = LED_ON; - - relayOn(); - - if (redButton()) { - if (SERIAL_LOGGING) Serial.println("[INFO] Off button pressed."); - logEvent(LOG_LOCK_OFF); - lockState = LOCK_OFF; - } - break; - default: - if (SERIAL_LOGGING) Serial.println("[ERROR] Invalid lock state."); - lockState = LOCK_OFF; - break; - } -} - -void greenLEDOn() { digitalWrite(GREEN_LED_PIN, LED_PIN_ON); } -void greenLEDOff() { digitalWrite(GREEN_LED_PIN, LED_PIN_OFF); } -void redLEDOn() { digitalWrite(RED_LED_PIN, LED_PIN_ON); } -void redLEDOff() { digitalWrite(RED_LED_PIN, LED_PIN_OFF); } - -void processLEDState() -{ - static uint16_t LEDArmedBlinkCount, LEDErrorBlinkCount; - - if (LEDState != LED_ARMED) LEDArmedBlinkCount = 0; - if (LEDState != LED_ERROR) LEDErrorBlinkCount = 0; - - switch (LEDState) { - case LED_OFF: - greenLEDOff(); - redLEDOn(); - - break; - case LED_ARMED: - LEDArmedBlinkCount++; - - if (LEDArmedBlinkCount < LED_ARMED_BLINK_TIME) { - greenLEDOn(); - redLEDOn(); - } else if (LEDArmedBlinkCount < LED_ARMED_BLINK_TIME * 2) { - greenLEDOff(); - redLEDOn(); - } else { - LEDArmedBlinkCount = 0; - } - break; - case LED_ON: - greenLEDOn(); - redLEDOn(); - - break; - case LED_ERROR: - LEDErrorBlinkCount++; - - if (LEDErrorBlinkCount < LED_ERROR_BLINK_TIME) { - greenLEDOff(); - redLEDOff(); - } else if (LEDErrorBlinkCount < LED_ERROR_BLINK_TIME * 2) { - greenLEDOff(); - redLEDOn(); - } else { - LEDErrorBlinkCount = 0; - LEDState = LED_OFF; - } - break; - default: - if (SERIAL_LOGGING) Serial.println("[ERROR] Invalid LED state."); - LEDState = LED_OFF; - break; - } -} - -// JSON functions to prevent memory leaking see: -// https://arduinojson.org/v5/faq/i-found-a-memory-leak-in-the-library/ -String serializeLockJson(uint8_t lockState) -{ - // Generated with: https://arduinojson.org/assistant/ - const size_t bufferSize = JSON_OBJECT_SIZE(1) + 50; - StaticJsonBuffer jsonBuffer; - - JsonObject& root = jsonBuffer.createObject(); - root["lockState"] = (uint8_t) lockState; - String postData = String(); - root.printTo(postData); - - return postData; -} - -String deserializeLockJson(String input) -{ - // Generated with: https://arduinojson.org/assistant/ - const size_t bufferSize = JSON_OBJECT_SIZE(1) + 50; - StaticJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.parseObject(input); - - String action = root["action"]; - - return action; -} - -String serializeLog() { - size_t logLengthBytes = logPosition * sizeof(struct logData); - return "{\"log\": \"" + base64::encode((uint8_t *) eventLog, logLengthBytes, false) + "\"}"; -} - -void deserializeInfoJson(String input, uint8_t *processed, uint32_t *unixTime, String *version) -{ - // Generated with: https://arduinojson.org/assistant/ - const size_t bufferSize = JSON_OBJECT_SIZE(3) + 70; - StaticJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.parseObject(input); - - *processed = root["processed"]; - *unixTime = root["unixTime"]; - *version = root["version"].as(); -} - - -//TODO: abstract http functions - -void postState() -{ - // Don't log more than one error at a time - static bool logErrors = true; - - HTTPClient lockHTTP; - - //lockHTTP.begin("https://url", "7a 9c f4 db 40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS - lockHTTP.begin(SOCKET_URL + wifiMACAddr); - lockHTTP.addHeader("Content-Type", "application/json"); - - if (SERIAL_LOGGING) Serial.println("[INFO] Lock state HTTP begin."); - - if (SERIAL_LOGGING) Serial.print("[INFO] HTTP POST: "); - String postData = serializeLockJson(lockState); - if (SERIAL_LOGGING) Serial.println(postData); - int16_t lockHTTPCode = lockHTTP.POST(postData); - String lockHTTPCodeStr = String(lockHTTPCode); - - if (lockHTTPCode > 0) { - if (SERIAL_LOGGING) Serial.printf("[INFO] POST success, code: %d\n", lockHTTPCode); - - if (lockHTTPCode == HTTP_CODE_OK) { - logErrors = true; - - if (SERIAL_LOGGING) Serial.print("[INFO] Resource found, parsing response: "); - String lockPayload = lockHTTP.getString(); - if (SERIAL_LOGGING) Serial.println(lockPayload); - String action = deserializeLockJson(lockPayload); - - if (action == "arm" && lockState == LOCK_OFF && LEDState == LED_OFF) { - logEvent(LOG_COMM_LOCK_ARM); - lockState = LOCK_PREARM; - } else if (action == "disarm" && lockState != LOCK_ON) { - logEvent(LOG_COMM_LOCK_DISARM); - if (SERIAL_LOGGING) Serial.println("[INFO] Unarming interlock."); - logEvent(LOG_LOCK_DISARM); - lockState = LOCK_OFF; - } - - if (SERIAL_LOGGING) Serial.println("[INFO] action: " + action); - } else { - if (SERIAL_LOGGING) Serial.println("[ERROR] Resource not found."); - if (logErrors) logEvent(LOG_COMM_LOCK_FAIL, lockHTTPCodeStr.c_str(), lockHTTPCodeStr.length()); - logErrors = false; - } - } else { - if (SERIAL_LOGGING) Serial.printf("[ERROR] POST failed, error: %s\n", lockHTTP.errorToString(lockHTTPCode).c_str()); - if (logErrors) logEvent(LOG_COMM_LOCK_FAIL, lockHTTPCodeStr.c_str(), lockHTTPCodeStr.length()); - logErrors = false; - } - - lockHTTP.end(); -} - -void getCards() -{ - // Don't log more than one error at a time - static bool logErrors = true; - - HTTPClient cardHTTP; - - //cardHTTP.begin("https://url", "7a 9c f4 db 40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS - cardHTTP.begin(CARD_URL + wifiMACAddr + "/"); - cardHTTP.addHeader("Content-Type", "application/json"); - - if (SERIAL_LOGGING) Serial.println("[INFO] Card state HTTP begin."); - - if (SERIAL_LOGGING) Serial.println("[INFO] HTTP GET"); - int16_t cardHTTPCode = cardHTTP.GET(); - String cardHTTPCodeStr = String(cardHTTPCode); - - if (cardHTTPCode > 0) { - if (SERIAL_LOGGING) Serial.printf("[INFO] GET success, code: %d\n", cardHTTPCode); - - if (cardHTTPCode == HTTP_CODE_OK) { - logErrors = true; - - if (SERIAL_LOGGING) Serial.print("[INFO] Resource found, parsing response: "); - String cardPayload = cardHTTP.getString(); - cardPayload += String(EEPROM_END_MARKER); - if (SERIAL_LOGGING) Serial.println(cardPayload); - - noInterrupts(); // commit() disables interrupts, but we want an atomic EEPROM buffer write - for (int i = 0; i < cardPayload.length(); i++) { - if (i >= EEPROM_SIZE) break; - EEPROM.write(i, cardPayload.charAt(i)); - } - EEPROM.commit(); - interrupts(); - - if (SERIAL_LOGGING) Serial.println("[INFO] Finished getting card data."); - } else { - if (SERIAL_LOGGING) Serial.println("[ERROR] Resource not found."); - if (logErrors) logEvent(LOG_COMM_CARD_FAIL, cardHTTPCodeStr.c_str(), cardHTTPCodeStr.length()); - logErrors = false; - } - } else { - if (SERIAL_LOGGING) Serial.printf("[ERROR] POST failed, error: %s\n", cardHTTP.errorToString(cardHTTPCode).c_str()); - if (logErrors) logEvent(LOG_COMM_CARD_FAIL, cardHTTPCodeStr.c_str(), cardHTTPCodeStr.length()); - logErrors = false; - } - - cardHTTP.end(); -} - -void postInfolog() -{ - // Don't log more than one error at a time - static bool logErrors = true; - - HTTPClient infoHTTP; - - //infoHTTP.begin("https://url", "7a 9c f4 db 40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS - infoHTTP.begin(INFOLOG_URL + wifiMACAddr + "/"); - infoHTTP.addHeader("Content-Type", "application/json"); - - if (SERIAL_LOGGING) Serial.println("[INFO] Info state HTTP begin."); - - if (SERIAL_LOGGING) Serial.println("[INFO] HTTP POST."); - String postData = serializeLog(); - int16_t infoHTTPCode = infoHTTP.POST(postData); - String infoHTTPCodeStr = String(infoHTTPCode); - - if (infoHTTPCode > 0) { - if (SERIAL_LOGGING) Serial.printf("[INFO] POST success, code: %d\n", infoHTTPCode); - - if (infoHTTPCode == HTTP_CODE_OK) { - logErrors = true; - - if (SERIAL_LOGGING) Serial.print("[INFO] Resource found, parsing response: "); - String infoPayload = infoHTTP.getString(); - if (SERIAL_LOGGING) Serial.println(infoPayload); - uint8_t processed; - uint32_t unixTime; - String version = String(); - deserializeInfoJson(infoPayload, &processed, &unixTime, &version); - - struct timeval tv = { .tv_sec = unixTime, .tv_usec = 0 }; - struct timezone tz = { .tz_minuteswest = 0, .tz_dsttime = 0 }; - settimeofday(&tv, &tz); - - removeLogRecords(processed); - - if (version != FIRMWARE_VERSION && lockState == LOCK_OFF) { - noInterrupts(); - if (SERIAL_LOGGING) Serial.println("[INFO] Firmware out of date. Updating..."); - WiFiClient client; - int16_t update_response = ESPhttpUpdate.update(client, UPDATE_URL + wifiMACAddr + "/"); - interrupts(); - - if (SERIAL_LOGGING) printf("[ERROR] %s\n", ESPhttpUpdate.getLastErrorString().c_str()); - String lastErrorNum = String(ESPhttpUpdate.getLastError()); - logEvent(LOG_UPDATE_FAILED, lastErrorNum.c_str(), lastErrorNum.length()); - } - - if (SERIAL_LOGGING) Serial.print("[INFO] Set system time to: "); - if (SERIAL_LOGGING) Serial.println(unixTime); - } else { - if (SERIAL_LOGGING) Serial.println("[ERROR] Resource not found."); - if (logErrors) logEvent(LOG_COMM_INFO_FAIL, infoHTTPCodeStr.c_str(), infoHTTPCodeStr.length()); - logErrors = false; - } - } else { - if (SERIAL_LOGGING) Serial.printf("[ERROR] POST failed, error: %s\n", infoHTTP.errorToString(infoHTTPCode).c_str()); - if (logErrors) logEvent(LOG_COMM_INFO_FAIL, infoHTTPCodeStr.c_str(), infoHTTPCodeStr.length()); - logErrors = false; - } - - infoHTTP.end(); -} - -void processCommState() -{ - static uint16_t commLockIdleCount, commCardIdleCount, commInfoIdleCount; - - switch (commState) { - case COMM_INIT: - commLockIdleCount = 0; - commCardIdleCount = 0; - commInfoIdleCount = 0; - - commState = COMM_IDLE; - break; - case COMM_IDLE: - commLockIdleCount++; - commCardIdleCount++; - commInfoIdleCount++; - - if (commLockIdleCount >= COMM_LOCK_IDLE_TIME) { - commState = COMM_LOCK; - } else if (commCardIdleCount >= COMM_CARD_IDLE_TIME) { - commState = COMM_CARD; - } else if (commInfoIdleCount >= COMM_INFO_IDLE_TIME) { - commState = COMM_INFO; - } - break; - case COMM_LOCK: - { - postState(); - - commLockIdleCount = 0; - - commState = COMM_IDLE; - } - break; - case COMM_CARD: - { - getCards(); - - commCardIdleCount = 0; - - commState = COMM_IDLE; - } - break; - case COMM_INFO: - { - postInfolog(); - - commInfoIdleCount = 0; - - commState = COMM_IDLE; - } - break; - } -} diff --git a/firmware/leds.cpp b/firmware/leds.cpp new file mode 100644 index 0000000..468ef1b --- /dev/null +++ b/firmware/leds.cpp @@ -0,0 +1,53 @@ +#include "leds.h" + +void processLEDState() +{ + static uint16_t LEDArmedBlinkCount, LEDErrorBlinkCount; + + if (LEDState != LED_ARMED) LEDArmedBlinkCount = 0; + if (LEDState != LED_ERROR) LEDErrorBlinkCount = 0; + + switch (LEDState) { + case LED_OFF: + greenLEDOff(); + redLEDOn(); + + break; + case LED_ARMED: + LEDArmedBlinkCount++; + + if (LEDArmedBlinkCount < LED_ARMED_BLINK_TIME) { + greenLEDOn(); + redLEDOn(); + } else if (LEDArmedBlinkCount < LED_ARMED_BLINK_TIME * 2) { + greenLEDOff(); + redLEDOn(); + } else { + LEDArmedBlinkCount = 0; + } + break; + case LED_ON: + greenLEDOn(); + redLEDOn(); + + break; + case LED_ERROR: + LEDErrorBlinkCount++; + + if (LEDErrorBlinkCount < LED_ERROR_BLINK_TIME) { + greenLEDOff(); + redLEDOff(); + } else if (LEDErrorBlinkCount < LED_ERROR_BLINK_TIME * 2) { + greenLEDOff(); + redLEDOn(); + } else { + LEDErrorBlinkCount = 0; + LEDState = LED_OFF; + } + break; + default: + if (SERIAL_LOGGING) Serial.println("[ERROR] Invalid LED state."); + LEDState = LED_OFF; + break; + } +} diff --git a/firmware/leds.h b/firmware/leds.h new file mode 100644 index 0000000..94c9098 --- /dev/null +++ b/firmware/leds.h @@ -0,0 +1,22 @@ +#ifndef LEDS_H +#define LEDS_H + +#include "firmware.h" + +// times below are multiplied by DELAY_TIME, in ms +#define LED_ARMED_BLINK_TIME 50 +#define LED_ERROR_BLINK_TIME 50 + +enum LEDStates +{ + LED_OFF, + LED_ARMED, + LED_ON, + LED_ERROR, +}; + +extern enum LEDStates LEDState; + +void processLEDState(); + +#endif diff --git a/firmware/lock.cpp b/firmware/lock.cpp new file mode 100644 index 0000000..54f0131 --- /dev/null +++ b/firmware/lock.cpp @@ -0,0 +1,76 @@ +#include "lock.h" + +void processLockState() +{ + static uint16_t lockArmedTimeoutCount; + + if (lockState != LOCK_ARMED) lockArmedTimeoutCount = 0; + + switch (lockState) { + case LOCK_OFF: + if (LEDState != LED_ERROR) LEDState = LED_OFF; + + relayOff(); + break; + case LOCK_PREARM: + if (!greenButton() && !redButton()) { + if (SERIAL_LOGGING) Serial.println("[INFO] Arming interlock."); + logEvent(LOG_LOCK_ARMED); + lockState = LOCK_ARMED; + } else { + if (SERIAL_LOGGING) Serial.println("[ERROR] Buttons held, aborting."); + logEvent(LOG_LOCK_ERROR); + LEDState = LED_ERROR; + lockState = LOCK_OFF; + } + break; + case LOCK_ARMED: + if (LEDState != LED_ERROR) LEDState = LED_ARMED; + + relayOff(); + lockArmedTimeoutCount++; + + if (redButton()) { + if (SERIAL_LOGGING) Serial.println("[INFO] Unarming interlock."); + logEvent(LOG_LOCK_DISARM); + lockState = LOCK_OFF; + } else if (greenButton()) { + if (SERIAL_LOGGING) Serial.println("[INFO] On button pressed."); + lockState = LOCK_ON_PRESSED; + } + + if (lockArmedTimeoutCount > LOCK_ARMED_TIMEOUT) { + if (SERIAL_LOGGING) Serial.println("[INFO] Arming timed out, disarming."); + logEvent(LOG_LOCK_TIMEOUT); + lockState = LOCK_OFF; + LEDState = LED_ERROR; + } + break; + case LOCK_ON_PRESSED: + if (redButton()) { + if (SERIAL_LOGGING) Serial.println("[ERROR] Off button pressed, aborting."); + logEvent(LOG_LOCK_ERROR); + lockState = LOCK_OFF; + } else if (!greenButton()) { + if (SERIAL_LOGGING) Serial.println("[INFO] Turning machine on."); + logEvent(LOG_LOCK_ON); + lockState = LOCK_ON; + } + break; + case LOCK_ON: + if (LEDState != LED_ERROR) LEDState = LED_ON; + + relayOn(); + + if (redButton()) { + if (SERIAL_LOGGING) Serial.println("[INFO] Off button pressed."); + logEvent(LOG_LOCK_OFF); + lockState = LOCK_OFF; + } + break; + default: + if (SERIAL_LOGGING) Serial.println("[ERROR] Invalid lock state."); + lockState = LOCK_OFF; + break; + } +} diff --git a/firmware/lock.h b/firmware/lock.h new file mode 100644 index 0000000..e899ff7 --- /dev/null +++ b/firmware/lock.h @@ -0,0 +1,22 @@ +#ifndef LOCK_H +#define LOCK_H + +#include "firmware.h" + +// times below are multiplied by DELAY_TIME, in ms +#define LOCK_ARMED_TIMEOUT 1000 + +enum lockStates +{ + LOCK_OFF, + LOCK_PREARM, // prevent arming while buttons held + LOCK_ARMED, + LOCK_ON_PRESSED, // to wait until button is released + LOCK_ON, +}; + +extern enum lockStates lockState; + +void processLockState(); + +#endif diff --git a/firmware/logging.cpp b/firmware/logging.cpp new file mode 100644 index 0000000..b4fedff --- /dev/null +++ b/firmware/logging.cpp @@ -0,0 +1,39 @@ +#include "logging.h" + +void logEvent(uint8_t eventCode, const char *data, size_t num) +{ + struct logData event; + + noInterrupts(); + + event.unixTime = time(nullptr); + event.eventCode = eventCode; + + memset(event.data, 0, LOG_DATA_LENGTH); + for (uint8_t i = 0; i < LOG_DATA_LENGTH; i++) { + if (i >= num) break; + event.data[i] = data[i]; + } + + if (logPosition < LOG_SIZE) { + eventLog[logPosition++] = event; + } + + interrupts(); +} + +void removeLogRecords(uint8_t num) +{ + // shift records down by num because they've been sent + + if (num > logPosition) return; + + noInterrupts(); + + for (int i = 0; i < num; i++) { + eventLog[i] = eventLog[i + num]; + } + logPosition -= num; + + interrupts(); +} diff --git a/firmware/logging.h b/firmware/logging.h new file mode 100644 index 0000000..fb620e3 --- /dev/null +++ b/firmware/logging.h @@ -0,0 +1,47 @@ +#ifndef LOGGING_H +#define LOGGING_H + +#include "firmware.h" + +#define SERIAL_LOGGING true + +#define LOG_SIZE 90 // 100 blew up the stack +#define LOG_DATA_LENGTH 10 + +enum eventCodes +{ + LOG_BOOT_UP, + LOG_INIT_COMPLETE, + LOG_WIFI_CONNECTED, + LOG_WIFI_DISCONNECTED, + LOG_COMM_LOCK_ARM, + LOG_COMM_LOCK_DISARM, + LOG_COMM_LOCK_FAIL, + LOG_COMM_CARD_FAIL, + LOG_COMM_INFO_FAIL, + LOG_LOCK_OFF, + LOG_LOCK_ARMED, + LOG_LOCK_TIMEOUT, + LOG_LOCK_ON, + LOG_LOCK_DISARM, + LOG_LOCK_ERROR, + LOG_CARD_GOOD_READ, + LOG_CARD_ACCEPTED, + LOG_CARD_DENIED, + LOG_UPDATE_FAILED, + LOG_TEST, +}; + +struct __attribute__((packed)) logData { + uint32_t unixTime; + uint8_t eventCode; + char data[LOG_DATA_LENGTH]; +}; + +extern struct logData eventLog[LOG_SIZE]; +extern uint16_t logPosition; + +void logEvent(uint8_t eventCode, const char *data = nullptr, size_t num = 0); +void removeLogRecords(uint8_t num); + +#endif diff --git a/firmware/utils.cpp b/firmware/utils.cpp new file mode 100644 index 0000000..65276f7 --- /dev/null +++ b/firmware/utils.cpp @@ -0,0 +1,124 @@ +#include "utils.h" + +bool greenButton() { return digitalRead(GREEN_BUTTON_PIN) == BUTTON_CLOSED; } +bool redButton() { return digitalRead(RED_BUTTON_PIN) == BUTTON_CLOSED; } +void relayOn() { digitalWrite(RELAY_PIN, RELAY_CLOSED); } +void relayOff() { digitalWrite(RELAY_PIN, RELAY_OPEN); } +void greenLEDOn() { digitalWrite(GREEN_LED_PIN, LED_PIN_ON); } +void greenLEDOff() { digitalWrite(GREEN_LED_PIN, LED_PIN_OFF); } +void redLEDOn() { digitalWrite(RED_LED_PIN, LED_PIN_ON); } +void redLEDOff() { digitalWrite(RED_LED_PIN, LED_PIN_OFF); } + +int8_t charToNum(char input) +{ + return String("0123456789ABCDEF").indexOf(input); +} + +bool checksum(struct cardData *cardData) +{ + // checksum is each hex data byte xord'd together. + // each char is a hex nibble, so we have to work in pairs. + + int8_t even = 0, odd = 0; + + for (int8_t i = 0; i < CARD_DATA_LENGTH; i++) { + int8_t num = charToNum(cardData->data[i]); + + if (num == -1) return false; + if (i % 2 == 0) even ^= num; + if (i % 2 == 1) odd ^= num; + } + + int8_t checksum_even = charToNum(cardData->checksum[0]); + int8_t checksum_odd = charToNum(cardData->checksum[1]); + + if (even == checksum_even && odd == checksum_odd) { + return true; + } else { + return false; + } +} + +void checkCard() +{ + struct cardData *cardData = (struct cardData *) cardBuffer; + + if (cardData->head == CARD_HEAD_BYTE && + cardData->tail == CARD_TAIL_BYTE && + checksum(cardData)) { + + String cardStr = String(); + String authorizedCards = String(); + + for (uint8_t i = 0; i < CARD_DATA_LENGTH; i++) { + cardStr += cardData->data[i]; + } + + for (uint16_t i = 0; i < EEPROM_SIZE; i++) { + char tmp = EEPROM.read(i); + authorizedCards += tmp; + if (tmp == EEPROM_END_MARKER) break; + } + + if (SERIAL_LOGGING) Serial.println("[INFO] Good scan from card: " + cardStr); + logEvent(LOG_CARD_GOOD_READ, cardStr.c_str(), cardStr.length()); + + if (authorizedCards.indexOf(cardStr) >= 0) { + if (SERIAL_LOGGING) Serial.println("[INFO] Card is authorized on machine."); + if (lockState == LOCK_OFF) { + lockState = LOCK_PREARM; + } + logEvent(LOG_CARD_ACCEPTED, cardStr.c_str(), cardStr.length()); + } else { + if (SERIAL_LOGGING) Serial.println("[INFO] Card not authorized on machine."); + LEDState = LED_ERROR; + logEvent(LOG_CARD_DENIED, cardStr.c_str(), cardStr.length()); + } + } +} + +// JSON functions to prevent memory leaking see: +// https://arduinojson.org/v5/faq/i-found-a-memory-leak-in-the-library/ +String serializeLockJson(uint8_t lockState) +{ + // Generated with: https://arduinojson.org/assistant/ + const size_t bufferSize = JSON_OBJECT_SIZE(1) + 50; + StaticJsonBuffer jsonBuffer; + + JsonObject& root = jsonBuffer.createObject(); + root["lockState"] = (uint8_t) lockState; + String postData = String(); + root.printTo(postData); + + return postData; +} + +String deserializeLockJson(String input) +{ + // Generated with: https://arduinojson.org/assistant/ + const size_t bufferSize = JSON_OBJECT_SIZE(1) + 50; + StaticJsonBuffer jsonBuffer; + JsonObject& root = jsonBuffer.parseObject(input); + + String action = root["action"]; + + return action; +} + +String serializeLog() +{ + size_t logLengthBytes = logPosition * sizeof(struct logData); + return "{\"log\": \"" + base64::encode((uint8_t *) eventLog, logLengthBytes, false) + "\"}"; +} + +void deserializeInfoJson(String input, uint8_t *processed, uint32_t *unixTime, String *version) +{ + // Generated with: https://arduinojson.org/assistant/ + const size_t bufferSize = JSON_OBJECT_SIZE(3) + 70; + StaticJsonBuffer jsonBuffer; + JsonObject& root = jsonBuffer.parseObject(input); + + *processed = root["processed"]; + *unixTime = root["unixTime"]; + *version = root["version"].as(); +} diff --git a/firmware/utils.h b/firmware/utils.h new file mode 100644 index 0000000..0dead77 --- /dev/null +++ b/firmware/utils.h @@ -0,0 +1,38 @@ +#ifndef UTILS_H +#define UTILS_H + +#include "firmware.h" + +#define CARD_BUFFER_LENGTH 14 +#define CARD_DATA_LENGTH 10 +#define CARD_CHECK_LENGTH 2 +#define CARD_HEAD_BYTE 0x2 +#define CARD_TAIL_BYTE 0x3 +struct __attribute__((packed)) cardData { + char head; + char data[CARD_DATA_LENGTH]; + char checksum[CARD_CHECK_LENGTH]; + char tail; +}; + +extern char cardBuffer[CARD_BUFFER_LENGTH]; + +bool greenButton(); +bool redButton(); +void relayOn(); +void relayOff(); +void greenLEDOn(); +void greenLEDOff(); +void redLEDOn(); +void redLEDOff(); + +int8_t charToNum(char input); +bool checksum(struct cardData *cardData); +void checkCard(); + +String serializeLockJson(uint8_t lockState); +String deserializeLockJson(String input); +String serializeLog(); +void deserializeInfoJson(String input, uint8_t *processed, uint32_t *unixTime, String *version); + +#endif diff --git a/firmware/wifi.cpp b/firmware/wifi.cpp new file mode 100644 index 0000000..3e163eb --- /dev/null +++ b/firmware/wifi.cpp @@ -0,0 +1,40 @@ +#include "wifi.h" + +void processWifiState() +{ + switch(wifiState) { + case WIFI_DISCONNECTED: + commState = COMM_INIT; + + if (SERIAL_LOGGING) Serial.println("[INFO] Wifi attempting to connect..."); + + WiFi.begin(LOCKOUT_WIFI_SSID, LOCKOUT_WIFI_PASS); + + wifiState = WIFI_CONNECTING; + break; + case WIFI_CONNECTING: + commState = COMM_INIT; + + if (WiFi.status() == WL_CONNECTED) { + if (SERIAL_LOGGING) Serial.println("[INFO] Wifi is connected."); + logEvent(LOG_WIFI_CONNECTED); + + if (SERIAL_LOGGING) Serial.print("[INFO] Wifi IP Address: "); + if (SERIAL_LOGGING) Serial.println(WiFi.localIP()); + + wifiState = WIFI_CONNECTED; + } + break; + case WIFI_CONNECTED: + if (WiFi.status() != WL_CONNECTED) { + if (SERIAL_LOGGING) Serial.println("[INFO] Wifi disconnected."); + logEvent(LOG_WIFI_DISCONNECTED); + wifiState = WIFI_DISCONNECTED; + } + break; + default: + if (SERIAL_LOGGING) Serial.println("[ERROR] Invalid wifi state."); + wifiState = WIFI_DISCONNECTED; + break; + } +} diff --git a/firmware/wifi.h b/firmware/wifi.h new file mode 100644 index 0000000..2158103 --- /dev/null +++ b/firmware/wifi.h @@ -0,0 +1,19 @@ +#ifndef WIFI_H +#define WIFI_H + +#include "firmware.h" + +enum wifiStates +{ + WIFI_DISCONNECTED, + WIFI_CONNECTING, + WIFI_CONNECTED, +}; + +extern enum wifiStates wifiState; +extern const char *LOCKOUT_WIFI_SSID PROGMEM; +extern const char *LOCKOUT_WIFI_PASS PROGMEM; + +void processWifiState(); + +#endif