Esp8266 NFC multi door lock system

This is an ESP 8266 based project that uses NFC reader PN532 to secure and open doors.

the code here is not current. Please use the code at github!!
Here is the github link

now tested with 4 scanners and multiple cards. also added OTA for updates and wifi / BLYNK provisioning.

The code has two blynk tokens
one for the lock and one for the master/scanner these are default values.

I implemented it as a multi door system with a maximum of 500 cards per door. It could easily be upgraded to more cards per door.

The project is now in 7 files

main file


/*

   Creative Commons License
   NFC Door lock
   by Michael Dale Burton
   is licensed under a Creative Commons Attribution-NonCommercial
   4.0 International License.

    Impliments a door lock system using NFC reader and the Blynk app
    Blynk is used to store the data on the cloud and provide
    a smart phone interface for adding, removing and assigning cards
    Device selector is used to choose the lock/door

    500 cards are supported
    unlimited  locks are supported
    1 master  is used as the scanner for adding cards it can also be used as a lock

    Nodemcu esp8266 and PN532 hardware is used

    V1 is a timer
    V2 is a reset button
    V3 ia a text input for the user name assigned to a card
    V4 is a value display for the card id
    V5 current card index "vcount"
    V6 is a terminal window for debug information
    V10 is the delete button
    V24 is a value display for the number of valid cards
    V25 - V26 are reserved for data bridge

*/

#include <Wire.h>
#include <PN532_I2C.h>
#include <PN532.h>
#include <NfcAdapter.h>
#include <ArduinoOTA.h>
#include <ESP8266WiFi.h>
#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager WiFi Configuration Magic
#include <ArduinoJson.h>          //https://github.com/bblanchon/ArduinoJson
#include <BlynkSimpleEsp8266.h>
#include <TimeLib.h>
#include <WidgetRTC.h>
#include "globals.h"                    // tab for other defines and globals


PN532_I2C pn532i2c(Wire);
PN532 nfc(pn532i2c);

//create terminal widget instance and attach it to the pin
WidgetTerminal terminal(terminaldisplay);

//Create the real time clock instance
WidgetRTC rtc;

//create timer instance
BlynkTimer timer;

//create bridge instance and assign pins
WidgetBridge bridge1(51);
WidgetBridge bridge2(52);
WidgetBridge bridge3(53);



// get the time and the day
void getCurrentTime() {
  currentTime = String(hour() + ":" + minute());
  currentDate = String(day());
}

void lockDoor() {
  digitalWrite(LED_PIN, LOW);             // turn off LED this causes the door to lock
  Blynk.virtualWrite(lockbutton, 0);      // display door locked on the app
  flag = 0;
}

void unlockDoor() {
  if (!flag) {
    flag = 1;
    digitalWrite(LED_PIN, HIGH);              // send power to unlock the door
    timer.setTimeout(5000L, lockDoor);        // lock the door after 5 seconds
  }
  Blynk.virtualWrite(lockbutton, 1);          // update the app
}


/**************************************
  connect to wifi
  connect to blynk server
  create timer
  start nfc
  start I2c
  set I2c rate and pins
  start serial and set speed

**************************************/
void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(RTS_PIN, OUTPUT);
  pinMode(DTR_PIN, OUTPUT);

  Wire.setClock(wirespeed);                     // set the Ic2 buss speed
  Serial.begin(serialspeed);                    // open the serial port
  Wire.pins(SDA_PIN, SLC_PIN);                  // SDA, SLC pins
  Wire.begin();                                 // start the Ic2 buss
  nfc.begin();                                  // init the NFC Hardware
  SPIFFS.begin();

  Serial.print(currentTime);
    // these two call get the data from the SPIFFS file and use the data to provision  blynk and wifi
  restoreparameters();
  startmywifi();

  Blynk.config(blynk_token);          // config Blynk
  
  getdatafromfile();
//  Blynk.syncVirtual(vcount, datapins);          // restore count and card data  This is how all the card data gets restored from the cloud
//  Blynk.syncVirtual(vcount);                    // restore count

  timer.setInterval(500l, scanNfc);             // scan the NFC every .5 seconds
  ArduinoOTA.setHostname(modulename); // give an name to our module
  ArduinoOTA.begin(); // OTA initialization 
  timer.setInterval(1200L, [] () { //  <<=== set how often the code should run 1200L = Run every 1.2 seconds
    ArduinoOTA.handle();                        // check for OTA update
  });  // END Timer Function
}

/**************************************
  main loop
**************************************/
void loop()
{
  if (Blynk.connected()){
    Blynk.run();
  } else Blynk.connect(1000l);                // timeout 1 second
  timer.run();
}

The Blynk Calls are in file 2.

#include <FS.h>

/*

   Creative Commons License
   NFC Door lock
   by Michael Dale Burton
   is licensed under a Creative Commons Attribution-NonCommercial
   4.0 International License.

    Impliments a door lock system using NFC reader and the Blynk app
    Blynk is used to store the data on the cloud and provide
    a smart phone interface for adding, removing and assigning cards
    Device selector is used to choose the lock/door

    20 cards are supported
    2 slaved locks are supported
    1 master  is used as the scanner for adding cards it can also be used as a lock

    Nodemcu esp8266 and PN532 hardware is used

    V1 is a timer
    V2 is a reset button
    V3 ia a text input for the user name assigned to a card
    V4 is a value display for the card id
    V5 current card index "vcount"
    V6 is a terminal window for debug information
    V10 is the delete button
    V24 is a value display for the number of valid cards
    V25 - V45 are reserved for data storage

*/

#include <Wire.h>
#include <PN532_I2C.h>
#include <PN532.h>
#include <NfcAdapter.h>
#include <ArduinoOTA.h>
#include <ESP8266WiFi.h>
#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager WiFi Configuration Magic
#include <ArduinoJson.h>          //https://github.com/bblanchon/ArduinoJson
#include <BlynkSimpleEsp8266.h>
#include <TimeLib.h>
#include <WidgetRTC.h>
#include "globals.h"                    // tab for other defines and globals


PN532_I2C pn532i2c(Wire);
PN532 nfc(pn532i2c);

//create terminal widget instance and attach it to the pin
WidgetTerminal terminal(terminaldisplay);

//Create the real time clock instance
WidgetRTC rtc;

//create timer instance
BlynkTimer timer;

//create bridge instance and assign pins
WidgetBridge bridge1(51);
WidgetBridge bridge2(52);
WidgetBridge bridge3(53);



// get the time and the day
void getCurrentTime() {
  currentTime = String(hour() + ":" + minute());
  currentDate = String(day());
}

void lockDoor() {
  digitalWrite(LED_PIN, LOW);             // turn off LED this causes the door to lock
  Blynk.virtualWrite(lockbutton, 0);      // display door locked on the app
  flag = 0;
}

void unlockDoor() {
  if (!flag) {
    flag = 1;
    digitalWrite(LED_PIN, HIGH);              // send power to unlock the door
    timer.setTimeout(5000L, lockDoor);        // lock the door after 5 seconds
  }
  Blynk.virtualWrite(lockbutton, 1);          // update the app
}


/**************************************
  connect to wifi
  connect to blynk server
  create timer
  start nfc
  start I2c
  set I2c rate and pins
  start serial and set speed

**************************************/
void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(RTS_PIN, OUTPUT);
  pinMode(DTR_PIN, OUTPUT);

  Wire.setClock(wirespeed);                     // set the Ic2 buss speed
  Serial.begin(serialspeed);                    // open the serial port
  Wire.pins(SDA_PIN, SLC_PIN);                  // SDA, SLC pins
  Wire.begin();                                 // start the Ic2 buss
  nfc.begin();                                  // init the NFC Hardware
  SPIFFS.begin();

  Serial.print(currentTime);
    // these two call get the data from the SPIFFS file and use the data to provision  blynk and wifi
  restoreparameters();
  startmywifi();

  Blynk.config(blynk_token);          // config Blynk
  
  getdatafromfile();
//  Blynk.syncVirtual(vcount, datapins);          // restore count and card data  This is how all the card data gets restored from the cloud
//  Blynk.syncVirtual(vcount);                    // restore count

  timer.setInterval(500l, scanNfc);             // scan the NFC every .5 seconds
  ArduinoOTA.setHostname(modulename); // give an name to our module
  ArduinoOTA.begin(); // OTA initialization 
  timer.setInterval(1200L, [] () { //  <<=== set how often the code should run 1200L = Run every 1.2 seconds
    ArduinoOTA.handle();                        // check for OTA update
  });  // END Timer Function
}

/**************************************
  main loop
**************************************/
void loop()
{
  if (Blynk.connected()){
    Blynk.run();
  } else Blynk.connect(1000l);                // timeout 1 second
  timer.run();
}


The data is handled in the third file.

/*
   NFC Door lock
   Auther Michael Burton

   Creative Commons License
   NFC Door lock
   by Michael Dale Burton
   is licensed under a Creative Commons Attribution-NonCommercial
   4.0 International License.

*/
/**************************************
  compairUid return bool

  in: card ids

  out: true if they are the same else false
**************************************/

bool compairUid(uint8_t uid1[], uint8_t uid2[], uint8_t j) {
  for (int i = 0; i < j; i++) {
    if (uid1[i] != uid2[i]) {
      return false;
    }
  }

  return true;
}
/*****************************************************
   findUid
   takes uid and length
   return index of uid in array or 0 if not found

*/
int findUid(String suid) {
  for (int i = 1; i <= rowIndex; i++) {
    if (!(suid.compareTo(String(&cardId[i][0])))) {
      return i; // index of card found
    }

  }

  return 0;
}
/**************************************
  storeCard
  in: row index, card Id, id length, card holder name

  places the uid and the name in the hardware memory at index location ri

  out: none
**************************************/
//put card id  in memory
void storeCard(int ri, String suid2, String holder, int af) {
  if(openspiffs()){
    uint16_t buffer_offset;
    buffer_offset = ri * recordlength;
    cardId[ri] = suid2; // save uid as a string
    cardHolder[ri] = holder;        // save name of card holder
    accessFlags[ri] = af;
    Blynk.virtualWrite(vcount, rowIndex);                                                    //save the card count on the server
    write16data(0, rowIndex);                                                                //save the card count at offset 0

//  Blynk.virtualWrite(DATASTORE + ri, ri, cardHolder[ri], cardId[ri], accessFlags[ri]);     //save card data on server
    write16data(buffer_offset, ri);
    buffer_offset += 2;
    writestringdata(buffer_offset, maxnamelength, cardHolder[ri]);
    buffer_offset += maxnamelength;
    writestringdata(buffer_offset, maxuidlength, cardId[ri]);
    buffer_offset += maxuidlength;
    write16data(buffer_offset, accessFlags[ri]);
    buffer_offset += 2;
    Blynk.virtualWrite(indexdisplay, ri);
    closespiffs();
  }
}

void getCard(int index) {
  cardId[0] = cardId[index];                      //get cardId
  cardHolder[0] = cardHolder[index];              // get name of card holder
}


/***************************************
   get card id access flags and user name from the cloud

   in:  card Id, card holder name, access flags, index

   datastore is the virtual pin for the card data
**************************************/
void readFromCloud(int count) {
  getdatafromfile();
//  getdatafromfram();
//  for (int i = 0; i < count; i++) {
//    Blynk.syncVirtual(DATASTORE + count);                             //send the sync to the data pin to request card data
//  }
  getCard(1);
  Blynk.virtualWrite(namedisplay, cardHolder[0]);
  Blynk.virtualWrite(cardiddisplay, cardId[0]);
}

file four is the data handling

/*
   NFC Door lock
   Auther Michael Burton

   Creative Commons License
   NFC Door lock
   by Michael Dale Burton
   is licensed under a Creative Commons Attribution-NonCommercial
   4.0 International License.

*/
//#define BLYNK_PRINT Serial  // Comment this line out to disable prints and save space 
//#define BLYNK_DEBUG

// only uncoment one at a time
//#define MASTER_LOCK true
#define LOCK1_LOCK  true
//#define LOCK2_LOCK  true
/*
   hardware defines
*/
#define wirespeed 400000L   // Ic2 buss speed
#define serialspeed 115200  // serial port speed
#define nfcretries 0x0f     // number of passive retries before timeout on nfc hardware 

/*
                                PIN MAP for ESP8266
*/
#define DATASTART   0x0           // start address of data buffer
#define IC2_FRAM_ADDRESS 0x50     // address of the fram board
#define SDA_PIN     2             // I2C SDA pin
#define SLC_PIN     14            // I2C SCL pin

#define RTS_PIN     4             // RTS line for C17A - DB9 pin 7
#define DTR_PIN     5             // DTR line for C17A - DB9 pin 4

#define RELAY_PIN   8             // relay output pin
#define LED_PIN     16            // attached LED



/********************************************************************************************

   BLYNK V PIN MAP

 ********************************************************************************************/
#define clockpin        V1
#define resetallpin     V2
#define namedisplay     V3
#define cardiddisplay   V4
#define indexdisplay    V5
#define terminaldisplay V6
#define copybutton      V11
#define copyallbutton   V12
#define bridgewrite     V7
#define lockbutton      V8
#define bridgedata      V9
#define deletebutton    V10
#define eventorpin      V14
#define vcount          V24
#define DATASTORE       V25
#define datapins        V25, V26, V27, V28, V29, V30, V31, V32, V33, V34, V35, V36, V37, V38, V39, V40, V41, V42, V43, V44, V45


#define maxcards      20
#define maxnamelength 20
#define maxuidlength  15
#define flagslength   1
#define recordlength    (4 + maxnamelength + maxuidlength)   // 4 bytes for the two ints + the max string lengths  for the name and uid

const char*  daystrings[7]  =  {"Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat"};

/* BLYNK tokens

*/
char reader_token[] = "blynk token1";
char lock1_token[] = "blynk token2";
char lock2_token[] = "blynk token 3";

char readername[] = "nfclockreader";
char lock1name[] = "nfclock1";
char lock2name[] = "nfclock2";

#if defined(MASTER_LOCK)

char blynk_token[34] = "blynk token1";
char modulename[] = "nfclockreader";

#elif defined(LOCK1_LOCK)

char blynk_token[34] = "blynk token2";
char modulename[] = "nfclock1";


#else

char blynk_token[34] = "blynk token 3";
char modulename[] = "nfclock2";


#endif
/*
    WiFi credentials.


char ssid[] = "ssid";
char pass[] = "password";
*/
/*
    Global data
*/
String  cardId[maxcards + 1];
String  cardHolder[maxcards + 1];
int     accessFlags[maxcards + 1];
String  SThour;
String  STmin;
String  SPhour;
String  SPmin;
int     days[7];
int     flag = 0;

int     cardIndex = 0;
int     rowIndex = 0;
String  currentTime;
String  currentDate;
int talktome = 0;

//define your default values here, if there are different values in config.json, they are overwritten.
char mqtt_server[40];
char mqtt_port[6] = "8080";

//flag for saving wifi data
bool shouldSaveConfig = true;

uint32_t versiondata = 0; // NFC hardware version
String carddatafile = "/carddata.txt";
File datafile;



Card reader file

// setupnfc does not block or wait for a success
// you will need to call it again if the global var versiondata is not set
// once it sees the hardware it sets the number of retries and configures the board
// it then prints the current time and "nfc started to the terminal
void setupnfc() {
  Serial.println("looking for NFC");
  //search for the NFC hardware
  versiondata = nfc.getFirmwareVersion();
  if (versiondata){
    nfc.setPassiveActivationRetries(nfcretries);  // Set the max number of retry attempts to read from a card
    nfc.SAMConfig();                              // configure board to read RFID tags
    terminal.println("NFC started");
    terminal.print(currentTime);
  }

}

/**************************************
  scanNfc
  Get card data from  NFC hardware
**************************************/
void scanNfc() {

  uint8_t uid[maxuidlength] = {0, 0, 0, 0, 0, 0, 0, 0}; // Buffer to store the returned UID
  String  suid;
  uint8_t uidLength = 0;                          // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
  char holder[maxnamelength];

  if (versiondata) {
    nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, &uid[0], &uidLength);
    if (uidLength) {                                      // found a card
      for (int i = 0; i < uidLength; i++) {
        suid += String(uid[i], HEX);                      // convert the uid to a hex string
      }
      Blynk.virtualWrite(cardiddisplay, suid);           // display the uid
      cardIndex = findUid(suid);
      if (cardIndex) {
        unlockDoor();                                     // unlock the door if the card was valid
        Blynk.virtualWrite(namedisplay, cardHolder[cardIndex]); //display the card holder name on the app
        terminal.print(cardHolder[cardIndex]);  // send debug data to the terminal
        terminal.print(" ");
        terminal.print(currentTime);
        terminal.print(" ");
        terminal.println(currentDate);
        terminal.println(cardId[cardIndex]);
        terminal.flush();
        Blynk.virtualWrite(indexdisplay, cardIndex);  // display the card index on the app
        return;                                       // card found exit
      }
      storeCard(0, suid, String(" "), 0);             // no card found clear card display
      Blynk.virtualWrite(namedisplay, " ");           // clear name display on the app
      Blynk.virtualWrite(indexdisplay, 0);            // set index display to 0 on the app
    }
  } else {
    setupnfc();                                       // if hardware not found then start it now.
  }
}

SPIFFS file


bool openspiffs(){

    datafile = SPIFFS.open(carddatafile, "r+b");        // exsisting file opened
    if (!datafile){
          datafile = SPIFFS.open(carddatafile, "w+b");  // create new file
    }
  return datafile;
}

void closespiffs(){

    if (datafile){
          datafile.close();
    }
}


void writestringdata(uint16_t whatbyte, int howlong, String databuffer){
    byte s[maxnamelength+1];
    datafile.seek(DATASTART+whatbyte, SeekSet);
    
    databuffer.getBytes(s, howlong);     
    datafile.write(s, howlong);
    datafile.flush();
}

String readstringdata(uint16_t whatbyte, int howlong){
  char s[maxnamelength+1];
  byte s2[maxnamelength+1];
  
  datafile.seek(DATASTART+whatbyte, SeekSet);
  datafile.read(s2, howlong);
  for ( int i = 0; i < howlong; i++)
    s[i] = char(s2[i]);
    s[howlong] = 0;
  return String(s);
}


uint16_t read16data(uint16_t whatbyte){
  uint16_t out;
  uint8_t high;
  uint8_t low;
  
  datafile.seek(DATASTART+whatbyte, SeekSet);
  datafile.read(&high, 1);
  datafile.read(&low, 1);

  out = high << 8;
  out += low;
  return out;
 }

void write16data(uint16_t whatbyte, uint16_t databuffer){
  uint8_t high;
  uint8_t low;
  high = highByte(databuffer);
  low = lowByte(databuffer);
  datafile.seek(DATASTART+whatbyte, SeekSet);
  datafile.write(&high, 1);
  datafile.write(&low, 1);  
  datafile.flush();
 }
 
void getdatafromfile(){
  if (openspiffs()){
  rowIndex = read16data(0);                                                   //read the card count from spiffs 
  
  uint16_t buffer_offset = recordlength;                                    //start at record 1
    Blynk.virtualWrite(vcount, rowIndex);       // write the rowIndex to the server
    
  for (int i = 1; i <= rowIndex; i++){                                             // read all the card data
//  read16data(buffer_offset, tempindex);                                       // skip the index
    buffer_offset += 2;
    cardHolder[i] = readstringdata(buffer_offset, maxnamelength);
    buffer_offset += maxnamelength;
    cardId[i] = readstringdata(buffer_offset, maxuidlength);
    buffer_offset += maxuidlength;
    accessFlags[i] = read16data(buffer_offset);
    buffer_offset += 2;
    yield();
  }
  closespiffs();
  }
}

void dumpdatafromfile(){
  String ch;
  String cId;
  int af;
  int ri;
  int tempIndex;
  uint16_t buffer_offset = recordlength;                                    //start at record 1

    if ( openspiffs()){
    tempIndex = read16data(0);                                                   //read the card count from fram address 0
    terminal.println("index "+String(tempIndex));
  for (int i = 1; i <= tempIndex; i++){                                             // read all the card data
    ri = read16data(buffer_offset);
    buffer_offset += 2;
    ch = readstringdata(buffer_offset, maxnamelength);
    buffer_offset += maxnamelength;
    cId = readstringdata(buffer_offset, maxuidlength);
    buffer_offset += maxuidlength;
    af = read16data(buffer_offset);
    buffer_offset += 2;
    terminal.println("index: " + String(ri) + " holder:  " + ch + " ID:  " + cId + " flags: " + String(af));
    yield();
  }
  terminal.flush();
  closespiffs();
    }
}


Wifi file



//callback notifying us of the need to save config
void saveConfigCallback () {
  shouldSaveConfig = true;
}

void restoreparameters() {

  if (SPIFFS.begin()) {
    if (SPIFFS.exists("/config.json")) {
      //file exists, reading and loading
      File configFile = SPIFFS.open("/config.json", "r");
      if (configFile) {
        size_t size = configFile.size();
        // Allocate a buffer to store contents of the file.
        std::unique_ptr<char[]> buf(new char[size]);
        configFile.readBytes(buf.get(), size);
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject(buf.get());
        //        json.printTo(Serial);
        if (json.success()) {
          // json will parse the file for the variable name and load it into the supplied variable
          strcpy(mqtt_server, json["mqtt_server"]);     //get the mqtt_server <== you need one of these for each param
          strcpy(mqtt_port, json["mqtt_port"]);         //get the mqtt port
          strcpy(blynk_token, json["blynk_token"]);     //get the blynk token
        }
        configFile.close();
      }
    }
  }
}



void startmywifi() {
  //Local intialization. Once its done, there is no need to keep it around
  WiFiManager wifiManager;

  //set config save notify callback
  wifiManager.setSaveConfigCallback(saveConfigCallback);

  // The extra parameters to be configured (can be either global or just here)
  // After connecting, parameter.getValue() will get you the configured value
  // id/name placeholder/prompt default length
  WiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, 40);
  WiFiManagerParameter custom_mqtt_port("port", "mqtt port", mqtt_port, 6);
  WiFiManagerParameter custom_blynk_token("blynk", "blynk token", blynk_token, 32);

  //set static ip if you want
  //  wifiManager.setSTAStaticIPConfig(IPAddress(10,0,1,99), IPAddress(10,0,1,1), IPAddress(255,255,255,0));

  //  <============== add all your parameters here
  wifiManager.addParameter(&custom_mqtt_server);
  wifiManager.addParameter(&custom_mqtt_port);
  wifiManager.addParameter(&custom_blynk_token);

  //reset settings - for testing
  //    wifiManager.resetSettings();    // <========= once you have this working you can comment out this line

  //set minimu quality of signal so it ignores AP's under that quality
  //defaults to 8%
  //wifiManager.setMinimumSignalQuality();

  //sets timeout until configuration portal gets turned off
  //useful to make it all retry or go to sleep
  //in seconds
  //wifiManager.setTimeout(120);

  //fetches ssid and pass and tries to connect
  //if it does not connect it starts an access point with the specified name
  //and goes into a blocking loop awaiting configuration
  if (!wifiManager.autoConnect("AutoConnectAP", "password")) {
    ESP.reset();    //reset and try again
  }

  //if you get here you have connected to the WiFi

  //get the custom parameters here
  strcpy(mqtt_server, custom_mqtt_server.getValue());
  strcpy(mqtt_port, custom_mqtt_port.getValue());
  strcpy(blynk_token, custom_blynk_token.getValue());

  //check the flag to see if we save the custom parameters to FS
  if (shouldSaveConfig) {
    DynamicJsonBuffer jsonBuffer;
    JsonObject& json = jsonBuffer.createObject();

    //set the custom parameters here
    json["mqtt_server"] = mqtt_server;
    json["mqtt_port"] = mqtt_port;
    json["blynk_token"] = blynk_token;

    File configFile = SPIFFS.open("/config.json", "w");
    if (configFile) {
      json.printTo(configFile);   //write the custom parameters here
      configFile.close();
    } else Serial.println("failed to open config file for writing");
    //end save
  }
}
3 Likes

Here is a very short video of the project in operation.

video of door lock

You will see that the relay is active for 5 seconds after each time a card is read that is on the system.

if a card is read on the master that is not stored in the system you can add the card to any door.

cards can be moved to any door or removed. the activity is logged in the terminal.

3 Likes

Nice for video and projects. Thank you.

Hi @Gyromike, nice project.
I’ve not studied your code, but from your description I guess that the system won’t work without your Wi-Fi and the Blynk server that you’re using being online?

I have a somewhat simpler system which uses just one RFID tag code (because I want the unique tag that’s used to access your community swimming pool to also open our gate). My single code is hard-coded into my sketch, but Blynk can be used to release the gate remotely, and a Blynk table is used to store info about when the gate has been released and how.

I get around the need to be online all the time by hard-coding my single RFID code I to my sketch (easy for me with gust one code) and using a non-blocking connection that will carry-on working if there’s no internet connection, and will try to re-connect periodically.

Had I needed to use more codes then I’d probably have explored the idea of storing them in the SPIFFS memory of the NodeMCU, with an option to add/remove via Blynk.

When I put my system together it worked perfectly on the bench, but went haywire when I connected it up to the gate release. My gate uses an AC release, which makes a buzzing noise for the 5 seconds that it’s avtivated. It turns-out that the issue was EMF feedback from the coil on the gate release, which I eventually solved with a capacitor across the sontacts of the relay. From memory, I think it was around 100uf.
If you, or anyone else trying this project, has similar issues with an AC lock release then this tip might save you a bit of head scratching.

Here’s some info on my project, which is very different because it uses Node-Red and MQTT as well as Blynk…

Pete.

1 Like

Thanks Pete,

The code has local storage that gets refreshed by the cloud on connect. It was working to open the locks offline but I have not checked it recently. I may have broken it in that regard.

Recently I have purchased some chips that have persistent storage that work with little power over i2C buss. I will augment the cloud storage with this in the next version and make sure that it will work offline.

Right now I am testing the locks on my home and hangar. So far so good.

It is a fun hobby for me. I retired from software engineering a few years back and this is a revival for me. I was quite burned out. I am now in it mostly for fun.

The things that drive me crazy are egomaniacs that think they are the sun and the moon. but I am good now at letting them think what they want and going my own way.

I have a lot of ideas for new projects.

1 Like

Good to hear that you’re doing some hobby stuff for fun now.

Many moons ago I was a professional photographer and when I changed careers it took me a long time to re-discover photography as a hobby. It can take quite a while to get out of the ‘working for the client’ mindset and starting to enjoy doing stuff for yourself instead.

Sounds like you have another hobby too though, which I guess is where your username comes from?

Pete.

I love photography also. One of my passions. I don’t do portraits!

The name comes from my involvement in Aviation. It is a life long love.

1 Like

good job !
but what did you expect in case the nodemcu crashes?
do you have planned a nodemcu auto reset ? :fearful:

Today i completed adding FRAM storage for the backup data. I used the small FRAM from Adafruit. It works over the I2c buss.

It was a fun addition it is now working.

This week I am going to work on the enclosures. To this point there have been no problems with the locks. The testing has been without event. The locks have been up for two weeks now except when I update them. I have been leaving one online to see how long it will be before it goes offline.

I have tested power cycles and network outages. :slight_smile:

1 Like

fram%20and%20card%20reader%20on%20adafruit%20board

Here is the reader and the FRAM connected to the adafruit ESP8266 board

1 Like

Here is the code for the file storage on the FRAM.

I used the Adafruit library but it was quit limited. so I wrote so additional bits to make it easy for myself to reuse.

you will nee to include “Adafruit_FRAM_I2C.h”

it still has some debug lines in my code as you will see.

call startfram(); it will make sure the Ic2 address is valid and print “fram started” when the FRAM is ready. It is non blocking.

I used it like this

timer.setInterval(700l, startfram); // scan for valid FRAM address every .7 seconds

It will continue to check the FRAM until it gets a valid address. then the code will just return without doing anything.




void startfram() {
  if (validfram) {
    return;
  } else {
    validfram = fram.begin(IC2_FRAM_ADDRESS);
    if (!validfram) {
      yield();
    } else {         
      terminal.println("fram started: ");
      terminal.flush();
      getdatafromfram();
    }
  }
}

void readrawdata(uint16_t whatbyte, int howlong, char databuffer[]){
    for (int j = 0; j < howlong; j++){
      databuffer[j] = fram.read8(DATASTART+whatbyte+j);
      yield();
    }
}

void writerawdata(uint16_t whatbyte, int howlong, char databuffer[]){
    for (int j = 0; j < howlong; j++){
       fram.write8(DATASTART+whatbyte+j, databuffer[j]);
      yield();
    }
}

void writestringdata(uint16_t whatbyte, int howlong, String databuffer){
   
    for (int j = 0; j < howlong; j++){
       fram.write8(DATASTART+whatbyte+j,  databuffer.charAt(j));
       yield();
    }
}

String readstringdata(uint16_t whatbyte, int howlong){
  char s[256];
    for (int j = 0; j < howlong; j++){
      s[j] = fram.read8(DATASTART+whatbyte+j);
      yield();
    }
    return String(s);
}

uint16_t read16data(uint16_t whatbyte){
  uint8_t high = fram.read8(DATASTART+whatbyte);
  uint8_t low = fram.read8(DATASTART+whatbyte+1);
  uint16_t out;
  out = high << 8;
  out += low;
  return out;
 }

void write16data(uint16_t whatbyte, uint16_t databuffer){
  uint8_t high = highByte(databuffer);
  uint8_t low = lowByte(databuffer);
  fram.write8(DATASTART+whatbyte, high);
  fram.write8(DATASTART+whatbyte+1, low);
 }
 
void getdatafromfram(){
  rowIndex = read16data(0);                                                   //read the card count from fram 
  uint16_t buffer_offset = recordlength;                                    //start at record 1
    Blynk.virtualWrite(vcount, rowIndex);       // write the rowIndex to the server
    
  for (int i = 1; i <= rowIndex; i++){                                             // read all the card data
//    tempindex = read16data(buffer_offset);                                       // skip the index
    buffer_offset += 2;
    cardHolder[i] = readstringdata(buffer_offset, maxnamelength);
    buffer_offset += maxnamelength;
    cardId[i] = readstringdata(buffer_offset, maxuidlength);
    buffer_offset += maxuidlength;
    accessFlags[i] = read16data(buffer_offset);
    buffer_offset += 2;
    yield();
  }
}

void dumpdatafromfram(){
  String ch;
  String cId;
  int af;
  int ri;
  int tempIndex;
  uint16_t buffer_offset = recordlength;                                    //start at record 1


    tempIndex = read16data(0);                                                   //read the card count from fram address 0
  for (int i = 1; i <= tempIndex; i++){                                             // read all the card data
    ri = read16data(buffer_offset);
    buffer_offset += 2;
    ch = readstringdata(buffer_offset, maxnamelength);
    buffer_offset += maxnamelength;
    cId = readstringdata(buffer_offset, maxuidlength);
    buffer_offset += maxuidlength;
    af = read16data(buffer_offset);
    buffer_offset += 2;
    terminal.println("index: " + String(ri) + " holder:  " + ch + " ID:  " + cId + " flags: " + String(af));
    yield();
  }
  terminal.flush();
}

1 Like

Alexis,

It restores the data when it reconnects to the server. The data is also stored in FRAM. On startup the data is restored and the lock returns to function even if the cloud does not connect.

32kb for $10, right?

Why not use the free 3MB of SPIFFS that most ESP8266’s have?

1 Like

you’re right, Costas,
it’s better to use nodemecu RAM SPIFFS.
it’s a scam the 32k for $ 10 !
nodemecu is only 2.50$ with 4 Mb.
:scream:

1 Like

I can’t be sure of the capacity or voltage requirements… but they do seem a bit expensive…

image

2 Likes

The advantage of FRAM memory is that it has a life of around 10,000,000,000,000 read/write cycles, as opposed to 100,000 for the Winbond memory used by SPIFFS.
Obviously in this scenario, the number of times data needs to be written to SPIFFS is limited, as it only needs to be used to store a few RFID key tag numbers, and these won’t change very often, but it also has the advantage of not being overwritten when flashing your NodeMCU code if you don’t take care about which flash mode you use.

Pete.

3 Likes

For no good reason. I just like playing with hardware and software. It was an enjoyable exercise.

You are right it is a bit expensive for the size of memory. The memory was made for things that need very rapid access to persistent storage and it is new. I am sure the cost will go down as it has for other storage media.

I will explore using the internal memory.

Funny Peter,

Thanks for taking another look.

I purchased them in 2 sizes 8kb and 32kb. Yes this is a bit more than some options for data storage.

For a house it does not change much but for a hotel it would change every day. Still 100,000 cycles would last quite some time.

One big advantage is that it does not get erased on update.