Öffnen einer Tür und schalten der Anwesenheit mit Zippato-RFID

Hallo

So nun habe ich für mich die Fertige Lösung.
Ich habe das noch erweitert um eine Checksummen Kontrolle des Datenverkehres.
Erst wollte ich noch eine Verschlüsselung ala ENIGMA einbauen aber das war mir dann doch zu Oversized für so eine Aufgabe.
Ich hoffe es kann einer gebrauchen zumindest zum abschauen wie so etwas gemacht werden kann.

Hier der HC3 Teil

--[[
                       ESP32_RFID_Garage QA
                       ====================
Der ESP32 liest einen RFID-Code mit hilfe eines "RFID-RC522" ein.

Die Werte werden im ESP32 vorverarbeitet und stehen als direkter Wert im QA zur Verfügung.
Die Datenkommunikation findet über WLAN per Json-Datenpacket verschlüsselt statt.
Der ESP meldet sich hierbei beim Router an und holt sich eine IP-Adresse.
Diese Adresse muss bei den Variablen angegeben werden.
Das WLAN-Passwort wie auch der SSID vom Router werden im Code des ESP32 angegeben.
Daraufhin holt sich der QA Zyklisch (Variabel intervall), die Daten vom ESP32.
In den Variabeln "Name_RFID-x" steht der Name und die Kartennummer getrennt von einem ";"".

Wenn ein RFID-Tag mit den Vorgegebenen übereinstimmt so wird ein Beeper der am ESP32 angeschlossen ist
für 20ms aktiv. Ein Falscher RFID-Tag erzeugt keine Reaktion und der Beeper giebt ein 600ms Signal ab. 
Sollte die überein stimmen so wird die Variabel ID-value true und geht nach 500ms wieder auf false.
Somit kann über ID-value getriggert werden.

Mit der Taste ESP-Daten senden, ist es möglich die RFID-Daten die in den Variabeln stehen dem ESP32 zu senden.
Dieser hat danach die Neuen Daten zum vergleichen zur verfügung.
 
Es ist aber auch möglich die Daten des Lesevorganges vorab zu erhalten.
Diesen Vorgang kann aber auch mit der Taste "Refresh" aktiviert werden.
Dann werden die gelesenen Daten wie bei der Zyklischen übermittlung bereit gestellt.

Eine Erweiterung ist ohne weiteres möglich da der ESP noch einen Haufen von Ein- und Ausgängen hat 
ist da noch so vieles möglich.

Die beiden Ausgänge out_1 und out_2 dienen nur zur funktionskontrolle und blinken auf dem ESP32 im wechsel.
Dieser Wechel wird von diesem QA ausgelöst, per Json an den ESP32 gesendet und dort mit den beiden LED´s angezeigt.

Version   : ESP32_RFID_Garage V2.1 
Ersteller : Frank Berges
Datum     : 07.04.2021
* --------------------------------------------------------------------------
* Enderungsindex
* V1.0  06.04.2021  F.B.
* V2.0  16.04.2021  F.B.
*                   - Datenverkehr verschluesselt HC3 <-> ESP
*                     Auswertung findet jetzt im ESP32 stat und nicht mehr im HC3
*                     Die Daten für den RFID-Vergleich werden vom HC3 übermittelt
*                     und im Flash abgespeichert, somit stehen diese nach jebem
*                     Neuen Einschalten sofort zurverfügung und müssen nicht
*                     neu Übermittelt werden. Ein Übermittlungsbefehl wird vom 
*                     QA aus über einen Button angestossen.
* V2.1  21.4.2021   F.B.
*                   - Alle Daten-Packete im Wlan mit Checksumm überwacht
*                     HC3 <-> ESP32
*                     Bei falscher Checksumm wird auf das Nächste Packet gewartet
*                   - Fehler beim Abspeichern der RFID Werte im Flash behoben
*                   - Fehlermeldung Checksumm und Datenpacket hinzugefügt
*
* ============================================================================= 
            
--]]

--******************* QuickApp Initialisieren ********************
------------------------------------------------------------------
function QuickApp:onInit()
    self:debug("onInit")
    
    -- Setup-Phase
    self:getQuickAppVariables()
    Daten_1 = ""    --Besitzer des RFID
    Daten_2 = ""    --gelesener Code des RFID
    Daten_3 = ""    --Letzter gelesene Code
        -- Dauerlaufschleife
    --******************   
    local function loop()
        self:getData() --ESP32 Daten holen und senden
        setTimeout(loop, self.looptime)
    end
    if self.ip ~= "changeme" then loop() end
end
------------------------------------------------------------------

--------------------- Alle Variabel einholen --------------------
------------------------------------------------------------------
function QuickApp:getQuickAppVariables() -- Get all variables 
    local ipAddress = self:getVariable("ipAddress")
    local path = "/data.json" -- Default path is /data.json
    interval = tonumber(self:getVariable("interval"))
    self.looptime = interval * 1000 --Angabe in 1000ms
    httpTimeout = 5 -- Default value for http Timeout
    userID = tonumber(self:getVariable("userID")) 
    debugLevel = tonumber(self:getVariable("debugLevel"))
    local icon = tonumber(self:getVariable("icon")) 
    out_1 = 0
    out_2 = 0
    
    -- Plausibilität der Variabeln überprüfen und im Zweifel mit default füllen
    if ipAddress == "" or ipAddress == nil then
        ipAddress = "192.168.2.82" -- Default IP address ist 192.168.2.82
        self:setVariable("ipAddress",ipAddress)
        self:trace("Added QuickApp variable ipAddress")
    end
    if interval == "" or interval == nil then
        interval = "3" -- Default interval is 3, normalerweise wir der RFID-Sensor alle 3S abgefragt
        self:setVariable("interval",interval)
        self:trace("Added QuickApp variable interval")
        interval = tonumber(interval)
    end
    if userID == "" or userID == nil then 
        userID = "2" -- Default userID
        self:setVariable("userID",userID)
        self:trace("Added QuickApp variable iserID")
        userID = tonumber(userID)
    end
    if debugLevel == "" or debugLevel == nil then
        debugLevel = "1" -- Default value for debugLevel response in seconds
        self:setVariable("debugLevel",debugLevel)
        self:trace("Added QuickApp variable debugLevel")
        debugLevel = tonumber(debugLevel)
    end
    if icon == "" or icon == nil then 
        icon = "0" -- Default icon
        self:setVariable("icon",icon)
        self:trace("Added QuickApp variable icon")
        icon = tonumber(icon)
    end
    if icon ~= 0 then 
        self:updateProperty("deviceIcon", icon) -- set user defined icon 
    end

    url = "http://" ..ipAddress ..path
    self.httpClient = net.HTTPClient() 
end
------------------------------------------------------------------

------------------------ Debuging & Level ------------------------
------------------------------------------------------------------
function QuickApp:logging(level,text) -- Logging function for debug
  if tonumber(debugLevel) >= tonumber(level) then 
      self:debug(text)
  end
end
------------------------------------------------------------------

----------------------- Daten im HC3 Update ----------------------
------------------------------------------------------------------
function QuickApp:updateProperties() -- Update properties
  self:logging(3,"updateProperties")
  self:updateProperty("log", os.date("%d-%m-%Y %X", os.time()-jsonTable.age) .."\nWiFi " ..jsonTable.sensordatavalues[3].value .."dBm")
end
------------------------------------------------------------------

---------------------- Labelfeld beschreiben ---------------------
------------------------------------------------------------------
function QuickApp:updateLabels() -- Update labels
  self:logging(3,"updateLabels")
  labelText = "Measurement: "..os.date("%d-%m-%Y %X", os.time()-jsonTable.age) .."\n" .."\n"
  labelText = labelText .."RFID - 1         : " ..jsonTable.sensordatavalues[1].value .."\n"
  labelText = labelText .."RFID - 2         : " ..jsonTable.sensordatavalues[2].value .."\n"
  labelText = labelText .."Letzter RFID     : " ..Daten_3 .."\n"
  labelText = labelText .."WiFi signal      : " ..jsonTable.sensordatavalues[3].value .." dBm" .."\n"
  labelText = labelText .."Output 1         : " ..jsonTable.out_1 .."\n"
  labelText = labelText .."Output 2         : " ..jsonTable.out_2 .."\n".."\n"
  labelText = labelText .."Firmwhareversion : " ..jsonTable.software_version 
  self:logging(2,"labelText: " ..labelText)
  self:updateView("label1", "text", labelText)
end
------------------------------------------------------------------

-------------------- Refresch Taste abfragen ---------------------
------------------------------------------------------------------
function QuickApp:button1Event()
  self:updateView("button1", "text", "Please wait...")
  self:getData()
  fibaro.setTimeout(2000, function() -- Pause for [timeout] seconds (default 5 seconds)
    self:updateView("button1", "text", "Refresh")
  end)
end
------------------------------------------------------------------

---------------- ESP-Daten senden Taste abfragen -----------------
------------------------------------------------------------------
function QuickApp:button2Event()
  self:updateView("button2", "text", "Please wait...")
  self:getMemo()
  fibaro.setTimeout(2000, function() -- Pause for [timeout] seconds (default 5 seconds)
    self:updateView("button2", "text", "ESP-Daten senden")
  end)
end
------------------------------------------------------------------

------------- Daten für ESP32 zum senden vorbereiten -------------
------------------------------------------------------------------
function QuickApp:sendeDaten_zusammenstellen()
    --Gewünschte  Ausgangszustände des ESP32 einstellen
    if out_1 == 0 then
        out_1 = 1
        out_2 = 0
    else
        out_1 = 0
        out_2 = 1
    end

    -- json-Datenstring für das Versenden zusammen bauen
    --print("Time = ",os.date("%H:%M:%S", os.time()))
    local daten = 
        {
            time = os.date("%H:%M:%S", os.time()),
            out_1 = out_1,
            out_2 = out_2,
        }
    self:logging(3,"sendeDaten-json : " ..json.encode(daten))
    
    --zu sendende Daten verschlüsseln
    local Ergebnis = json.encode(daten)
    Ergebnis = Base64enc(Ergebnis)

 return Ergebnis
end
------------------------------------------------------------------

---------- Memo-Daten für ESP32 zum senden vorbereiten -----------
------------------------------------------------------------------
function QuickApp:sendeMemo_zusammenstellen()

    -- json-Datenstring für das Versenden zusammen bauen
    local Memo_Sende = "{\"time\" :\""..os.date("%H:%M:%S", os.time())
          Memo_Sende = Memo_Sende .."\",\"Codedaten\" :["
    --Alle Variabeln holen
    local a = json.encode(api.get("/devices/"..self.id).properties.quickAppVariables)
    a=json.decode(a)
    --Alle Vraiabeln durchsuchen
    for _,d in ipairs(a) do
        --print("Data Nr. ",_," = ",a[_].name," ",a[_].value)
        if string.match(a[_].name, "RFID") ~= nil then -- ist das ein RFID Datensatz
            local Semikolon = string.find(a[_].value, ";") --Semikolon suchen
            local RFID_Name = string.sub(a[_].value,0,Semikolon-1)
            local RFID_Code = string.sub(a[_].value,Semikolon+1,string.len(a[_].value))
            
            Memo_Sende = Memo_Sende .."{\"RFID_Name\" :\""..RFID_Name.."\",\"RFID_Code\" :\""..RFID_Code.."\"},"
        end
    end
    Memo_Sende = string.sub(Memo_Sende,0,string.len(Memo_Sende)-1)
    Memo_Sende = Memo_Sende .."]}" -- json-String schließen

    --zu sendende Daten verschlüsseln
    local Ergebnis = Memo_Sende
    Ergebnis = Base64enc(Ergebnis)

    return Ergebnis
end
------------------------------------------------------------------

----------------- Daten vom ESP32 abholen/senden -----------------
------------------------------------------------------------------
function QuickApp:getData()
  self:logging(3,"Start getData")
  self:logging(2,"URL: " ..url)
  self.httpClient:request(url, 
    {
      options={
          headers = {
              Accept = "data/json",
              data = self:sendeDaten_zusammenstellen() -- Daten zum ESP32 senden
           },
           method = 'GET'
       },   
      success = function(response) --Daten vom ESP32 holen
      self:logging(3,"response status: " ..response.status)
      self:logging(3,"headers: " ..response.headers["Content-Type"])
      self:logging(2,"Response data: " ..response.data)

        if (response.status >= 200 and response.status < 300) then
            --self:debug("OK: " .. response.data)
            if response.data == nil or response.data == "" or response.data == "[]" then -- Check for empty result
                self:warning("Momentan Keine Daten vom ESP32_RFID_Garage")
            else --Gültige Daten erhalten 
                --Daten sind verschlüßelt also erst wieder endschlüßeln
                response.data = Base64dec(response.data)
                if response.data ~= "Fehler Checksumme" then
                    --print("response.data =",response.data)
                    jsonTable = json.decode(response.data) -- JSON decode from api to lua-table
                    --jsonTable = json.encode(response.data) -- JSON Kontrolle zeigt was alles im String ist
                    --print("jsonTable =",jsonTable)
                    self:updateLabels()     -- Anzeige der Labels
                    self:updateProperties() -- Eigene Properties abgleichen 
                    self:RFID_auswerten()   -- Daten vom ESP32 auswerten
                end
            end
        end
      end,
      error = function (error)
        self:error("error: " ..json.encode(error))
        self:updateProperty("log", "error: " ..json.encode(error))
      end  
    }) 
end --function QuickApp:getData
------------------------------------------------------------------

------------ Daten vom ESP32 abholen/Memodaten senden ------------
------------------------------------------------------------------
function QuickApp:getMemo()
  self:logging(3,"Start getMemo")
  self:logging(2,"URL: " ..url)
  self.httpClient:request(url, 
    {
      options={
          headers = {
              Accept = "memo/json",
              data = self:sendeMemo_zusammenstellen() -- Daten zum ESP32 sende
--------------------------------------------------------------------------------
           },
           method = 'GET'
       },
      success = function(response) --Daten vom ESP32 holen
      self:logging(3,"response status: " ..response.status)
      self:logging(3,"headers: " ..response.headers["Content-Type"])
      self:logging(2,"Response data: " ..response.data)
        if (response.status >= 200 and response.status < 300) then
            --self:debug("OK: " .. response.data)
            if response.data == nil or response.data == "" or response.data == "[]" then -- Check for empty result
                self:warning("Momentan Keine Daten vom ESP32_RFID_Garage")
            else --Gültige Daten erhalten
                --Daten sind verschlüßelt also erst wieder endschlüßeln
                response.data = Base64dec(response.data)
                if response.data ~= "Fehler Checksumme" then
                    jsonTable = json.decode(response.data) -- JSON decode from api to lua-table
                    --jsonTable = json.encode(response.data) -- JSON Kontrolle zeigt was alles im String ist
                    --print("jsonTable =",jsonTable)
                    self:updateLabels()     -- Anzeige der Labels
                    self:updateProperties() -- Eigene Properties abgleichen 
                    self:RFID_auswerten()   -- Daten vom ESP32 auswerten
                end
            end
        end
      end,
      error = function (error)
        self:error("error: " ..json.encode(error))
        self:updateProperty("log", "error: " ..json.encode(error))
      end  
    }) 
end --function QuickApp:getMemo
------------------------------------------------------------------

------ SELF-ID auswerten und Status auf on für 500ms ändern ------
------------------------------------------------------------------
function QuickApp:RFID_auswerten()
    Daten_1 = jsonTable.sensordatavalues[1].value
    Daten_2 = jsonTable.sensordatavalues[2].value
    if (Daten_1 ~= "Unbekannt" and Daten_1 ~= "") then
            Daten_3 = Daten_2
            self:logging(2,"Gefunden")
            self:turnOn()
            fibaro.sleep(500)
            self:turnOff()
    end
    if Daten_1 == "Unbekannt" then --Könnte ja neuer zum einlesen sein mal merken
        Daten_3 = Daten_2
    end
end --RFID_auswerten
------------------------------------------------------------------

------------------ SELF-ID Status auf on ändern ------------------
------------------------------------------------------------------
function QuickApp:turnOn()
    if self.ip ~= "changeme" then
        --self:debug("binary switch turned on")
        self:updateProperty("value", true)
    end
end --function QuickApp:turnOn
------------------------------------------------------------------

----------------- SELF-ID Status auf off ändern ------------------
------------------------------------------------------------------
function QuickApp:turnOff()
    if self.ip ~= "changeme" then
        --self:debug("binary switch turned off")
        self:updateProperty("value", false)
    end
end --function QuickApp:turnOff
------------------------------------------------------------------

--------------------- Base64 Decode/Encode -----------------------
------------------------------------------------------------------
-- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss <alexthkloss@web.de>
-- licensed under the terms of the LGPL2
-- Start taken from https://stackoverflow.com/a/35303321

-- character table string
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

-- encoding
function Base64enc_sub(data_sub)
    return ((data_sub:gsub('.', function(x) 
        local r,b='',x:byte()
           for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
       return r;
       end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
         if (#x < 6) then return '' end
        local c=0
        for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
        return b:sub(c+1,c+1)
     end)..({ '', '==', '=' })[#data_sub%3+1])
end

function Base64enc(data)
    data = Base64enc_sub(data)
    -- Checksumme erstellen von Data
    local Summe = 0
    for i=1,string.len(data) do
        if string.find(b,string.sub(data,i,i)) ~= nil then
            Summe = Summe + string.find(b,string.sub(data,i,i))
        end
    end
    Summe = "0000000000"..tostring(Summe) --Darstellung anpassen
    Summe = string.sub(Summe,string.len(Summe)-9,string.len(Summe)) -- Max 10 Zeichen
    data = data..Summe
    return data
end
 
-- decoding
function Base64dec(data)
    local Check = string.sub(data,string.len(data)-11,string.len(data)-2) --CR&LF müssen weg
    data = string.sub(data,1,string.len(data)-12)
    local Summe = 0
    -- Checksumme erstellen von Data
    for i=1,string.len(data) do
        if string.find(b,string.sub(data,i,i)) ~= nil then
            Summe = Summe + string.find(b,string.sub(data,i,i))
        end
    end
    Summe = "0000000000"..tostring(Summe) --Darstellung anpassen
    Summe = string.sub(Summe,string.len(Summe)-9,string.len(Summe)) -- Max 10 Zeichen
    if Check == Summe then
        data = string.gsub(data, '[^'..b..'=]', '')
        return (data:gsub('.', function(x)
            if (x == '=') then return '' end
            local r,f='',(b:find(x)-1)
            for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
            return r;
        end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
            if (#x ~= 8) then return '' end
            local c=0
            for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
                return string.char(c)
        end))
    else
        print("Fehler Checksumme der ESP32 Daten")
        return "Fehler Checksumme"
    end
end
-- end of copy
------------------------------------------------------------------

--EOF

Teil 2 nächster Post

Teil 2
Hier der von ESP32 und der QA zum Download

/*
* ESP32 Webserver für den Datenaustausch zwischen ESP32 <--> Fibaro-HC3
* RFID Leser ansprechenn und Code zum QA HC3 senden.
* Bei Übereinstimmung Relais schalten
* Datenverkehr in einem Json-String versenden.
* Jegliche Daten werden Verschlüsselt übertragen. HC3 <-> ESP32
* Der Datenverkehr wird per Checksumm überprüft
*
* Anzeige der Übermittelten und endfangenen Daten mittels OLED-128x64-i2c 
* 
* Die erfolgreiche Datenübermittlung zum HC3 wird mit dem Wechel 
* der 2 LED´2 out_1 und out_2 angezeigt.
* 
* Angegeben werden müßen für den Router
* ssid = "Geheim"  
* password = "Super Geheim"
* 
* ESP32_RFID_Garage_V1.0
* Erstellt am 06.04.2021
* Frank Berges
* ---------------------------------
* Enderungsindex
* V1.0  06.04.2021  F.B.
* V2.0  16.04.2021  F.B.
*                   - Datenverkehr verschluesselt HC3 <-> ESP
*                     Auswertung findet jetzt im ESP32 stat und nicht mehr im HC3
*                     Die Daten für den RFID-Vergleich werden vom HC3 übermittelt
*                     und im Flash abgespeichert, somit stehen diese nach jebem
*                     Neuen Einschalten sofort zurverfügung und müssen nicht
*                     neu Übermittelt werden. Ein Übermittlungsbefehl wird vom 
*                     QA aus über einen Button angestossen.
* V2.1  21.4.2021   F.B.
*                   - Alle Daten-Packete im Wlan mit Checksumm überwacht
*                     HC3 <-> ESP32
*                     Bei falscher Checksumm wird auf das Nächste Packet gewartet
*                   - Fehler beim Abspeichern der RFID Werte im Flash behoben
*                   - Fehlermeldung Checksumm und Datenpacket hinzugefügt
*
* ============================================================================= 
*/


// Load librarys
//-----------------------------------------------------------------------------

#include <deprecated.h>
#include <require_cpp11.h>
#include "time.h"             // Zeitdarstellungen
#include <ssl_client.h>
#include <string>
#include <iostream>
#include <WiFi.h>
#include <Wire.h>
#include <ArduinoJson.h>
#include <SPI.h>
#include <MFRC522.h>
#include <MFRC522Extended.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h> // für Display
#include <Adafruit_Sensor.h>
#include <Preferences.h>      // Variabel im Flash ablegen
#include <WiFiClientSecure.h>
extern "C" 
  {
    #include "crypto/base64.h"
  }

//-----------------------------------------------------------------------------

// Vorbereitung des SSD1306 display I2C
//-----------------------------------------------------------------------------
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
//-----------------------------------------------------------------------------

// Vorbereitung des Timeserver zugriffs
//-----------------------------------------------------------------------------
const char* ntpServer = "pool.ntp.org";
const long  gmtOffset_sec = 3600;
const int   daylightOffset_sec = 3600;
//-----------------------------------------------------------------------------

// Vorbereitung der Ausgänge
//-----------------------------------------------------------------------------
// Variabeln für die Ausgänge
#define ausgang_1 25 // wird von HC3 gesteuert
#define ausgang_2 26 // wird von HC3 gesteuert
#define Beep_PIN 2 // LED-blau-Pin 
#define RST_PIN 33 // SPI-Pin RST-Pin
#define SDA_PIN 32 // SPI-Pin SS- bzw. SDA-Pin
//-----------------------------------------------------------------------------

//Web Daten für den Router
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
const char* ssid = "Geheim"; 
const char* password = "Super-Geheim";
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

//Sendedaten an HC3
//-----------------------------------------------------------------------------
String Software_Version = "ESP32_RFID_Garage_V2.1"; // Softwareversion
int age = 0;
String Daten_1 = "";              // Erster Kartendatensatz z.B "Karin Berges"
String Daten_2 = "";              // Zweiter Kartendatensatz z.B "B2 3E 7C 31"                        
String sende_json = "";           // Sendedaten an den HC3
//-----------------------------------------------------------------------------

//RFID-Daten
//-----------------------------------------------------------------------------
struct astruct 
        {
        String RFID_Name;
        String RFID_Code;
        };
struct astruct RFID_Daten[50];      //Maximal 50 RFID-Tag´s sind vorgesehen
//Möglicher zugriff ist : RFID_Daten[3].RFID_Name = "Frank";

String Rohdaten = "";     // Für die Datenübergabe

Preferences preferences; //Abspeichern der Daten 

//-----------------------------------------------------------------------------

//Gegentacktblinker
//-----------------------------------------------------------------------------
byte out_1 = 0;
byte out_2 = 0;
//-----------------------------------------------------------------------------

//Lese und Auswertebestätigung
//-----------------------------------------------------------------------------
bool RFID_gelesen = false;
//-----------------------------------------------------------------------------

//Für die Displayanzeige Systemzeit
//-----------------------------------------------------------------------------
long myTimer = 0;
long myTimeout = 1000;  //Blinkzeit ein & aus
//-----------------------------------------------------------------------------

//Webserver vorbereiten
//-----------------------------------------------------------------------------
// Set web server port number to 80
WiFiServer server(80);
// Variable to store the HTTP request
String header;    // Transferdaten von http
String input;     // im header endhaltene Daten als json
//-----------------------------------------------------------------------------

//MFRC522-Instanz erstellen
//-----------------------------------------------------------------------------
MFRC522 mfrc522(SDA_PIN, RST_PIN);
//-----------------------------------------------------------------------------

// Timeout definieren
//-----------------------------------------------------------------------------
// Current time
unsigned long currentTime = millis();
// Previous time
unsigned long previousTime = 0; 
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;
//-----------------------------------------------------------------------------

// Für die Checksummenberechnung da nicht beide Systeme 
// die gleich Basis ASCII benutzen
//-----------------------------------------------------------------------------
String index_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//-----------------------------------------------------------------------------


//*********************************** SETUP ***********************************
//-----------------------------------------------------------------------------

void setup() 
  {  
    //SSD1306 display I2C
      if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) 
        {
          Serial.println(F("SSD1306 allocation failed"));
          for(;;);
        }
      delay(2000);
      display.clearDisplay();
      display.setTextColor(WHITE);
  
    //Zeit von Zeitserver holen
      //connect to WiFi
      Serial.printf("Connecting to %s ", ssid);
      WiFi.begin(ssid, password);
      while (WiFi.status() != WL_CONNECTED) 
        {
          delay(500);
          Serial.print(".");
        }
      Serial.println(" CONNECTED für Timeserver");
      //init and get the time
      configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

      //ACHTUNG
      //Erst mit der ersten Ausgabe wird die Zeit übertragen zum Internen Timer
      printLocalTime();
      
      //disconnect WiFi
      WiFi.disconnect(true);
       
    // Bestimmen der pin-Funktion
      pinMode(ausgang_1, OUTPUT);
      pinMode(ausgang_2, OUTPUT);
      pinMode(Beep_PIN, OUTPUT);     //Beeper
      digitalWrite(Beep_PIN, LOW);   //RC522
      
    //SPI-Bus Initialisieren
      SPI.begin();

    //RFID-RC522 initialisieren
      mfrc522.PCD_Init();
    
    Serial.begin(115200);

    // Connect to Wi-Fi network with SSID and password
    Serial.print("Connecting to Network:");
    Serial.println(ssid);
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) 
      {
      delay(500);
      Serial.print(".");
      }
    // Print the connnect IP address and start web server
    Serial.println("");
    Serial.println("Connected. The device can be found at IP address: ");
    Serial.println(WiFi.localIP());
    server.begin();

    //Daten aus dem Flash holen
    Rohdaten = Daten_aus_dem_Flash("Flash_RFID");

    //Json-String in Variabel überführen
    json_string_in_Variabel(Rohdaten);

  }
//-----------------------------------------------------------------------------

//******************************* Hauptprogramm *******************************
//-----------------------------------------------------------------------------
void loop()
  {
    WiFiClient client = server.available();   // Listen for incoming clients
    if (client) 
      { // If a new client connects,
        currentTime = millis();
        previousTime = currentTime;
        Serial.println("New Client found.");          // print a message out in the serial port
        String currentLine = "";                      // make a String to hold incoming data from the client
        while (client.connected() && currentTime - previousTime <= timeoutTime) 
          { // loop while the client's connected
          currentTime = millis();
          if (client.available()) 
          
            { // if there's bytes to read from the client,
              char c = client.read();                // read a byte, then
              Serial.write(c);                       // print it out the serial monitor
              header += c;

            if (c == '\n') 
              { // if the byte is a newline character
                // if the current line is blank, you got two newline characters in a row.
                // that's the end of the client HTTP request, so send a response:
              if (currentLine.length() == 0) 
                {
                  // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
                  // and a content-type so the client knows what's coming, then a blank line:
                  client.println("HTTP/1.1 200 OK");
                  client.println("Content-Type: data/json");
                  client.println("Connection: close");
                  client.println();
                  client.println();

                  // Anzeige der Werte im Display
                  display_anzeigen();
                  // send sensor data in JSON format    
                  uebergabestring();            //Zusammenbauen des json Strings
                  //Sendedaten verschlüsseln
                  sende_json = encodeBase64 (sende_json);
                  client.println(sende_json); //Senden des Strings
                  
                  Daten_1 = "";
                  Daten_2 = "";
                  // Break out of the while loop
                  break;
                } 
              else 
                { // if you got a newline, then clear currentLine
                  currentLine = "";
                }
              } 
            else if (c != '\r') 
              {  // if you got anything else but a carriage return character,
                currentLine += c;      // add it to the end of the currentLine
              }
            }
          }
          
        // Daten im Hader verarbeiten
        reaktionaufanfrage(header);
        //Header Löschen
        header = "";
        
        // Close the connection
        client.stop();
        Serial.println("Client disconnected.");
        Serial.println("");
      }
      
    // Einholen der RFID
    RFID_lesen();
    RFID_vergleichen();

    if (RFID_gelesen)
        {
          if (Daten_1 != "Unbekannt")
            {
              // REFID bekannt
              digitalWrite(Beep_PIN, HIGH);
              delay(50); //50ms
              digitalWrite(Beep_PIN, LOW);
            }
          else
            {
              //RFID unbekannt
              digitalWrite(Beep_PIN, HIGH);
              delay(600); //600ms
              digitalWrite(Beep_PIN, LOW); 
            }
          RFID_gelesen = false;
       }
    
    //Ausgabe der Systemzeit
    display_systemzeit();
    if (WiFi.status() != WL_CONNECTED) 
      {
        Serial.println("Verbindung zur Fritz Box verloren");
        //ESP.restart(); //????????? Dann muesste eigendlich Neu Gestartet werden ????????????????????????????????????
        delay(20000); // Nur zu Testzwecken muss wieder raus
      }
    
  } // End Loop
//-----------------------------------------------------------------------------

//------------------------------ RFID Auslesen --------------------------------
//-----------------------------------------------------------------------------
void RFID_lesen()
    {
      if (Daten_2 == "") //Die letzen Daten wurden übermittelt?
        {
          String rfidTag= "";
          if (mfrc522.PICC_IsNewCardPresent() && // liegt ein neues Tag vor
              mfrc522.PICC_ReadCardSerial()) 
            { // richtige Tag-Art
              Serial.print("RFID-Tag/Karten-ID:");
              for (byte i = 0; i < mfrc522.uid.size; i++) 
                { // Karten-ID extrah.
                  Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
                  Serial.print(mfrc522.uid.uidByte[i], HEX);
                  rfidTag.concat(String(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "));
                  rfidTag.concat(String(mfrc522.uid.uidByte[i], HEX));
                }
              Serial.println();
              Daten_1 = "";
              Daten_2 = rfidTag.substring(1);
              RFID_gelesen = true;          
            }
        }
    }
      

//-----------------------------------------------------------------------------

//---------------------- RFID mit Vorgaben vergleichen ------------------------
//-----------------------------------------------------------------------------
void RFID_vergleichen()
  {
    if (RFID_gelesen)
      {
        for (int i=0; i < 50; i++) 
          {
            if (RFID_Daten[i].RFID_Code == Daten_2)
              {
                //Daten dem Nahmen zuordnen
                Daten_1 = RFID_Daten[i].RFID_Name;
                display_infozeile("RFID Daten gefunden");
                break; //Gefunden ! Jetzt aber schnell raus hier
              }
            else
              {
                Daten_1 = "Unbekannt"; //Keinen übereinstimmenden Datensatz gefunden
              }
          }
      }
  }
//-----------------------------------------------------------------------------

//--------- Aus dem Inputstring die Json Daten holen und verarbeiten ----------
//-----------------------------------------------------------------------------
String reaktionaufanfrage(String Eingangswert)
  {

    // Raussuchen was an Daten gesendet wurde
    Rohdaten = Eingangswert.substring (Eingangswert.indexOf("data:")+5, Eingangswert.indexOf("Content"));
    //Jetzt müssen die Daten noch decodiert werden
    Rohdaten = decodeBase64(Rohdaten);

    if ((Eingangswert.indexOf("data/json") > -1)  && (Rohdaten != "Checksum Fehler"))  //Kommen Reaktionsdaten ? -1 = nein && Checksumm OK
      {
        Serial.println("Daten kommen");
        StaticJsonDocument<128> doc;
        DeserializationError error = deserializeJson(doc, Rohdaten);
        if (error) 
          {
            Serial.print(F("deserializeJson() failed: "));
            Serial.println(error.f_str());
          }
        else // Alles OK 
          {
            //Sendestring inhalt auf Variabeln übertragen
            const char* s_time = doc["time"]; // "05:22:00" hh:mm:ss
            out_1 = doc["out_1"];             //0 oder 1
            out_2 = doc["out_2"];             //0 oder 1
        
            // Schalten der Ausgänge
            if (out_1 == 1)
              {
                //Serial.println("GPIO 25 on");
                out_1 = 1;
                digitalWrite(ausgang_1, HIGH);
              }
            if (out_1 == 0)
              {
                //Serial.println("GPIO 25 off");
                out_1 = 0;
                digitalWrite(ausgang_1, LOW);
              }
            if (out_2 == 1)
              {
                //Serial.println("GPIO 26 on");
                out_2 = 1;
                digitalWrite(ausgang_2, HIGH);
              }
            if (out_2 == 0)
              {
                //Serial.println("GPIO 26 off");
                out_2 = 0;
                digitalWrite(ausgang_2, LOW);
              }

          }   
      }

    if ((Eingangswert.indexOf("memo/json") > -1) && (Rohdaten != "Checksum Fehler"))  //Kommen Speicherdaten ? -1 = nein  && Checksumm OK 
      {
        Serial.println("Memos kommen");
        display_infozeile("Neue Daten vom HC3");

        //Daten im Flash Abspeichern
        Daten_im_Flash_speichern("Flash_RFID", Rohdaten);

        //Json-String <Rohdaten> in Variabel überführen
        json_string_in_Variabel(Rohdaten);

        Rohdaten = ""; //Wieder leeren
      }

    if (((Eingangswert.indexOf("memo/json") == -1) && (Eingangswert.indexOf("data/json") == -1)) || (Rohdaten == "Checksum Fehler")) 
      {
          Serial.println("Unbekantes Datenpacket- oder Checksummenfehler");
          display_infozeile("Datenpacket Fehler");
      }

      //In der Globalen Variabel abspeichern
      input = Eingangswert;
  }   
//-----------------------------------------------------------------------------

//--------------- Json-String zusammen bauen für das versenden ---------------- 
//-----------------------------------------------------------------------------
void uebergabestring()
    {
      // Hier wird das JSON-Objekt erstellt
      DynamicJsonDocument configJSON(1024);
      configJSON["software_version"] = Software_Version;
      configJSON["age"] = 0;
      configJSON["out_1"] = out_1;
      configJSON["out_2"] = out_2;
      configJSON["sensordatavalues"][0]["value_type"] = "Daten_1";
      configJSON["sensordatavalues"][0]["value"] = Daten_1;
      configJSON["sensordatavalues"][1]["value_type"] = "Daten_2";
      configJSON["sensordatavalues"][1]["value"] = Daten_2;
      configJSON["sensordatavalues"][2]["value_type"] = "signal";
      configJSON["sensordatavalues"][2]["value"] = WiFi.RSSI();
      sende_json = "";
      serializeJson(configJSON, sende_json);
      //Serial.println(sende_json);
    }
//-----------------------------------------------------------------------------


//-------------- Json-String zerlegen und in Struktur umwandeln --------------- 
//-----------------------------------------------------------------------------
void json_string_in_Variabel(String in_Rohdaten)
  {
    StaticJsonDocument<1024> doc; //Das solte für einige RFID-Tags reichen
    DeserializationError error = deserializeJson(doc, in_Rohdaten);
      if (error) 
        {
          Serial.print(F("deserializeJson() failed: "));
          Serial.println(error.f_str());
        }
      else
        {
          const char* time = doc["time"]; // "13:30:22"
          int count = 0;
          for (JsonObject elem : doc["Codedaten"].as<JsonArray>()) 
            {
              ///Daten vom HC3 in einer Structur hinterlegen 
              const char* RFID_Name = elem["RFID_Name"]; // "Karin", "Frank" usw.
              const char* RFID_Code = elem["RFID_Code"]; // "B2 3E 7C 31", "6c 3d 41 33" usw.
              RFID_Daten[count].RFID_Name = RFID_Name;
              RFID_Daten[count].RFID_Code = RFID_Code;
              if (count < 50) // ACHTUNG Max 50 Stück
                {
                  count = count + 1;
                }
            }
        }
      // Jetzt sind die Daten aus dem json in der Struktur.
  }


//-----------------------------------------------------------------------------

//--------------------------- BASE64 encode/decode ----------------------------
//-----------------------------------------------------------------------------
String encodeBase64 (String Input_Text) //OK funktioniert
{
  String Output_Code;
  char* toEncode = (char*) Input_Text.c_str(); // cast from string to unsigned char*
  size_t outputLength;
  unsigned char * encoded = base64_encode((const unsigned char *)toEncode, strlen(toEncode), &outputLength);
  Output_Code = (const char*)encoded;
  Output_Code = Output_Code.substring(0,outputLength-1);
  free(encoded);

  //Checksumm mit 10 Zeichen anfügen
  int i; // Schleifenzähler
  int Summe = 0;
  String Check;
  for (i=0;i< Output_Code.length(); i++) 
    {
      if (index_table.indexOf(Output_Code[i]) != -1)
        {
          Summe  = Summe + index_table.indexOf(Output_Code[i])+1;  
        }
    }
  Check = "0000000000" + String(Summe);
  Check = Check.substring(Check.length()-10,Check.length());
  Output_Code = Output_Code + Check;
  return Output_Code;
}
//.........................................................

String decodeBase64 (String Input_Code) //OK funktioniert
{
  String Check_Rein = Input_Code.substring(Input_Code.length()-12,Input_Code.length()-2); //Check rausholen & CR/LF abtrennen
  Input_Code = Input_Code.substring(0,Input_Code.length()-12); //Check abtrennen  

  String Output_Text = "";  

  //Checksumm mit 10 Zeichen anfügen
  int i; // Schleifenzähler
  int Summe = 0;
  String Check;
  for (i=0;i< Input_Code.length(); i++) 
    {
      if (index_table.indexOf(Input_Code[i]) != -1)
        {
          Summe  = Summe + 1 + index_table.indexOf(Input_Code[i]);
        }
    }
  Check = "0000000000" + String(Summe);
  Check = Check.substring(Check.length()-10,Check.length());

  if(Check == Check_Rein)
    {
      char* toDecode = (char*) Input_Code.c_str(); // cast from string to unsigned char*
      size_t outputLength;
      unsigned char * decoded = base64_decode((const unsigned char *)toDecode, strlen(toDecode), &outputLength);
      Output_Text = (const char*)decoded;
      Output_Text = Output_Text.substring(0,outputLength);
      free(decoded);
    }
  else
    {
      Output_Text = "Checksum Fehler";
      Serial.println("Daten vom HC3 hatten einen Checksum Fehler");
    }
  return Output_Text;
}
//-----------------------------------------------------------------------------


//------------------------ Daten im Flash hinterlegen ------------------------- 
//https://github.com/espressif/arduino-esp32/tree/master/libraries/Preferences
//-----------------------------------------------------------------------------
void Daten_im_Flash_speichern(const char * Save_Variabel, String Save_Data)
{
  // Öffenen der Preferences
  // RW-mode (Zweiter parameter muss auf false stehen).
  // Note: Nameslänge des Platzhalters max. 15 Zeichen.
  preferences.begin(Save_Variabel, false);

  // Lösche alle Save_Platzhalten
  //preferences.clear();

  //Löschen nur eines Platzhalters
  preferences.remove(Save_Variabel);

  // Abspeichern der Daten auf den Platzhalter
  preferences.putString(Save_Variabel, Save_Data);

  // Schließen der Preferences
  preferences.end();
}
//-----------------------------------------------------------------------------

//------------------------ Daten aus dem Flash hohlen ------------------------- 
// https://github.com/espressif/arduino-esp32/tree/master/libraries/Preferences
//-----------------------------------------------------------------------------
String Daten_aus_dem_Flash(const char * Save_Variabel)
{
  Serial.print("Save_Variabel = ");
  Serial.println(Save_Variabel);
  // Öffenen der Preferences
  // RW-mode (Zweiter parameter muss auf false stehen).
  // Note: Nameslänge des Platzhalters max. 15 Zeichen.
  preferences.begin(Save_Variabel, false);

  // Einholen eines Srings aus dem Flash Platzhalter 1 Variabel
  // Wenn der Platzhalter nicht da ist kommt ein 0 zurück
  // Note: Nameslänge des Platzhalters max. 15 Zeichen.
  String Flash_Daten = preferences.getString(Save_Variabel,"");

  // Schließen der Preferences
  preferences.end();

  return Flash_Daten;
}
//-----------------------------------------------------------------------------

//---------------------------- Ausgabe Systemzeit -----------------------------
//-----------------------------------------------------------------------------
void printLocalTime()
    {
      struct tm timeinfo;
      if(!getLocalTime(&timeinfo))
        {
          Serial.println("Failed to obtain time");
          return;
        }
      Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
    }
//-----------------------------------------------------------------------------

//-------------------- Ausgabe der Daten auf dem SSD1306 ----------------------
//-----------------------------------------------------------------------------
void display_anzeigen()
    {
      // clear display
      display.clearDisplay();
      
      // Alles was ausgegeben wird in der Schriftgroesse
      display.setTextSize(1);

      //display Datensatz RFID 1
      display.setCursor(0,0);
      display.print("Daten_1 : ");
      display.print(Daten_1);
  
      //display Datensatz RFID 2
      display.setCursor(0,10);
      display.print("Daten_2 : ");
      display.print(Daten_2);  

      //display Zeile 3
      //display.setCursor(0,20);
      //display.print("xxxxx   : ");
      //display.print(Variabel);
      //display.print(" xx");  

      //display Zeile 4
      //display.setCursor(0,30);
      //display.print("xxxxxx  : ");
      //display.print(Variabel);
      //display.print(" xx");  

      //display Ausgang 1
      display.setCursor(0,40);
      display.print("Out_1 : ");
      display.print(out_1);
      display.print(" / ");  

      //display Ausgang 2
      display.print("Out_2 : ");
      display.print(out_2);
      display.print(" ");  

      display.drawLine(0,50,128,50,1);         // Zeichnet eine Linie mit Hilfe von Start- und Endkoordinaten;
                                              
      //display Systemzeit
      display.setCursor(0,53);
      display.print("Systemzeit : ");          // An dieser Stelle wird die Systemzeit ausgegeben
      
      display.drawLine(0,62,128,62,1);         // Zeichnet eine Linie mit Hilfe von Start- und Endkoordinaten;
                                             
      display.display();                       // Alles aus dem Speicher zur Anzeige bringen
    }
//-----------------------------------------------------------------------------

//Für die Anzeige der Systemzeit
//-----------------------------------------------------------------------------
void display_systemzeit() 
  {
    if (millis() > myTimeout + myTimer ) 
      {
        myTimer = millis();
        struct tm timeinfo;
        if(!getLocalTime(&timeinfo))
          {
            Serial.println("Failed to obtain time");
          }
        else
          {
            //Die alte Anzeige löschen
            display.drawFastHLine(80,53,50,SSD1306_BLACK);
            display.drawFastHLine(80,54,50,SSD1306_BLACK);
            display.drawFastHLine(80,55,50,SSD1306_BLACK);
            display.drawFastHLine(80,56,50,SSD1306_BLACK);
            display.drawFastHLine(80,57,50,SSD1306_BLACK);
            display.drawFastHLine(80,58,50,SSD1306_BLACK);
            display.drawFastHLine(80,59,50,SSD1306_BLACK);
            display.drawFastHLine(80,60,50,SSD1306_BLACK);

            //Zeit anzeigen
            display.setTextSize(1);
            display.setCursor(80,53);
            display.print(&timeinfo, "%H:%M:%S");
            display.display();
          }
      }
  }
//-----------------------------------------------------------------------------

//Für die Anzeige der Infozeile max 22 Zeichen
//-----------------------------------------------------------------------------
void display_infozeile(String Text) 
  {
    //Die alte Anzeige löschen
    display.drawFastHLine(0,20,127,SSD1306_BLACK);
    display.drawFastHLine(0,21,127,SSD1306_BLACK);
    display.drawFastHLine(0,22,127,SSD1306_BLACK);
    display.drawFastHLine(0,23,127,SSD1306_BLACK);
    display.drawFastHLine(0,24,127,SSD1306_BLACK);
    display.drawFastHLine(0,25,127,SSD1306_BLACK);
    display.drawFastHLine(0,26,127,SSD1306_BLACK);
    display.drawFastHLine(0,27,127,SSD1306_BLACK);

    //Text auf 22 Zeichen begrenzen
    Text = Text.substring (0, 22);
    //int x= (128-(Text.length()*6))/2;
    //Text anzeigen
    display.setTextSize(1);
    display.setCursor((128-(Text.length()*6))/2,20);
    display.print(Text);
    display.display();
  }
//-----------------------------------------------------------------------------


// EOF

RFID-Leser_v2.1(1).fqa (22,1 KB)

Ist schon recht umfangreich geworden, hatte ich mir erst nicht so Gross vorgestellt.

Gruss Frank