From a8331ab798bb68f9c46d8873f331acebb88b412b Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sat, 10 Nov 2018 14:56:50 -0700 Subject: [PATCH] Add RFID card scanning off a list of card numbers --- firmware/firmware.ino | 152 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 126 insertions(+), 26 deletions(-) diff --git a/firmware/firmware.ino b/firmware/firmware.ino index 1732972..e0cecaf 100644 --- a/firmware/firmware.ino +++ b/firmware/firmware.ino @@ -6,7 +6,22 @@ const char* WIFI_SSID = "Protospace"; const char* WIFI_PASS = "yycmakers"; char wifiMACAddr[18]; -const String API_ROUTE = String("http://tools-socket.protospace.ca/api/lockout/"); +const String SOCKET_URL = String("http://tools-socket.protospace.ca/api/lockout/"); +const String CARD_URL = String("http://tools-auth.protospace.ca/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; + +String authorizedCards = String("00000C5123,00000D1234,00000C5999,00000C5998,00000C5666"); #define RELAY_PIN D1 #define GREEN_BUTTON_PIN D3 @@ -23,8 +38,10 @@ const String API_ROUTE = String("http://tools-socket.protospace.ca/api/lockout/" #define LOGGING true -#define DELAY_TIME 10 -#define COMM_IDLE_TIME 500 / DELAY_TIME +#define ONE_SECOND 1000 +#define DELAY_TIME ONE_SECOND / 100 +#define COMM_LOCK_IDLE_TIME ONE_SECOND / 2 +#define COMM_CARD_IDLE_TIME ONE_SECOND * 60 * 5 enum wifiStates { @@ -38,7 +55,7 @@ enum lockStates LOCK_OFF, LOCK_PREARM, LOCK_ARMED, - LOCK_ON_PRESSED, + LOCK_ON_PRESSED, // to wait until button is released LOCK_ON, } lockState = LOCK_OFF; @@ -46,12 +63,13 @@ enum commStates { COMM_INIT, COMM_IDLE, - COMM_SEND, + COMM_GET_LOCK, + COMM_GET_CARD, } commState = COMM_INIT; void setup() { - if (LOGGING) Serial.begin(115200); + Serial.begin(9600); if (LOGGING) Serial.println("[INFO] Serial started."); pinMode(RELAY_PIN, OUTPUT); @@ -67,14 +85,88 @@ void loop() processLockState(); processCommState(); + 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; + } + } + } + delay(DELAY_TIME); } +int8_t charToNum(char input) { + return String("0123456789ABCDEF").indexOf(input); +} + +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; + } +} + +void checkCard() { + cardData_t *cardData = (cardData_t *) cardBuffer; + + if (cardData->head == 0x2 && cardData->tail == 0x3 && checksum(cardData)) { + String cardStr = String(); + + for (int i = 0; i < CARD_DATA_LENGTH; i++) + cardStr += cardData->data[i]; + + 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 { + if (LOGGING) Serial.println("[INFO] Card not authorized or machine."); + } + } +} + void processWifiState() { switch(wifiState) { case WIFI_DISCONNECTED: - lockState = LOCK_OFF; commState = COMM_INIT; if (LOGGING) Serial.println("[INFO] Wifi is disconnected. Attempting to connect..."); @@ -83,7 +175,6 @@ void processWifiState() wifiState = WIFI_CONNECTING; break; case WIFI_CONNECTING: - lockState = LOCK_OFF; commState = COMM_INIT; if (WiFi.status() == WL_CONNECTED) { @@ -148,7 +239,7 @@ void processLockState() if (redButton()) { if (LOGGING) Serial.println("[INFO] Unarming interlock."); - lockState = LOCK_OFF_PRESSED; + lockState = LOCK_OFF; } else if (greenButton()) { if (LOGGING) Serial.println("[INFO] On button pressed."); lockState = LOCK_ON_PRESSED; @@ -157,7 +248,7 @@ void processLockState() case LOCK_ON_PRESSED: if (redButton()) { if (LOGGING) Serial.println("[ERROR] Off button pressed, aborting."); - lockState = LOCK_OFF_PRESSED; + lockState = LOCK_OFF; } else if (!greenButton()) { if (LOGGING) Serial.println("[INFO] Turning machine on."); lockState = LOCK_ON; @@ -182,20 +273,20 @@ void processLockState() } // JSON functions to prevent memory leaking -String serializeJson(int lockState) { +String serializeLockJson(uint8_t 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; + root["lockState"] = (uint8_t) lockState; String postData = String(); root.printTo(postData); return postData; } -String deserializeJson(String input) { +String deserializeLockJson(String input) { // Generated with: https://arduinojson.org/assistant/ const size_t bufferSize = JSON_OBJECT_SIZE(1) + 50; DynamicJsonBuffer jsonBuffer(bufferSize); @@ -208,30 +299,37 @@ String deserializeJson(String input) { void processCommState() { - static int commIdleCount = 0; + static uint16_t commLockIdleCount = 0; + static uint16_t commCardIdleCount = 0; switch (commState) { case COMM_INIT: - commIdleCount = 0; + commLockIdleCount = 0; + commCardIdleCount = 0; + commState = COMM_IDLE; break; case COMM_IDLE: - commIdleCount++; - if (commIdleCount >= COMM_IDLE_TIME) { - commState = COMM_SEND; + commLockIdleCount++; + commCardIdleCount++; + + if (commLockIdleCount >= COMM_LOCK_IDLE_TIME / DELAY_TIME) { + commState = COMM_GET_LOCK; + } else if (commCardIdleCount >= COMM_CARD_IDLE_TIME / DELAY_TIME) { + commState = COMM_GET_CARD; } break; - case COMM_SEND: - if (LOGGING) Serial.println("[INFO] HTTP begin."); + case COMM_GET_LOCK: + if (LOGGING) Serial.println("[INFO] Lock state 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.begin(SOCKET_URL + wifiMACAddr); http.addHeader("Content-Type", "application/json"); if (LOGGING) Serial.print("[INFO] HTTP POST: "); - String postData = serializeJson(lockState); + String postData = serializeLockJson(lockState); if (LOGGING) Serial.println(postData); - int httpCode = http.POST(postData); + uint16_t httpCode = http.POST(postData); if (httpCode > 0) { if (LOGGING) Serial.printf("[INFO] POST success, code: %d\n", httpCode); @@ -240,10 +338,10 @@ void processCommState() if (LOGGING) Serial.print("[INFO] Resource found, parsing response: "); String payload = http.getString(); if (LOGGING) Serial.println(payload); - String action = deserializeJson(payload); + String action = deserializeLockJson(payload); if (action == "arm" && lockState == LOCK_OFF) { - lockState = LOCK_PREARMED; + lockState = LOCK_PREARM; } else if (action == "disarm") { lockState = LOCK_OFF; } @@ -256,7 +354,9 @@ void processCommState() if (LOGGING) Serial.printf("[ERROR] POST failed, error: %s\n", http.errorToString(httpCode).c_str()); } - commState = COMM_INIT; + commLockIdleCount = 0; + + commState = COMM_IDLE; break; } }