Esp32 oder auch esp8266 Mikrocontroller eignen sich hervorragend für IOT-Anwendungen. Vielfach werden diese kleinen Module verwendet, um darauf die Firmware tasmota zu betreiben.
Der ESP32 und der ESP8266 sind beides Mikrocontroller mit integriertem WLAN, die häufig für IoT-Projekte genutzt werden. Der ESP8266 ist der ältere und kostengünstigere Chip, ideal für einfache Anwendungen, bei denen eine Internetverbindung benötigt wird. Er hat begrenzte Rechenleistung und weniger Speicher. Der ESP32 ist eine leistungsfähigere Weiterentwicklung, die neben WLAN auch Bluetooth-Unterstützung bietet. Er hat mehr Rechenleistung, Speicher und zusätzliche Features wie mehrere GPIO-Pins (für den Anschluss von Sensoren und Aktoren) und Hardware-Unterstützung für Verschlüsselung. Der ESP32 ist daher flexibler
Es geht aber auch noch einfacher. Mit der Arduino Entwicklungsumgebung lassen sich einfache Programme erstellen und auf den Mikrocontroller übertragen. Das ist insbesondere dann sinnvoll und hilfreich, wenn in der tasmota Software diese Funktionalität nicht abgebildet ist.
Aufgabenstellung und Motivation
Mit Hilfe eines RFID-Chips und einem Kartenleser Terminal soll eine Aktivität verknüpft werden. Dazu wird die eindeutige ID des RFID-Chips durch das Kartenlesegerät erfasst und durch den Mikrocontroller an einen MQTT-Server weitergeleitet werden. Die übertragene Information initiiert einen flow innerhalb des Node-RED Hubs.
Welche Aktivität mit dem Lesen des RFID Chips verbunden ist, obliegt der Logik innerhalb der Node-RED Umgebung.
Zeiterfassung
Um aus diesem Grundmodul eine Zeiterfassung zu machen, benötigt es nur wenige Softwareschritte. In meinem Fall eine Datenbank mit einer Tabelle, in der ein Zeitstempel mit der ID des RFID-Chips abgelegt wird.
Programmierung der Hardware
Die wesentlichen Schritte in dem Programm für den Mikrocontroller sind folgende.
- Initialisierung der Hardware inklusive des Kartenlesers
- Verbindung mit dem WLAN herstellen
- Verbindung mit dem MQTT-Server herstellen
- In einer Endlosschleife läuft die Abfrage des Kartenlesers und sollte eine ID erkannt werden, so wird diese an den MQTT-SERVER übertragen
- Innerhalb der Software können noch qualitätsverbessernde Aktivitäten und Reaktionen programmiert werden, um das Betriebsverhalten stabil und sicher zu gewährleisten. Beispielsweise Überprüfung der WLAN-Verbindung und der Übertragung und Kommunikation mit dem MQTT-Server
Weiterverarbeitung der Daten
Drei einfache Knoten benötigt es um die Daten zu übernehmen und in die Datenbank zu schreiben. Die unteren beiden Knoten dienen der Ausgabe im Dashboard. Die weitere Verarbeitung der Daten aus der Tabelle erfolgt entweder durch Export der Tabelle oder aber wie ich es gemacht hat mit einer ole-verbindung direkt in Excel und dann werden in Excel weitere Tools zur Datenaufbereitung verwendet.
Code für den ESP
/*
Version 1.0
27.08.2024
*/
#include <WiFi.h>
#include <MySQL_Connection.h>
#include <MySQL_Cursor.h>
#include <PubSubClient.h>
#include <esp_task_wdt.h>
#include <ESPmDNS.h>
#include <ESPping.h>
#include <SPI.h>
#include <MFRC522.h>
// Hier NUR EIN EINZIGES Profil aktivieren
#define ESP32WROOM32 //
//#define ESP32WROOMD1MINI //
//#define ESP8266D1Mini //
// DEBUG => True | FLASE
const boolean debug = true;
// Portzuweisungen abhängig von der HW
#ifdef ESP8266D1Mini
#define STATUS_LED 21 // LED zur Anzeige der Bestätigung und Error-Codes
#define RST_PIN 22 // RC522 Reset-Pin
#define SS_PIN 5 // RC522 SDA-Pin
const char* mDNSname = "ESP8266D1mini";
const char* pinghost = "FQDN";
#endif
// Portzuweisungen abhängig von der HW
#ifdef ESP32WROOMD1MINI
#define STATUS_LED 21 // LED zur Anzeige der Bestätigung und Error-Codes
#define RST_PIN 22 // RC522 Reset-Pin
#define SS_PIN 5 // RC522 SDA-Pin
const char* mDNSname = "ESP32WROOM32D1mini";
const char* pinghost = "FQDN";
#endif
// Portzuweisungen abhängig von der HW
#ifdef ESP32WROOM32
#define STATUS_LED 17 // LED zur Anzeige der Bestätigung und Error-Codes
#define RST_PIN 5 // RC522 Reset-Pin
#define SS_PIN 21 // RC522 SDA-Pin
const char* mDNSname = "ESP32WROOM32";
const char* pinghost = "FQDN";
#endif
// WLAN-Zugangsdaten
const char* ssid = "*********";
const char* password = "******";
// MQTT-Broker-Zugangsdaten
const char* mqtt_server = "FQDN";
const int mqtt_port = 1883; // Standard-MQTT-Port
const char* mqtt_user = "user";
const char* mqtt_password = "password";
const char* mqtt_topic = "zeiterfassung/rfid01/cardid";
//const char* mqtt_topic = "zeiterfassung/rfid01/TEST/cardid";
// RFID RC-522
MFRC522 mfrc522(SS_PIN, RST_PIN); // Erstelle ein MFRC522-Objekt
// ***********************************************************************************************
// END OF DECLARATION
// ***********************************************************************************************
// ***********************************************************************************************
// MQTT-Client
// ***********************************************************************************************
WiFiClient wifiClient;
PubSubClient client(wifiClient);
// Der zu sendende Wert
int valueToSend = 0;
// ***********************************************************************************************
// RF-ID-Chip auslesen
// ***********************************************************************************************
String readRFID() {
String rfidValue = "";
// Suche nach neuen RFID-Tags
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
// Konvertiere die UID des Tags in einen String
for (byte i = 0; i < mfrc522.uid.size; i++) {
rfidValue += (mfrc522.uid.uidByte[i] < 0x10 ? "0" : ""); // Führende Null hinzufügen, wenn nötig
rfidValue += String(mfrc522.uid.uidByte[i], HEX);
}
// Halt die aktuelle Karte
mfrc522.PICC_HaltA();
}
return rfidValue;
}
// ***********************************************************************************************
// ***********************************************************************************************
// Das ist die neue Version. Wenn die Verbindung zu MQTT nicht erfolgreich war, blinkt die LED 3x
// ***********************************************************************************************
void sendMQTTValue(String valueToSend) {
// Verbindung zum MQTT-Broker aufbauen, wenn nicht verbunden
if (!client.connected()) {
if (debug) {Serial.println("sendMQTTValue: Client is NOT connected");}
if (client.connect("ESP32Client", mqtt_user, mqtt_password)) {
if (debug) {Serial.println("sendMQTTValue: Verbunden mit MQTT-Broker");}
} else {
if (debug) {Serial.println("sendMQTTValue: Verbindung mit MQTT-Broker fehlgeschlagen. Erneuter Versuch in 1 Sekunde...");}
blinkLED(3);
return;
}
}
if (client.connected()) {
if (debug) {Serial.println("sendMQTTValue: Client is connected");}
// Sende den Wert mit QoS 1 (mindestens einmal zustellen)
if (client.publish(mqtt_topic, valueToSend.c_str(), true)) { // QoS 1 wird standardmäßig verwendet
if (debug) {Serial.println(String("sendMQTTValue: Nachricht gesendet ") + valueToSend);}
if (valueToSend != "no-card") {
blinkLED(1);
}
} else {
if (debug) {Serial.println("sendMQTTValue: Nachricht senden fehlgeschlagen");}
blinkLED(3);
}
}
}
// ***********************************************************************************************
// ***********************************************************************************************
// LED Blinken lassen
// ***********************************************************************************************
void blinkLED(int times) {
for (int i = 0; i < times; i++) {
digitalWrite(STATUS_LED, HIGH);
delay(200); // LED ein für 200ms
digitalWrite(STATUS_LED, LOW);
delay(200); // LED aus für 200ms
}
}
// ***********************************************************************************************
// ***********************************************************************************************
// prüfe die WLAN Verbindung, ggf. reconnect
// ***********************************************************************************************
boolean checkPing() {
// Ping starten
if (Ping.ping(pinghost)) {
if (debug) {Serial.println("Ping erfolgreich!");}
return true;
} else {
Serial.println("Ping fehlgeschlagen!");
return false;
}
}
// ***********************************************************************************************
// ***********************************************************************************************
// prüfe die WLAN Verbindung, ggf. reconnect
// ***********************************************************************************************
void checkWiFiConnection() {
// Prüfe, ob das WLAN noch verbunden ist
if (WiFi.status() != WL_CONNECTED) {
if (debug) {Serial.println("WLAN-Verbindung verloren! Versuche erneut zu verbinden...");}
blinkLED(5);
// Versuche, die WLAN-Verbindung erneut herzustellen
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
if (debug) {Serial.println("Verbindung zum WLAN wird wiederhergestellt...");}
}
if (debug) {Serial.println("WLAN-Verbindung wiederhergestellt!");}
}
}
// ***********************************************************************************************
// ***********************************************************************************************
// MQTT Connection prüfen
// ***********************************************************************************************
void checkMQTTConnection() {
// Prüfe, ob die MQTT-Verbindung noch aktiv ist
if (!client.connected()) {
if (debug) {Serial.println("MQTT-Verbindung verloren! Versuche erneut zu verbinden...");}
// LED blinken lassen, um anzuzeigen, dass die MQTT-Verbindung verloren gegangen ist
blinkLED(3);
}
// Versuche, die MQTT-Verbindung erneut herzustellen
if (client.connect("ESP32Client", mqtt_user, mqtt_password)) {
if (debug) {Serial.println("MQTT-Verbindung wiederhergestellt!");}
// Optional: Initiale Nachrichten senden, falls nötig
sendMQTTValue("reconnectedESP32");
} else {
if (debug) {Serial.println("Erneuter Verbindungsversuch fehlgeschlagen. Versuch in 1 Sekunde...");}
}
}
// ***********************************************************************************************
// ***********************************************************************************************
// Teste ob der RFID Leser verbunden ist
// ***********************************************************************************************
bool checkRFIDConnection() {
// Ein einfacher Weg, um zu überprüfen, ob der Leser funktioniert, ist ein Selbsttest
if (!mfrc522.PCD_PerformSelfTest()) {
return false; // Der Selbsttest ist fehlgeschlagen, der Leser ist nicht richtig verbunden
}
// Alternativ: Prüfe, ob der Leser auf Anfragen reagiert (z.B. Firmwareversion)
byte firmware = mfrc522.PCD_ReadRegister(MFRC522::VersionReg);
if (firmware == 0x00 || firmware == 0xFF) {
return false; // Ungültige Antwort, der Leser ist nicht verbunden oder defekt
}
return true; // Der Leser ist korrekt verbunden und funktioniert
}
// ***********************************************************************************************
// ***********************************************************************************************
// Das ist das Hauptprogramm
// ***********************************************************************************************
// ***********************************************************************************************
// Setup and Loop
// ***********************************************************************************************
void setup() {
Serial.begin(115200);
// Initialisiere den Pin D5 als Ausgang
pinMode(STATUS_LED, OUTPUT);
// WIFI
WiFi.begin(ssid, password);
int retryCount = 0;
const int maxRetries = 10; // Maximal 10 Versuche
while (WiFi.status() != WL_CONNECTED && retryCount < maxRetries) {
blinkLED(5); // Blinkt die LED 5 Mal, während auf eine Verbindung gewartet wird
delay(2000); // Warte 2 Sekunden zwischen den Versuchen
retryCount++; // Erhöhe den Zähler für die Verbindungsversuche
if (debug) {
Serial.print("SETUP: Verbindung zum WLAN wird hergestellt... Versuch ");
Serial.println(retryCount);
}
}
if (WiFi.status() == WL_CONNECTED) {
if (debug) {
Serial.println("WLAN: Verbunden mit dem WLAN");
Serial.println("WLAN: IP-Addresse = " + WiFi.localIP().toString());
//Serial.println(WiFi.localIP());
}
} else {
if (debug) {Serial.println("SETUP: Verbindung zum WLAN fehlgeschlagen, Neustart...");}
ESP.restart(); // Neustart des ESP32
}
// Initialize mDNS
if (!MDNS.begin(mDNSname)) { // Set the hostname
if (debug) {Serial.println("Error setting up MDNS responder!");}
while(1) {
delay(1000);
}
}
if (debug) {
Serial.print("mDNS: responder started, name: ");
Serial.println(mDNSname);
}
client.setServer(mqtt_server, mqtt_port);
if (debug) {Serial.println("SETUP: Verbunden mit dem MQTT");}
if (client.connected()) Serial.println("SETUP: Ich bin immer noch verbunden, Initiale Verbindung erfolgreich");
sendMQTTValue(mDNSname);
//RFID-RC522
SPI.begin(); // Initialisiere SPI-Bus
mfrc522.PCD_Init(); // Initialisiere den MFRC522-Chip
if (debug) {
Serial.println("SETUP: RFID Reader gestartet!");
}
//WDT
esp_task_wdt_init(20, true); // Timeout nach 10 Sekunden
esp_task_wdt_add(NULL); // Fügt den aktuellen Task zum Watchdog Timer hinzu
}
// ***********************************************************************************************
// ***********************************************************************************************
// Endlosschleife
// ***********************************************************************************************
void loop() {
static int counter = 0; // Zähler für die Durchläufe
static int WDT = 0; // Zähler für WDT
static int pcounter = 0; // Zähler für WDT
// RFID-Wert lesen
String rfidValue = readRFID();
// sende die gelesene ID an MQTT
if (!rfidValue.isEmpty()) {
//Sende Daten an den MQTT-Server
sendMQTTValue(rfidValue);
delay(1000); // Warte 1 Sekunde um doppelte zu vermeiden
}
// Teste die WLAN Verbindung nur alle 100 Durchläufe
if (++counter == 1000) {
checkWiFiConnection();
counter = 0; // Zähler zurücksetzen
}
//sende ein ping um zu prüfen ob Kommunikation möglich ist
if (++pcounter == 1000) {
if (!checkPing) {
if (debug) {Serial.println("PING: nicht erfolgreich");}
}
}
// WDT
if (++WDT == 20) {
esp_task_wdt_reset();
WDT = 0; // Zähler zurücksetzen
}
// Teste den RFID-Reader
// MQTT-Nachrichten empfangen (optional)
//client.loop();
delay(200);
}
Hinterlasse einen Kommentar