--- /dev/null
+#include <ESP8266WiFi.h>
+
+#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;
+}
+
--- /dev/null
+bool log_attempt(const char *uid, bool success);
+
--- /dev/null
+// 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
--- /dev/null
+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
--- /dev/null
+#include <SPI.h>
+#include <ESP8266WiFi.h>
+#include <MFRC522.h>
+#include <Adafruit_NeoPixel.h>
+#include <FS.h>
+
+
+#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);
+
+}
--- /dev/null
+#include <MFRC522.h>
+#include <FS.h>
+#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<len; i++) {
+ *(p++) = hex[ uid[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;
+}
+
--- /dev/null
+#include <MFRC522.h>
+#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);
+