pslockout/firmware/firmware.ino

420 lines
10 KiB
Arduino
Raw Normal View History

2018-02-02 06:46:44 +00:00
#include <Arduino.h>
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
2018-11-14 02:31:53 +00:00
#include <EEPROM.h>
2018-02-02 06:46:44 +00:00
2018-11-14 02:31:53 +00:00
const char* WIFI_SSID = "tanner";
const char* WIFI_PASS = "point cloud truck dust swim army";
2018-02-06 02:31:54 +00:00
char wifiMACAddr[18];
2018-11-14 02:31:53 +00:00
const String SOCKET_URL = String("http://192.168.1.9:8080/api/lockout/");
const String CARD_URL = String("http://192.168.1.9:8000/cards/");
#define CARD_BUFFER_LENGTH 14
char cardBuffer[CARD_BUFFER_LENGTH];
#define CARD_DATA_LENGTH 10
#define CARD_CHECK_LENGTH 2
typedef struct __attribute__((packed)) cardData {
char head;
char data[CARD_DATA_LENGTH];
char checksum[CARD_CHECK_LENGTH];
char tail;
} cardData_t;
2018-11-14 02:31:53 +00:00
#define LOGGING true
2018-02-02 06:46:44 +00:00
#define RELAY_PIN D1
#define GREEN_BUTTON_PIN D3
#define RED_BUTTON_PIN D4
#define GREEN_LED_PIN D6
#define RED_LED_PIN D7
2018-02-02 06:46:44 +00:00
#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
2018-02-02 06:46:44 +00:00
2018-11-14 02:31:53 +00:00
#define DELAY_TIME 10
#define COMM_LOCK_IDLE_TIME 50
#define COMM_CARD_IDLE_TIME 200
2018-02-02 06:46:44 +00:00
2018-11-14 02:31:53 +00:00
#define EEPROM_SIZE 4095
#define EEPROM_START 0
2018-02-02 06:46:44 +00:00
2018-02-06 02:31:54 +00:00
enum wifiStates
{
WIFI_DISCONNECTED,
WIFI_CONNECTING,
WIFI_CONNECTED,
} wifiState = WIFI_DISCONNECTED;
enum lockStates
{
LOCK_OFF,
2018-11-08 02:09:34 +00:00
LOCK_PREARM,
2018-02-06 02:31:54 +00:00
LOCK_ARMED,
LOCK_ON_PRESSED, // to wait until button is released
2018-02-06 02:31:54 +00:00
LOCK_ON,
} lockState = LOCK_OFF;
enum commStates
{
COMM_INIT,
COMM_IDLE,
2018-11-14 02:31:53 +00:00
COMM_LOCK,
COMM_CARD,
2018-02-06 02:31:54 +00:00
} commState = COMM_INIT;
void setup()
{
Serial.begin(9600);
2018-02-06 02:31:54 +00:00
if (LOGGING) Serial.println("[INFO] Serial started.");
pinMode(RELAY_PIN, OUTPUT);
pinMode(GREEN_BUTTON_PIN, INPUT_PULLUP);
pinMode(RED_BUTTON_PIN, INPUT_PULLUP);
pinMode(GREEN_LED_PIN, OUTPUT);
pinMode(RED_LED_PIN, OUTPUT);
2018-11-14 02:31:53 +00:00
EEPROM.begin(EEPROM_SIZE);
2018-02-02 06:46:44 +00:00
}
2018-02-06 02:31:54 +00:00
void loop()
{
processWifiState();
processLockState();
processCommState();
2018-02-02 06:46:44 +00:00
if (Serial.available() >= CARD_BUFFER_LENGTH) {
uint8_t bufPos = 0;
while (true) {
char readChar = Serial.read();
if (readChar == -1) {
break;
} else if (readChar == 0x2) {
bufPos = 0;
}
if (bufPos >= CARD_BUFFER_LENGTH) {
break;
}
cardBuffer[bufPos++] = readChar;
if (readChar == 0x3 && bufPos == CARD_BUFFER_LENGTH) {
checkCard();
break;
}
}
}
2018-02-06 02:31:54 +00:00
delay(DELAY_TIME);
}
2018-02-02 06:46:44 +00:00
2018-11-14 02:31:53 +00:00
int8_t charToNum(char input)
{
return String("0123456789ABCDEF").indexOf(input);
}
2018-11-14 02:31:53 +00:00
bool checksum(cardData_t *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 (int 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;
}
}
2018-11-14 02:31:53 +00:00
void checkCard()
{
cardData_t *cardData = (cardData_t *) cardBuffer;
if (cardData->head == 0x2 && cardData->tail == 0x3 && checksum(cardData)) {
String cardStr = String();
2018-11-14 02:31:53 +00:00
String authorizedCards = String();
2018-11-14 02:31:53 +00:00
for (int i = 0; i < CARD_DATA_LENGTH; i++) {
cardStr += cardData->data[i];
2018-11-14 02:31:53 +00:00
}
for (int i = EEPROM_START; i < EEPROM_SIZE; i++) {
char tmp = EEPROM.read(i);
authorizedCards += tmp;
if (tmp == '$') break;
}
if (LOGGING) Serial.println("[INFO] Good scan from card: " + cardStr);
if (authorizedCards.indexOf(cardStr) >= 0) {
if (LOGGING) Serial.println("[INFO] Card is authorized on machine.");
if (lockState == LOCK_OFF) {
lockState = LOCK_PREARM;
}
} else {
2018-11-14 02:31:53 +00:00
if (LOGGING) Serial.println("[INFO] Card not authorized on machine.");
}
}
}
2018-02-06 02:31:54 +00:00
void processWifiState()
{
switch(wifiState) {
case WIFI_DISCONNECTED:
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:
commState = COMM_INIT;
if (WiFi.status() == WL_CONNECTED) {
if (LOGGING) Serial.println("[INFO] Wifi is connected.");
2018-02-06 06:59:47 +00:00
if (LOGGING) Serial.print("[INFO] Wifi IP Address: ");
2018-02-06 02:31:54 +00:00
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]);
2018-02-06 06:59:47 +00:00
if (LOGGING) Serial.print("[INFO] Wifi MAC Address: ");
2018-02-06 02:31:54 +00:00
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;
}
}
2018-02-02 06:46:44 +00:00
2018-11-08 02:09:34 +00:00
bool greenButton() { return digitalRead(GREEN_BUTTON_PIN) == BUTTON_CLOSED; }
bool redButton() { return digitalRead(RED_BUTTON_PIN) == BUTTON_CLOSED; }
void greenLEDOn() { digitalWrite(GREEN_LED_PIN, LED_ON); }
void greenLEDOff() { digitalWrite(GREEN_LED_PIN, LED_OFF); }
void redLEDOn() { digitalWrite(RED_LED_PIN, LED_ON); }
void redLEDOff() { digitalWrite(RED_LED_PIN, LED_OFF); }
2018-11-08 02:14:59 +00:00
void relayOn() { digitalWrite(RELAY_PIN, RELAY_CLOSED); }
void relayOff() { digitalWrite(RELAY_PIN, RELAY_OPEN); }
2018-02-06 02:31:54 +00:00
void processLockState()
{
switch (lockState) {
case LOCK_OFF:
2018-11-08 02:11:41 +00:00
greenLEDOff();
2018-11-11 00:25:17 +00:00
redLEDOff();
2018-02-06 02:31:54 +00:00
2018-11-08 02:14:59 +00:00
relayOff();
2018-02-06 02:31:54 +00:00
break;
2018-11-08 02:09:34 +00:00
case LOCK_PREARM:
if (!greenButton() && !redButton()) {
if (LOGGING) Serial.println("[INFO] Arming interlock.");
lockState = LOCK_ARMED;
} else {
lockState = LOCK_OFF;
}
break;
2018-02-06 02:31:54 +00:00
case LOCK_ARMED:
2018-11-11 00:25:17 +00:00
greenLEDOn();
redLEDOff();
2018-02-06 02:31:54 +00:00
2018-11-08 02:14:59 +00:00
relayOff();
2018-02-06 02:31:54 +00:00
2018-11-08 02:09:34 +00:00
if (redButton()) {
2018-02-06 02:31:54 +00:00
if (LOGGING) Serial.println("[INFO] Unarming interlock.");
lockState = LOCK_OFF;
2018-11-08 02:09:34 +00:00
} else if (greenButton()) {
2018-02-06 02:31:54 +00:00
if (LOGGING) Serial.println("[INFO] On button pressed.");
lockState = LOCK_ON_PRESSED;
}
break;
case LOCK_ON_PRESSED:
2018-11-08 02:09:34 +00:00
if (redButton()) {
if (LOGGING) Serial.println("[ERROR] Off button pressed, aborting.");
lockState = LOCK_OFF;
2018-11-08 02:09:34 +00:00
} else if (!greenButton()) {
2018-02-06 02:31:54 +00:00
if (LOGGING) Serial.println("[INFO] Turning machine on.");
lockState = LOCK_ON;
}
break;
case LOCK_ON:
2018-11-11 00:25:17 +00:00
greenLEDOff();
redLEDOn();
2018-02-06 02:31:54 +00:00
2018-11-08 02:14:59 +00:00
relayOn();
2018-02-06 02:31:54 +00:00
2018-11-08 02:09:34 +00:00
if (redButton()) {
2018-02-06 02:31:54 +00:00
if (LOGGING) Serial.println("[INFO] Off button pressed.");
lockState = LOCK_OFF;
}
break;
default:
if (LOGGING) Serial.println("[ERROR] Invalid lock state.");
lockState = LOCK_OFF;
break;
}
2018-02-02 06:46:44 +00:00
}
// JSON functions to prevent memory leaking
2018-11-14 02:31:53 +00:00
String serializeLockJson(uint8_t lockState)
{
2018-02-06 06:59:47 +00:00
// Generated with: https://arduinojson.org/assistant/
const size_t bufferSize = JSON_OBJECT_SIZE(1) + 50;
DynamicJsonBuffer jsonBuffer(bufferSize);
JsonObject& root = jsonBuffer.createObject();
root["lockState"] = (uint8_t) lockState;
2018-02-06 06:59:47 +00:00
String postData = String();
root.printTo(postData);
2018-02-06 06:59:47 +00:00
return postData;
}
2018-11-14 02:31:53 +00:00
String deserializeLockJson(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;
}
2018-02-06 06:59:47 +00:00
2018-02-06 02:31:54 +00:00
void processCommState()
{
static uint16_t commLockIdleCount = 0;
static uint16_t commCardIdleCount = 0;
2018-02-06 02:31:54 +00:00
switch (commState) {
case COMM_INIT:
commLockIdleCount = 0;
commCardIdleCount = 0;
2018-02-06 02:31:54 +00:00
commState = COMM_IDLE;
break;
case COMM_IDLE:
commLockIdleCount++;
commCardIdleCount++;
2018-11-14 02:31:53 +00:00
if (commLockIdleCount >= COMM_LOCK_IDLE_TIME) {
commState = COMM_LOCK;
} else if (commCardIdleCount >= COMM_CARD_IDLE_TIME) {
commState = COMM_CARD;
2018-02-06 02:31:54 +00:00
}
break;
2018-11-14 02:31:53 +00:00
case COMM_LOCK:
{
if (LOGGING) Serial.println("[INFO] Lock state HTTP begin.");
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 (LOGGING) Serial.print("[INFO] HTTP POST: ");
String postData = serializeLockJson(lockState);
if (LOGGING) Serial.println(postData);
uint16_t lockHTTPCode = lockHTTP.POST(postData);
if (lockHTTPCode > 0) {
if (LOGGING) Serial.printf("[INFO] POST success, code: %d\n", lockHTTPCode);
if (lockHTTPCode == HTTP_CODE_OK) {
if (LOGGING) Serial.print("[INFO] Resource found, parsing response: ");
String lockPayload = lockHTTP.getString();
if (LOGGING) Serial.println(lockPayload);
String action = deserializeLockJson(lockPayload);
if (action == "arm" && lockState == LOCK_OFF) {
lockState = LOCK_PREARM;
} else if (action == "disarm") {
lockState = LOCK_OFF;
}
if (LOGGING) Serial.println("[INFO] action: " + action);
} else {
if (LOGGING) Serial.println("[ERROR] Resource not found.");
2018-02-06 06:59:47 +00:00
}
2018-02-06 02:31:54 +00:00
} else {
2018-11-14 02:31:53 +00:00
if (LOGGING) Serial.printf("[ERROR] POST failed, error: %s\n", lockHTTP.errorToString(lockHTTPCode).c_str());
2018-02-06 02:31:54 +00:00
}
2018-11-14 02:31:53 +00:00
commLockIdleCount = 0;
commState = COMM_IDLE;
2018-02-06 02:31:54 +00:00
}
2018-11-14 02:31:53 +00:00
break;
case COMM_CARD:
{
if (LOGGING) Serial.println("[INFO] Card state HTTP begin.");
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 (LOGGING) Serial.println("[INFO] HTTP GET");
uint16_t cardHTTPCode = cardHTTP.GET();
if (cardHTTPCode > 0) {
if (LOGGING) Serial.printf("[INFO] GET success, code: %d\n", cardHTTPCode);
if (cardHTTPCode == HTTP_CODE_OK) {
if (LOGGING) Serial.print("[INFO] Resource found, parsing response: ");
String cardPayload = cardHTTP.getString();
cardPayload += "$"; // Mark the end
if (LOGGING) Serial.println(cardPayload);
for (int i = EEPROM_START; i < cardPayload.length(); i++) {
if (i >= EEPROM_SIZE) break;
EEPROM.write(i, cardPayload.charAt(i));
}
EEPROM.commit();
if (LOGGING) Serial.println("[INFO] Finished getting card data.");
} else {
if (LOGGING) Serial.println("[ERROR] Resource not found.");
}
} else {
if (LOGGING) Serial.printf("[ERROR] POST failed, error: %s\n", cardHTTP.errorToString(cardHTTPCode).c_str());
}
2018-02-06 02:31:54 +00:00
2018-11-14 02:31:53 +00:00
commCardIdleCount = 0;
2018-11-14 02:31:53 +00:00
commState = COMM_IDLE;
}
2018-02-06 02:31:54 +00:00
break;
}
}