Initial basic door lock code, reads from textfile on flash drive
authorJustin Mitchell <justin@discordia.org.uk>
Tue, 29 Mar 2016 13:49:49 +0000 (14:49 +0100)
committerJustin Mitchell <justin@discordia.org.uk>
Tue, 29 Mar 2016 13:49:49 +0000 (14:49 +0100)
client.cpp [new file with mode: 0644]
client.h [new file with mode: 0644]
config.h [new file with mode: 0644]
data/keys [new file with mode: 0644]
doorlock_v1.ino [new file with mode: 0644]
tag_handler.cpp [new file with mode: 0644]
tag_handler.h [new file with mode: 0644]

diff --git a/client.cpp b/client.cpp
new file mode 100644 (file)
index 0000000..44269a9
--- /dev/null
@@ -0,0 +1,45 @@
+#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;
+}
+
diff --git a/client.h b/client.h
new file mode 100644 (file)
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 (file)
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 (file)
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 (file)
index 0000000..40b53b3
--- /dev/null
@@ -0,0 +1,201 @@
+#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);
+
+}
diff --git a/tag_handler.cpp b/tag_handler.cpp
new file mode 100644 (file)
index 0000000..1bf95af
--- /dev/null
@@ -0,0 +1,257 @@
+#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;
+}
+
diff --git a/tag_handler.h b/tag_handler.h
new file mode 100644 (file)
index 0000000..6b49c79
--- /dev/null
@@ -0,0 +1,13 @@
+#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);
+