Es kommt vor, dass man Kleinigkeiten, Schlüssel oder anderes für fremde Personen hinterlegen muss, jedoch nicht vor Ort sein kann. Man kann die Dinge irgendwo verstecken oder hinlegen und hoffen, dass sie nicht von Dritten gefunden werden oder man baut sich eine Box, die mit Code, sowie via NFC geöffnet werden kann.
Ich habe mich für die zweite Variante entschieden, die ich hier vorstelle.
Funktionsweise
Die NFC bzw. Code Schlüsselbox soll sich sowohl durch Nutzung von NFC-Karten, sowie durch Eingabe von Codes öffnen lassen. Die Codes und NFC-Karten sollen im Home Assistant bequem und dynamisch hinterlegt werden können.
Nach einer Bestimmten Zeit wird das Display deaktiviert. Durch Drücken der Taste “0” kann dieses wieder aufgeweckt werden. Es kann dann zwischen Code-Eingbe mit “#”, sowie NFC-Karte mit “*” gewählt werden. Das ist wichtig, da während der ESP32 auf die Daten einer NFC-Karte wartet, keine weiteren Interaktionen verarbeiten kann. Zudem ergibt es energetisch keinen Sinn den NFC Kartenleser dauerhaft aktiv zu lassen. Bei Eingabe eines Code wird dieser mit “#” bestätigt.
Daraufhin prüft der ESP32 die Eingabe oder die NFC-Karte, ob diese berechtigt ist. Dafür holt sich der ESP32 regelmäßig die Daten zu den NFC-Karten, sowie Codes vom HomeAssistant ab.
Nach Verfikation und Korrektheit wird die Tür entriegelt und der Schlüssel oder was auch immer in der Box liegt, kann entnommen werden.
Im HomeAssistant werden bei NFC Karten die UID/MAC eingegeben, sowie bei Codes die Zahlen. Das Format der UID/MAC ist: 00:AA:BB:CC…
Zudem wird der positive Zugriff, sowie auch der Fall des negativen Zugriffs an den HomeAssistant zu Dokumentationszwecken übersandt.
Geschlossen wird die Box durch herzhaftes zudrücken, bis das Schloss einrastet. Der Status, ob die Box offen oder geschlossen ist, kann am Schloss selbst abgegriffen werden, dafür bauen die Hersteller dieser Öffner oft Kontakte ein, die diese Information bereitstellen.
Hardware
Wie bereits viele andere meiner Projekte, startet auch dieses mit einem ESP32 Mikrocontroller. Dazu habe ich mir ein Nummernfeld als Tastatur, einen NFC Code Modul, ein Display, sowie einen Öffner besorgt.
Als NFC Modul verwende ich ein PN532 mit i²c Interface. Für dieses Modul gibt es eine gleichnamige Bibliothek.
Ein Keypad 3 Tasten breit und 4 Tasten hoch ist für dieses Projekt ausreichend. Es gibt vierschiedene Keypads, diese funktionieren meistens ähnlich.
Das Display für die Anzeige ist ein LCD Display mit 2×16 Zeichen und i²c als Schnittstelle. Wichtig ist: Falls das Display nichts anzeigt, hilft es, nach zu lesen, ob das Display mit 3,3V oder 5V versorgt werden muss. Ich habe eines, das 5V benötigt, mit nur 3,3V wird nichts angezeigt.
Als Öffner bzw. Schloss verwende ich ein elektrisches Spind Schloss. Zu finden sind sie meistens mit den Suchbegriff “12v dc elektroschloss”. Wichtig ist, dass man diese Schlösser nur ganz kurz mit Strom versorgen darf, da sie sich sonst in Rauch auflösen. Das steht allerdings in der Regel im Begleitpapier nochmal dabei. Diese Spind Öffner melden oft auch den Status zurück.
Platine
Hier die Platine dazu (Afillate): https://aisler.net/p/VXNKFJSZ
- ESP32 mit 38 PINs
- EN-DC -> Brücke um den ESP über den DC-Wandler zu versorgen (im Normalbetrieb) oder über USB, wenn nicht gebrückt wurde, interessant für Debug-Zwecke
- DC-Wandler : RECOM R-78E50-05
- MOSFET vom Typ : IRF5210
- C1, C2, C3 : 10nF
- D1 : Freilaufdode z.B. 1N 4148 oder 1N 4007
- L1 : 10 uH
- R1 : 5.6 kO
- R2 : 1 kO
- R3 : 10 kO
- T1 : BC547C
Die Platine wird mit 12V DC versorgt. Der DC-Wandler erzeugt daraus 5V für den ESP32. Im Falle des Öffnens, wird vom ESP32 zunächst der Transistor T1 geschaltet, der wiederum 12V / 0V am Gate des IRF5210 setzt. Über R3 wird das Gate des IRF5210 immer auf 12V gehalten, bis T1 durchgeschaltet wird und das Gate des IRF auf 0V zieht, dann schaltet der IRF und legt 12V an den Elektroschloss Öffner Kontakt an. Die Diode D1 ist die Freilaufdiode für das Elektroschloss. SW ist der Elektroschloss Schloss Status Kontakt. Das Elektroschloss hat intern einen kleinen Schalter, der Rückmeldet, ob das Schloss geschlossen (1) oder offen (0) ist. Display, NFC Modul, sowie der optionale BME280 werden am i²c Interface links oben angeschlossen. Mein Display musste statt an 3V an 5V angeschlossen werden. Der Rest muss an 3V angeschlossen werden. 5 = +5V | L = SCL | A = SDA | 3 = +3V | G = GND.
Das Keypad wird unten rechts an den Kontakten 1-7 angeschlossen. Bei meinem Keybad sieht die Kontaktierung wie folgt aus. Das kann je nach Keypad abweichen, bitte selbst prüfen:
Messpunkte
- M1 : 12V Versorgungsspannung
- M2 : 5V Spannung nach dem DC-Wandler
- M3 : Basis des NPN Transistors T1
- M4 : G32 des ESP32 zur Steuerung des T1
- M5 : Kollektor des NPN Transistors T1, der mit dem Gate des IRF5210 verbunden ist.
- M6 : Spannung die am Elektroschloss Öffner Kontakt anliegt.
- M7 : Elektroschloss Schloss Status Kontakt.
Gehäuse
Platine
Die Platine findet in einer einfachen Box Platz.
Box
Die Box habe ich aus Holz gebaut. An der Tür befindet sich ein Knauf, um sie zu öffnen.
NFC Modul
Das PN532 Modul wird von hinten in eine 3D gedrucktes gehäuse eingesetzt und dann fixiert. Dazu kann entweder Kleber oder, so wie ich es gemacht habe erhitztes Filament verwendet werden.
Display und Keypad
Display und Keypad werden zusammen in einem Gehäuse platziert. Beim Display handelt es sich , wie bereits oben beschrieben, um eines vom Typ LiquidCrystal 2×16 mit i²c. Das Display wird verschraubt, das Keypad mit Dichtmasse eingeklebt.
Fotos
Video
Home Assistant
rest:
- scan_interval: 60
resource: http://<ESP32IP>/jsondoaction
sensor:
- name: "ESP32 NFC Code Box - Door State"
value_template: "{{ value_json.doorstate | int }}"
- name: "ESP32 NFC Code Box - Last Failed"
value_template: "{{ value_json.lastfailed }}"
- name: "ESP32 NFC Code Box - Last Granted"
value_template: "{{ value_json.lastgranted }}"
- name: "ESP32 NFC Code Box - Free RAM"
unit_of_measurement: "bytes"
value_template: "{{ value_json.freeram | int }}"
input_text:
access_card_001:
name: "Access Card 001"
access_card_002:
name: "Access Card 002"
access_card_003:
name: "Access Card 003"
access_card_004:
name: "Access Card 004"
access_card_005:
name: "Access Card 005"
access_card_006:
name: "Access Card 006"
access_card_007:
name: "Access Card 007"
access_code_001:
name: "Access Code 001"
access_code_002:
name: "Access Code 002"
access_code_003:
name: "Access Code 003"
access_code_004:
name: "Access Code 004"
access_code_005:
name: "Access Code 005"
access_code_006:
name: "Access Code 006"
access_code_007:
name: "Access Code 007"
Code
Im Code müssen einige Anpassungen vorgenommen werden, wie Beispielsweise Authentication Bearer für den HomeAssistant, dessen IP und Port, WLAN-Name und -Passwort, sowie ggf. Namen der Felder im HomeAssistant. Optional kann ein BME280 Sensor an i²c angeschlossen werden, dieser kann mit der Option “enableBme = true” aktiviert werden. Danach stehen die Werte des BME280 auf dem Webserver des ESP32 in JSON Form zur Verfügung.
#include <TaskScheduler.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <Wire.h>
#include <PN532_I2C.h>
#include <PN532.h>
//#include <NfcAdapter.h>
#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
// --------- WIFI -----------
#define STASSID "" // wifi name
#define STAPSK "" // wifi pw
#define DEVICENAME "ESP32-NFC-Code-Box";
unsigned long previousMillis = 0;
unsigned long interval = 2000;
// --------- END WIFI -------
// --------- INITS -------
const char* ssid = STASSID;
const char* password = STAPSK;
const char* deviceName = DEVICENAME;
WebServer server(80);
StaticJsonDocument<1024> jsonDocument;
char jsonBuffer[1024];
PN532_I2C pn532i2c(Wire);
PN532 nfc(pn532i2c);
int lcdColumns = 16;
int lcdRows = 2;
LiquidCrystal_I2C lcd(0x27, lcdColumns, lcdRows);
StaticJsonDocument<1024> jsonHADocument;
const String HomeAssistantBearerName = "Authorization";
const String HomeAssistantBearerContent = "Bearer xyzBearerHomeAssistantxyz....";
const String SendApiIotUrl = "http://<homeAssistantIp:Port>/api/states/";
const String AccessCard001Name = "input_text.access_card_001";
const String AccessCard002Name = "input_text.access_card_002";
const String AccessCard003Name = "input_text.access_card_003";
const String AccessCard004Name = "input_text.access_card_004";
const String AccessCard005Name = "input_text.access_card_005";
const String AccessCard006Name = "input_text.access_card_006";
const String AccessCard007Name = "input_text.access_card_007";
const String AccessCode001Name = "input_text.access_code_001";
const String AccessCode002Name = "input_text.access_code_002";
const String AccessCode003Name = "input_text.access_code_003";
const String AccessCode004Name = "input_text.access_code_004";
const String AccessCode005Name = "input_text.access_code_005";
const String AccessCode006Name = "input_text.access_code_006";
const String AccessCode007Name = "input_text.access_code_007";
const String DoorStateName = "sensor.esp32_nfc_code_box_door_state";
const String LastFailedName = "sensor.esp32_nfc_code_box_last_failed";
const String LastGrantedName = "sensor.esp32_nfc_code_box_last_granted";
const byte ROWS = 4; // Four rows
const byte COLS = 3; // Three columns
// Define the Keymap
char keys[ROWS][COLS] =
{
{'1','2','3'},
{'4','5','6'},
{'7','8','9'},
{'*','0','#'}
};
byte rowPins[ROWS] = { 25, 17, 18, 27 };
byte colPins[COLS] = { 26, 33, 19 };
// Create the Keypad
Keypad kpd = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS );
bool displayEnabled = true;
unsigned long lastMillsKeyEnter = 0;
unsigned long displayOffInterval = 60000;
// --------- END INITS -------
// --------- SCHEDULER BEGIN -------
void checkFreeRam();
Task scheduleCheckFreeRam(21*1000, TASK_FOREVER, &checkFreeRam);
void wifiReconnectCheck();
Task scheduleWifiReconnectCheck(5*1000, TASK_FOREVER, &wifiReconnectCheck);
void retrieveAccess();
Task scheduleRetrieveAccess(50*1000, TASK_FOREVER, &retrieveAccess);
void getBme();
Task scheduleGetBme(50*1000, TASK_FOREVER, &getBme);
void reportDoorStateChange();
Task scheduleReportDoorStateChange(2*1000, TASK_FOREVER, &reportDoorStateChange);
Scheduler runner;
// --------- SCHEDULER END ---------
// ------- DEFINITIONS ----------
static String bme280State = "State";
static String bme280Enabled = "enabled";
static String bme280Disabled = "disabled";
static String temperatureName = "Temperature";
static String temperatureName2 = "°C";
static String humidityName = "Humidity";
static String humidityName2 = "%";
static String pressureName = "Pressure";
static String pressureName2 = "hPa";
static String linkColorNormal = "#2321B0";
static String linkColorVisited = "#2321B0";
static String activeMarkerBegin = "<b>»";
static String activeMarkerEnd = "«</b>";
// ------- END DEFINITIONS ----------
// ------- PINS ----------
static int morsePin = 2;
static int i2cSdaPin = 21;
static int i2cSclPin = 22;
static int relaisPin = 32;
static int statusPin = 34;
// ------- END PINS ----------
// --------- Variables ---------
static bool enableBme = false;
String bmeTemp = String("");
String bmeHum = String("");
String bmePress = String("");
Adafruit_BME280 bme; // I2C
int freeHeap = 0;
bool stateDoor = false;
bool stateRelais = false;
// Working
String accessCard001 = "";
String accessCard002 = "";
String accessCard003 = "";
String accessCard004 = "";
String accessCard005 = "";
String accessCard006 = "";
String accessCard007 = "";
String accessCode001 = "";
String accessCode002 = "";
String accessCode003 = "";
String accessCode004 = "";
String accessCode005 = "";
String accessCode006 = "";
String accessCode007 = "";
bool needForChange = true;
bool selectCard = false;
bool cardAccess = false;
bool selectCode = false;
bool codeAccess = false;
bool accessDenied = false;
String codeString = "";
String lastFailed = "";
String lastGranted = "";
// --------- END Variables ---------
void setup()
{
Serial.println("Setup");
initSerial();
initWifi();
initPinModes();
initSchedules();
initServer();
initBme();
initNfc();
initKeyBoard();
initLcd();
checkFreeRam();
Serial.println("END Setup");
}
// --------------- LOOP ---------------
void loop()
{
server.handleClient();
runner.execute();
getKeyBoard();
writeLcd();
getNfcCard();
disableDisplayIfNeeded();
}
// --------------- INITS ---------------
void initSerial()
{
Serial.begin(115200);
while(!Serial){} // Waiting for serial connection
Serial.println();
}
void initWifi()
{
Serial.println("WiFi init");
Serial.print("Wifi: ");
Serial.println(ssid);
Serial.println("turn wifi off...");
WiFi.mode(WIFI_OFF);
delay(200);
WiFi.mode(WIFI_STA);
delay(250);
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
delay(200);
Serial.println("setting hostname");
WiFi.setHostname(deviceName);
delay(200);
Serial.println("Connecting to WiFi..");
WiFi.begin(ssid, password);
delay(200);
int iCounter = 0;
int iMax = 30;
while (WiFi.status() != WL_CONNECTED && iCounter < iMax)
{
digitalWrite(morsePin, LOW);
delay(500);
digitalWrite(morsePin, HIGH);
delay(500);
Serial.print(".");
iCounter++;
}
Serial.println(WiFi.localIP());
}
void initPinModes()
{
Serial.println("PIN MODES init");
pinMode(morsePin, OUTPUT);
digitalWrite(morsePin, LOW);
pinMode(relaisPin, OUTPUT);
digitalWrite(relaisPin, LOW);
pinMode(statusPin, INPUT);
}
void initSchedules()
{
Serial.println("SCHEDULES init");
runner.init();
runner.addTask(scheduleCheckFreeRam);
scheduleCheckFreeRam.enable();
runner.addTask(scheduleWifiReconnectCheck);
scheduleWifiReconnectCheck.enable();
runner.addTask(scheduleGetBme);
scheduleGetBme.enable();
runner.addTask(scheduleRetrieveAccess);
scheduleRetrieveAccess.enable();
runner.addTask(scheduleReportDoorStateChange);
scheduleReportDoorStateChange.enable();
}
void initServer()
{
Serial.println("HTTP server init");
server.on("/", handleConnect);
server.on("/jsondoaction", jsonDoAct);
server.on("/jsondoaction", HTTP_POST, jsonDoAct);
server.onNotFound(handleConnect);
server.begin();
Serial.println("HTTP server started");
}
void initBme()
{
Serial.println("BME280 init");
// BME 280
if (enableBme == true)
{
for(int i = 0; i < 10; i++)
{
Serial.println("detecting BME280...");
delay(500);
if (bme.begin(0x76))
{
i = 10;
}
}
}
}
void initNfc()
{
Serial.println("NFC init");
nfc.begin();
// Connected, show version
uint32_t versiondata = nfc.getFirmwareVersion();
if (!versiondata)
{
Serial.println("PN53x card not found!");
return;
}
nfc.setPassiveActivationRetries(0xFF);
// configure board to read RFID tags
nfc.SAMConfig();
}
void initLcd()
{
Serial.println("LCD init");
lcd.init();
// turn on LCD backlight
lcd.backlight();
// set cursor to first column, first row
lcd.setCursor(0, 0);
// print message
lcd.print("# for code");
lcd.setCursor(0, 1);
lcd.print("* for card");
lastMillsKeyEnter = millis();
}
void initKeyBoard()
{
Serial.println("KEYBOARD init");
}
// --------------- SCHEDULER ---------------
void checkFreeRam()
{
freeHeap = ESP.getFreeHeap();
if (ESP.getFreeHeap() < 60000)
{
ESP.restart();
}
//Serial.println("Free RAM: " + String(ESP.getFreeHeap()));
}
void wifiReconnectCheck()
{
unsigned long currentMillis = millis();
// if WiFi is down, try reconnecting every CHECK_WIFI_TIME seconds
if ((WiFi.status() != WL_CONNECTED) && (currentMillis - previousMillis >=interval))
{
Serial.print(millis());
Serial.println("Reconnecting to WiFi...");
WiFi.disconnect();
WiFi.reconnect();
previousMillis = currentMillis;
}
}
void getBme()
{
if (enableBme == true)
{
Serial.println("Refresh BME Vals");
String zBmeTemp = String(bme.readTemperature());
String zBmeHum = String(bme.readHumidity());
String zBmePress = String(bme.readPressure() / 100.0F);
bmeTemp = zBmeTemp;
bmeHum = zBmeHum;
bmePress = zBmePress;
Serial.println("END - Refresh BME Vals");
}
}
void retrieveAccess()
{
Serial.println("retrieve Access...");
accessCard001 = getStringHttp(AccessCard001Name);
//Serial.println("AccessCard001: " + (String)accessCard001);
accessCard002 = getStringHttp(AccessCard002Name);
//Serial.println("AccessCard002: " + (String)accessCard002);
accessCard003 = getStringHttp(AccessCard003Name);
//Serial.println("AccessCard003: " + (String)accessCard003);
accessCard004 = getStringHttp(AccessCard004Name);
//Serial.println("AccessCard004: " + (String)accessCard004);
accessCard005 = getStringHttp(AccessCard005Name);
//Serial.println("AccessCard005: " + (String)accessCard005);
accessCard006 = getStringHttp(AccessCard006Name);
//Serial.println("AccessCard006: " + (String)accessCard006);
accessCard007 = getStringHttp(AccessCard007Name);
//Serial.println("AccessCard007: " + (String)accessCard007);
accessCode001 = getStringHttp(AccessCode001Name);
//Serial.println("AccessCode001: " + (String)accessCode001);
accessCode002 = getStringHttp(AccessCode002Name);
//Serial.println("AccessCode002: " + (String)accessCode002);
accessCode003 = getStringHttp(AccessCode003Name);
//Serial.println("AccessCode003: " + (String)accessCode003);
accessCode004 = getStringHttp(AccessCode004Name);
//Serial.println("AccessCode004: " + (String)accessCode004);
accessCode005 = getStringHttp(AccessCode005Name);
//Serial.println("AccessCode005: " + (String)accessCode005);
accessCode006 = getStringHttp(AccessCode006Name);
///Serial.println("AccessCode006: " + (String)accessCode006);
accessCode007 = getStringHttp(AccessCode007Name);
//Serial.println("AccessCode007: " + (String)accessCode007);
}
void getNfcCard()//void * pvParameters
{
//while(true)
//{
if (selectCard == true)
{
Serial.println("getNfcCard...");
bool success;
// Buffer to store the UID
String uidStr = "";
char uidChar;
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };
// UID size (4 or 7 bytes depending on card type)
uint8_t uidLength;
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, &uid[0], &uidLength, 5000);
// If the card is detected, print the UID
if (success)
{
Serial.println("Card Detected");
Serial.print("Size of UID: "); Serial.print(uidLength, DEC);
Serial.println(" bytes");
Serial.print("UID: ");
uidStr = uintMacToHexString(uid, uidLength);
Serial.println("UID: " + uidStr);
Serial.println("");
Serial.println("");
handleAccess(uidStr, "");
delay(200);
initNfc();
}
else
{
// PN532 probably timed out waiting for a card
// Serial.println("Timed out waiting for a card");
}
selectCard = false;
needForChange = true;
}
}
void reportDoorStateChange()
{
int curDoorState = digitalRead(statusPin);
if (curDoorState == 0 && stateDoor == true)
{
sendStatusHttp(DoorStateName, "0", false);
}
else if (curDoorState == 1 && stateDoor == false)
{
sendStatusHttp(DoorStateName, "1", false);
}
if (curDoorState == 0)
{
stateDoor = false;
}
else
{
stateDoor = true;
}
}
void getKeyBoard()
{
char key = kpd.getKey();
if(key) // Check for a valid key.
{
if (displayEnabled == false)
{
if (key == '0')
{
lastMillsKeyEnter = millis();
needForChange = true;
}
}
else
{
lastMillsKeyEnter = millis();
//Serial.println("selectCard " + String(selectCard));
//Serial.println("selectCode " + String(selectCode));
if (selectCard == false && selectCode == false)
{
Serial.println(key);
switch (key)
{
case '*':
//digitalWrite(ledpin, LOW);
selectCard = true;
selectCode = false;
break;
case '#':
//digitalWrite(ledpin, HIGH);
selectCard = false;
selectCode = true;
break;
//default:
//Serial.println(key);
}
}
else if (selectCode == true)
{
Serial.println(key);
if (key == '#')
{
// check code access
//codeAccess = checkAccessCode(codeString);
//selectCode = false;
handleAccess("", codeString);
/*if (codeAccess == false)
{
accessDenied = true;
}*/
}
else
{
if (codeString.length() < 16)
{
codeString.concat(key);
}
//Serial.println("codeString " + String(codeString));
}
}
needForChange = true;
}
}
else
{
//Serial.print(".");
}
}
void writeLcd()
{
//Serial.println("LCD write");
// set cursor to first column, first row
if (displayEnabled == false)
{
return;
}
if (needForChange == true)
{
lcd.clear();
if (selectCard == true)
{
lcd.setCursor(0, 0);
lcd.print("reading card...");
needForChange = false;
}
else if (cardAccess == true)
{
lcd.setCursor(0, 0);
lcd.print("Card:");
lcd.setCursor(0, 1);
lcd.print("access granted!");
openGate();
resetValsToNormal();
delay(3000);
}
else if (selectCode == true)
{
lcd.setCursor(0, 0);
lcd.print("Code + #:");
lcd.setCursor(0, 1);
lcd.print(codeString);
needForChange = false;
}
else if(codeAccess == true)
{
lcd.setCursor(0, 0);
lcd.print("Code:");
lcd.setCursor(0, 1);
lcd.print("access granted!");
openGate();
resetValsToNormal();
delay(3000);
}
else if (accessDenied)
{
lcd.setCursor(0, 1);
lcd.print("access denied!");
resetValsToNormal();
delay(3000);
}
else
{
lcd.setCursor(0, 0);
// print message
lcd.print("# for code");
lcd.setCursor(0, 1);
lcd.print("* for card");
needForChange = false;
}
}
}
void disableDisplayIfNeeded()
{
unsigned long curMillis = millis();
if (displayEnabled == true && lastMillsKeyEnter > curMillis)
{
lastMillsKeyEnter = curMillis;
return;
}
if (displayEnabled == false && lastMillsKeyEnter > curMillis)
{
lastMillsKeyEnter = 0;
return;
}
if ( (lastMillsKeyEnter + displayOffInterval) < curMillis )
{
if (displayEnabled == true)
{
displayEnabled = false;
lcd.clear();
lcd.noBacklight();
resetValsToNormal();
}
}
else
{
if (displayEnabled == false)
{
displayEnabled = true;
needForChange = true;
lcd.backlight();
}
}
}
void resetValsToNormal()
{
needForChange = true;
selectCard = false;
cardAccess = false;
selectCode = false;
codeAccess = false;
accessDenied = false;
codeString = "";
}
void openGate()
{
digitalWrite(relaisPin, LOW);
digitalWrite(relaisPin, HIGH);
delay(500);
digitalWrite(relaisPin, LOW);
sendStatusHttp(LastGrantedName, lastGranted, true);
delay(1000);
reportDoorStateChange();
}
// --------------- HANDLE ---------------
void handleAccess(String cardUidIn, String codeIn)
{
if (checkAccessCard(cardUidIn) == true)
{
Serial.println("Access Garanted by Card");
cardAccess = true;
selectCard = false;
selectCode = false;
needForChange = true;
lastGranted = "Card:" + cardUidIn;
}
else if (checkAccessCode(codeIn) == true)
{
Serial.println("Access Garanted by PIN");
codeAccess = true;
selectCard = false;
selectCode = false;
needForChange = true;
lastGranted = "Code:" + codeIn;
}
else
{
Serial.println("Access Denied!");
accessDenied = true;
selectCard = false;
selectCode = false;
needForChange = true;
lastFailed = "";
if (cardUidIn != "")
{
lastFailed = "Card:" + cardUidIn;
}
if (codeIn != "")
{
lastFailed = "Code:" + codeIn;
}
sendStatusHttp(LastFailedName, lastFailed, true);
}
}
bool checkAccessCard(String cardUidIn)
{
if (cardUidIn.length() > 0)
{
if (cardUidIn == accessCard001 ||
cardUidIn == accessCard002 ||
cardUidIn == accessCard003 ||
cardUidIn == accessCard004 ||
cardUidIn == accessCard005 ||
cardUidIn == accessCard006 ||
cardUidIn == accessCard007)
{
return true;
}
}
return false;
}
bool checkAccessCode(String codeIn)
{
if (codeIn.length() > 0)
{
if (codeIn == accessCode001 ||
codeIn == accessCode002 ||
codeIn == accessCode003 ||
codeIn == accessCode004 ||
codeIn == accessCode005 ||
codeIn == accessCode006 ||
codeIn == accessCode007)
{
return true;
}
}
return false;
}
// --------------- SERVER ---------------
void handleConnect()
{
Serial.println("Connect");
server.send(200, "text/html", SendHTML(""));
}
String SendHTML(String context)
{
String ptr = "<!DOCTYPE html> <html>\n";
ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
ptr +="<title>ESP32 NFC Code Box</title>\n";
ptr +="<style>html { font-family: Arial; display: inline-block; margin: 0px auto; text-align: center;}\n";
ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
ptr +=".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
ptr +="a, a:active, { color: " + linkColorNormal + "; text-decoration: underline; }\n";
ptr +="a:visited { color: " + linkColorVisited + "; text-decoration: underline; }\n";
ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
ptr +="</style>\n";
ptr +="</head>\n";
ptr +="<body>\n";
ptr +="<h1>ESP32 NFC Code Box</h1>\n";
String lineBreak = "<br><br>";
ptr += "";
ptr += "<br>";
ptr += lineBreak;
ptr +="<b>BME 280</b><br>\n";
ptr += bme280State + ": ";
if (enableBme == true)
{
ptr += bme280Enabled;
ptr += "<br>";
ptr += temperatureName + ": ";
ptr += bmeTemp + temperatureName2 + "<br>";
ptr += humidityName + ": ";
ptr += bmeHum + humidityName2 + "<br>";
ptr += pressureName + ": ";
ptr += bmePress + pressureName2 + "<br>";
}
else
{
ptr += bme280Disabled;
ptr += "<br>";
}
ptr += lineBreak;
ptr += "<b>FreeHeap</b><br>\n";
ptr += (String)freeHeap + " bytes";
ptr +="</body>\n";
ptr +="</html>\n";
return ptr;
}
void jsonDoAct()
{
Serial.println("JSON Act");
if (server.hasArg("plain") == false)
{
//handle error here
}
String body = server.arg("plain");
Serial.println(body);
deserializeJson(jsonDocument, body);
createStatusJson("");
server.send(200, "application/json", jsonBuffer);
}
void createStatusJson(String statusIn)
{
if (statusIn == "")
{
statusIn = "OK";
}
jsonDocument.clear();
jsonDocument["state"] = statusIn;
jsonDocument["enableBme"] = enableBme;
jsonDocument["doorstate"] = digitalRead(statusPin);
jsonDocument["lastfailed"] = lastFailed;
jsonDocument["lastgranted"] = lastGranted;
if (enableBme == true &&
bmePress != String("") &&
bmePress.toDouble() > 0)
{
jsonDocument["bmeTemp"] = bmeTemp.toDouble();
jsonDocument["bmeHum"] = bmeHum.toDouble();
jsonDocument["bmePress"] = bmePress.toDouble();
}
jsonDocument["freeram"] = freeHeap;
serializeJson(jsonDocument, jsonBuffer);
}
// --------------- HELPER ---------------
String uintMacToHexString(uint8_t values[], uint8_t length)
{
String retStr = "";
if (length < 1)
{
return retStr;
}
Serial.println("Length: " + String(length));
for (uint8_t i = 0; i < length; i++)
{
String converted = "";
Serial.println(values[i], HEX);
converted = String(values[i], HEX);
if (converted.length() < 2)
{
converted = (String)"0" + (String)converted;
}
retStr += (String)converted;
if (i < length - 1)
{
retStr += ":";
}
}
retStr.toUpperCase();
return retStr;
}
String getStringHttp(String attributeStringIn)
{
String retStr;
HTTPClient *http = new HTTPClient();
http->setReuse(false);
if (http->begin(SendApiIotUrl + attributeStringIn))
{
//Serial.print("[HTTP] GET...\n");
http->setTimeout(5000);
// start connection and send HTTP header
http->addHeader("Content-Type", "application/json; charset=UTF-8");
http->addHeader(HomeAssistantBearerName, HomeAssistantBearerContent); // auth
//Serial.println("Content: " + sendContent);
int httpCode = http->GET();
// httpCode will be negative on error
if (httpCode > 0)
{
// file found at server
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY)
{
//String payload = http->getString();
//Serial.println(payload);
retStr = http->getString();
//Serial.println(retStr);
deserializeJson(jsonHADocument, retStr);
if (jsonHADocument.containsKey("state") == true)
{
retStr = jsonHADocument["state"].as<String>();
}
else
{
retStr = "";
}
}
}
else
{
Serial.printf("[HTTP] GET... failed, error: %s\n", http->errorToString(httpCode).c_str());
retStr = "error: ";
retStr += http->errorToString(httpCode).c_str();
}
http->end();
}
else
{
//Serial.printf("[HTTP] Unable to connect\n");
retStr = "";
}
delete http;
return retStr;
}
String sendStatusHttp(String attributeStringIn, String valueIn, bool isString)
{
String retStr;
HTTPClient *http = new HTTPClient();
http->setReuse(false);
Serial.print("[HTTP] begin...\n");
Serial.println("[HTTP] URL: " + SendApiIotUrl + attributeStringIn);
if (http->begin(SendApiIotUrl + attributeStringIn))
{
Serial.print("[HTTP] POST...\n");
http->setTimeout(5000);
// start connection and send HTTP header
http->addHeader("Content-Type", "application/json; charset=UTF-8");
http->addHeader(HomeAssistantBearerName, HomeAssistantBearerContent); // auth
// status Json
String sendContent = "";
if (isString == true)
{
sendContent = "{\"state\": \"" + valueIn + "\" }";
}
else
{
sendContent = "{\"state\": " + valueIn + " }";
}
Serial.println("Content: " + sendContent);
int httpCode = http->POST(sendContent);
// httpCode will be negative on error
if (httpCode > 0)
{
Serial.printf("[HTTP] POST... code: %d\n", httpCode);
// file found at server
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY)
{
//String payload = http->getString();
//Serial.println(payload);
retStr = http->getString();
}
}
else
{
Serial.printf("[HTTP] GET... failed, error: %s\n", http->errorToString(httpCode).c_str());
retStr = "error: ";
retStr += http->errorToString(httpCode).c_str();
}
http->end();
}
else
{
Serial.printf("[HTTP] Unable to connect\n");
retStr = "ConErr";
}
delete http;
return retStr;
}
Hinweis
Dieser Artikel dokumentiert lediglich meinen Aufbau. Für den Nachbau, die Nutzung einzelner Komponenten, die Platinen und den gesamten Inhalt wird die Haftung in jeglicher Form ausgeschlossen. Der Aufbau erhebt zudem nicht den Anspruch irgend ein Level von Sicherheit zu gewährleisten.