From: Justin Mitchell Date: Tue, 29 Mar 2016 13:49:49 +0000 (+0100) Subject: Initial basic door lock code, reads from textfile on flash drive X-Git-Url: http://stoneship.org.uk/projects/git/?a=commitdiff_plain;h=bb5fbbec51005e218f9986387fbec486cbb5ecee;p=doorlock_v1.git Initial basic door lock code, reads from textfile on flash drive --- bb5fbbec51005e218f9986387fbec486cbb5ecee diff --git a/client.cpp b/client.cpp new file mode 100644 index 0000000..44269a9 --- /dev/null +++ b/client.cpp @@ -0,0 +1,45 @@ +#include + +#include "config.h" + +static String http_get(const String url) +{ + WiFiClient client; + if (!client.connect(AUTH_HOST, 80)) { + Serial.println("connection failed"); + return ""; + } + + client.print(String("GET ") + url + " HTTP/1.1\r\n" + + "Host: " + AUTH_HOST + "\r\n" + + "Connection: close\r\n" + + "\r\n"); + delay(10); + + String answer; + while (client.available()) { + String line = client.readStringUntil('\r'); + answer += line; + } + return answer; +} + +bool log_attempt(const char *uid, bool success) +{ + String url = AUTH_URL; + url += "?device="; + url += DEVICE_ID; + url += "&uid="; + url += uid; + url += "&action=access"; + if (success) { + url += "&success=true"; + } else { + url += "&success=false"; + } + + String answer = http_get(url); + if (answer == "") return false; + return true; +} + diff --git a/client.h b/client.h new file mode 100644 index 0000000..edfdac8 --- /dev/null +++ b/client.h @@ -0,0 +1,2 @@ +bool log_attempt(const char *uid, bool success); + diff --git a/config.h b/config.h new file mode 100644 index 0000000..4a1fbb2 --- /dev/null +++ b/config.h @@ -0,0 +1,34 @@ +// WiFi network to use for connections +#define WIFI_SSID "Hackspace" +#define WIFI_PASS "electronics" + +// This devices unique ID and shared auth secret +#define DEVICE_ID "frontdoor" +#define SECRET "abcdef" + +// URL of the API +#define AUTH_HOST "swansea.hackspace.org.uk" +#define AUTH_URL "/auth/log.php" + +// MFRC522 Pins +#define RST_PIN 5 // MFRC522 Reset Pin +#define SS_PIN 15 // MFRC522 Select Pin + +#define LED_PIN 2 + +#define LOCK_PIN 16 +#define LOCK_TIME 3000 // unlock for 3 seconds + +// how long to try to connect the wifi +#define WIFI_COUNT 20 +#define WIFI_DELAY 250 // 20 x 250mS == 5s + +// how often to retry the connection +#define WIFI_RETRY 60000 + +// filename on SPIFFS that stores key list +#define KEYS_FILE "/keys" + + + +#undef DEBUG diff --git a/data/keys b/data/keys new file mode 100644 index 0000000..6a02ba9 --- /dev/null +++ b/data/keys @@ -0,0 +1,17 @@ +4:A0000000 +4:A0000001 +4:A0000002 +4:A0000003 +4:A0000004 +4:A0000005 +4:A0000006 +4:A0000007 +4:A0000008 +4:A0000009 +4:A000000A +4:A000000B +4:A000000C +4:A000000D +4:A000000E +4:A000000F +4:87FA2E0B diff --git a/doorlock_v1.ino b/doorlock_v1.ino new file mode 100644 index 0000000..40b53b3 --- /dev/null +++ b/doorlock_v1.ino @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include + + +#include "config.h" +#include "tag_handler.h" +#include "client.h" + +MFRC522 mfrc522(SS_PIN, RST_PIN); + +Adafruit_NeoPixel led = Adafruit_NeoPixel(9, LED_PIN, NEO_GRB + NEO_KHZ800); + +// when did we last try to connect to wifi +unsigned long last_try = 0; + +// is the wifi up and connected +bool wifi_connected() +{ + if (WiFi.status() == WL_CONNECTED) return true; + return false; +} + +// try and connect us, but give up after a while +bool wifi_connect() +{ + int count = 0; + int pulse = 0; + int rate = 32; + + if (WiFi.status() == WL_CONNECTED) return true; + + + Serial.print(F("WiFi Connecting to ")); + Serial.println(WIFI_SSID); + + WiFi.begin(WIFI_SSID, WIFI_PASS); + while (WiFi.status() != WL_CONNECTED && count < WIFI_COUNT) { + Serial.print("."); + led.setPixelColor(0, 0, 0, pulse); + led.show(); + pulse += rate; + if (pulse <= 0 || pulse >= 192) rate=-rate; + delay(WIFI_DELAY); + count++; + } + if (WiFi.status() == WL_CONNECTED) { + Serial.print(F("Connected. IP=")); + Serial.println(WiFi.localIP()); + return true; + } + Serial.println(F("Wifi connect failed.")); + return false; +} + +// unrecoverable error state +// pulse red +void error_loop() +{ + int pulse = 0; + int rate = 8; + while (1) { + led.setPixelColor(0, pulse, 0, 0); + led.show(); + pulse += rate; + if (pulse <= 0 || pulse >= 256) rate=-rate; + delay(50); + } +} + + +// Lock is idle, colour indicates online or not +void led_status(bool has_wifi) +{ + // must be neatly divisible to work + static int pulse = 32; + static int rate = -2; + + if (has_wifi) { + led.setPixelColor(0, pulse, pulse, pulse); + } else { + led.setPixelColor(0, pulse, pulse, 0); + } + led.show(); + + pulse += rate; + if (pulse == 0 || pulse == 32) rate=-rate; +} + +void setup() { + SPI.begin(); + + Serial.begin(9600); + Serial.println(F("\n\nRFID Doorlock. v1")); + + pinMode(LOCK_PIN, OUTPUT); + digitalWrite(LOCK_PIN, LOW); + + // Enable and clear LEDs + pinMode(LED_PIN, OUTPUT); + led.begin(); + led.setBrightness(64); + led.show(); + + // Test comms with the RFID board + mfrc522.PCD_Init(); + byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg); + + if (v == 0) { + Serial.println(F("RFID Init failed")); + error_loop(); + } + Serial.print(F("MFRC Rev 0x")); + Serial.println(v, HEX); + + // connect and indicate + led_status( wifi_connect() ); + last_try = millis(); + + // check if we have a keyfile + SPIFFS.begin(); + + if (SPIFFS.exists(KEYS_FILE)) { + Serial.print(F("Found keys file ")); + Serial.println(KEYS_FILE); + } else { + Serial.print(F("Keys file ")); + Serial.print(KEYS_FILE); + Serial.println(F(" not found")); + } + + + ESP.wdtEnable(5000); +} + +void open_lock() +{ + led.setPixelColor(0, 0, 255, 0); + led.show(); + digitalWrite(LOCK_PIN, HIGH); + delay(3000); + digitalWrite(LOCK_PIN, LOW); + led.setPixelColor(0, 0, 0, 0); + led.show(); +} + +void fail_lock() +{ + led.setPixelColor(0, 255, 0, 0); + led.show(); + delay(3000); + led.setPixelColor(0, 0, 0, 0); + led.show(); + +} + +void loop() { + MFRC522::Uid uid; + int count = 0; + bool success = false; + + ESP.wdtFeed(); + while (uid_fetch(mfrc522, uid, count)) { + Serial.print("Card: "); + Serial.print(uid_print(uid)); + + if (uid_valid(uid)) { + Serial.println(" MATCH"); + open_lock(); + success = true; + } else { + Serial.println(" FAILED"); + fail_lock(); + } + + // wifi is up, report it + if (wifi_connected()) { + log_attempt(uid_print(uid), success); + } + } + + led_status( wifi_connected() ); + + // wifi isnt connected, time to try again ? + if (!wifi_connected()) { + unsigned int now = millis(); + if ( last_try + WIFI_RETRY < now) { + Serial.print("now="); Serial.print(now); + Serial.print(" last="); Serial.print(last_try); + Serial.print(" then="); Serial.println(last_try + WIFI_RETRY); + led_status( wifi_connect() ); + last_try = now; + } + } + + // dont spin too fast + delay(100); + +} diff --git a/tag_handler.cpp b/tag_handler.cpp new file mode 100644 index 0000000..1bf95af --- /dev/null +++ b/tag_handler.cpp @@ -0,0 +1,257 @@ +#include +#include +#include "config.h" + +typedef uint8_t byte_t; + +static const char * hex = "0123456789ABCDEF"; + + +int hexto(char c) +{ + if (c >= '0' && c <='9') return c - '0'; + if (c >= 'a' && c <= 'f') return 10 + (c - 'a'); + if (c >= 'A' && c <= 'F') return 10 + (c - 'A'); +} + +static const char * cardid(const byte_t uid[], byte_t len) +{ + static char text[21]; + if (len > 10) return NULL; + + char * p = text; + *(p++) = hex[ len ]; //DEBUG + *(p++) = ':'; //DEBUG + for (int i=0; i> 4 ]; + *(p++) = hex[ uid[i] & 15 ]; + } + *p = 0; + return text; +} + +const char * uid_print(const MFRC522::Uid uid) +{ + return cardid(uid.uidByte, uid.size); +} + +void uid_copy(MFRC522::Uid &a, const MFRC522::Uid b) +{ + a.size = b.size; + memcpy(a.uidByte, b.uidByte, b.size); +} + +bool uid_compare(const MFRC522::Uid a, const MFRC522::Uid b) +{ + if (a.size != b.size) return 0; + if (a.size == 0 || b.size == 0) return 0; + return memcmp(a.uidByte, b.uidByte, a.size)==0?true:false; +} + +void uid_zero(MFRC522::Uid &uid) +{ + uid.size = 0; +} + +/* is this card id permitted */ +bool uid_auth(const MFRC522::Uid uid) +{ + // TODO: everything + // return card_auth(cardid(uid.uidByte, uid.size)); + return true; +} + +bool uid_isset(const MFRC522::Uid uid) +{ + return (uid.size!=0); +} + +bool uid_visible(MFRC522 &reader, const MFRC522::Uid master) +{ + MFRC522::Uid uid; + + // no tag given + if (master.size == 0) return false; + + byte bufferATQA[2]; + byte bufferSize = sizeof(bufferATQA); + byte status = 0; + + SPI.begin(); + + // no tags in range to wakeup + if (reader.PICC_WakeupA(bufferATQA, &bufferSize) == MFRC522::STATUS_TIMEOUT) { + return false; + } + + bool found = false; + + uid_copy(uid, master); + if (reader.PICC_Select(&uid, uid.size * 8) == MFRC522::STATUS_OK) { + // something answered, but was it the right one + if (uid_compare(uid, master)) { + // yes, match + found=true; + } else { + // uid did not match + } + } else { + // nothing answered + } + reader.PICC_HaltA(); + return found; +} + + +bool uid_fetch(MFRC522 &reader, MFRC522::Uid &uid, int count) +{ + SPI.begin(); + + byte bufferATQA[2]; + byte bufferSize = sizeof(bufferATQA); + byte status = 0; + + if (count == 0) { + status = reader.PICC_WakeupA(bufferATQA, &bufferSize); + } else { + status = reader.PICC_RequestA(bufferATQA, &bufferSize); + } + + uid_zero(uid); + if (status != MFRC522::STATUS_OK && status != MFRC522::STATUS_COLLISION) { + if (status == MFRC522::STATUS_TIMEOUT) { + // Nobody listening + return false; + } + } + + bool result = false; + if ( (status = reader.PICC_Select(&uid, 0)) == MFRC522::STATUS_OK ) { + result=true; + } + reader.PICC_HaltA(); + + return result; +} + + +bool uid_read(File &f, MFRC522::Uid &uid) +{ + char line[40]; + + int len = f.readBytesUntil('\n', line, 40); + if (len < 0 || len > 24) { + Serial.print("Invalid read: "); + Serial.println(len); + return false; + } + line[len]=0; + + uid_zero(uid); + if (len == 0) { + // end of file + return false; + } + + // minimum length is 4 bytes == 10 chars + if (len < 10) { + Serial.print(F("Read Card: bad line length=")); + Serial.print(len); + Serial.print(" '"); + Serial.print(line); + Serial.println("'"); + return false; + } + + // invalid format + if (line[1] != ':') { + Serial.print(F("Read Card: bad format '")); + Serial.print(line); + Serial.println("'"); + return false; + } + + int bytes = hexto(line[0]); + + // invalid lengths: 4, 7, and 10 are the standards + if (bytes != 4 && bytes != 7 && bytes != 10) { + Serial.print(F("Read Card: bad size=")); + Serial.print(bytes); + Serial.print(" '"); + Serial.print(line); + Serial.println("'"); + return false; + } + + if (len < (bytes*2)+2) { + Serial.print(F("Read card: line too short. len=")); + Serial.print(len); + Serial.print(F(" size=")); + Serial.print(bytes); + Serial.print(" '"); + Serial.print(line); + Serial.println("'"); + return false; + } + + uid.size = bytes; + byte * p = uid.uidByte; + int in = 2; + + for (;bytes > 0;bytes--) { + int a,b; + a = hexto(line[in++]); + b = hexto(line[in++]); + if (a == -1 || b == -1) { + uid_zero(uid); + + Serial.print(F("Read Card: Non Hex char in line '")); + Serial.print(line); + Serial.println("'"); + return false; + } + *(p++) = a<<4 | b; + } + +#ifdef DEBUG + Serial.print(F("Loaded card '")); + Serial.print(line); + Serial.print("' as "); + Serial.println(uid_print(uid)); +#endif + + return true; +} + +bool uid_valid(MFRC522::Uid &uid) +{ +#ifdef DEBUG + Serial.print(F("Opening file ")); + Serial.println(KEYS_FILE); +#endif + + File f = SPIFFS.open(KEYS_FILE, "r"); + // cant open the file, so card isnt there + if (!f) { + Serial.println(F("Cannot open cards file")); + return false; + } + + MFRC522::Uid entry; + while ( uid_read(f, entry) ) { + if (uid_compare(uid, entry)) { + f.close(); +#ifdef DEBUG + Serial.println(F("Found card")); +#endif + return true; + } + } + +#ifdef DEBUG + Serial.println(F("Card not found")); +#endif + f.close(); + return false; +} + diff --git a/tag_handler.h b/tag_handler.h new file mode 100644 index 0000000..6b49c79 --- /dev/null +++ b/tag_handler.h @@ -0,0 +1,13 @@ +#include +#include "config.h" + +const char * uid_print(const MFRC522::Uid uid); +void uid_copy(MFRC522::Uid &a, const MFRC522::Uid b); +bool uid_compare(const MFRC522::Uid a, const MFRC522::Uid b); +bool uid_auth(const MFRC522::Uid uid); +void uid_zero(MFRC522::Uid &uid); +bool uid_visible(MFRC522 &reader, const MFRC522::Uid master); +bool uid_isset(const MFRC522::Uid uid); +bool uid_fetch(MFRC522 &reader, MFRC522::Uid &uid, int count); +bool uid_valid(MFRC522::Uid &uid); +