Temperaturføler med ESP32, DS18B20 og OLED-skærm
Appearance
Indledning
Dette projekt beskriver en ESP32-baseret temperaturmåler, der bruger en DS18B20-sensor til at måle temperaturer, f.eks. i et vildmarksbad hvor 0 °C til ~42 °C er normalt. Temperaturen vises på en OLED-skærm og kan ses i en web-browser.
Projektet inkluderer nem konfiguration af opkobling til Wi-Fi og der er mulighed for, at sende data til en MQTT-server, for eksempel i et Home Assistant setup.
Indkøbsliste
- ESP32 USB-C IoT bundkort DevKitC CP2102 38PIN
- 0,96" I2C 128x64 OLED-display
- DS18B20 vandtæt digital termisk sonde (1 meter kabel)
- HLK-PMxx 220VAC til 5VDC Step Down Converter
- Sonoff IP66 vandtæt etui
- Prototype PCB print - Type 1
Byggevejledning
1. Tilslutninger:
- DS18B20:
- VCC (rød) → 3.3V på ESP32
- GND (sort) → GND på ESP32
- Data (gul) → GPIO 4 på ESP32
- Tilføj en 4,7kΩ pull-up modstand mellem Data og VCC. (gul-violet-rød)
- OLED-skærm (I2C):
- VCC → 3.3V på ESP32
- GND → GND på ESP32
- SDA → GPIO 21 på ESP32
- SCL → GPIO 22 på ESP32
2. Strømforsyning:
Tilslut ESP32 og OLED til 5V strømforsyning via HLK-PMxx-modulet.
3. Montering:
- Montér komponenterne på prototype-PCB. - Placér hele opsætningen i Sonoff IP66 vandtæt etui for beskyttelse mod vejr og støv.
Kode til ESP32
/* README plz...
Benyt "uPesy ESP32 Wroom DevKit" som board model til kompilering.
Jeg har oplevet, at andre board models har fejl der får LED til at blinke for hurtigt !?!
Hvis ssid og password sættes til '0' aktiveres offline mode
Projekt start: 21-12-2024
Seneste opdaterings dato: 03-06-2026
*/
#include <WiFi.h> // Bibliotek til Wi-Fi forbindelse på ESP32.
#include <time.h> // Bibliotek til tidsstyring og synkronisering.
#include <OneWire.h> // Bibliotek til OneWire-kommunikation (f.eks. temperaturfølere).
#include <WebServer.h> // Bibliotek til at oprette en webserver.
#include <Preferences.h> // Bibliotek til at gemme brugerindstillinger i ikke-flygtig hukommelse.
#include <Adafruit_GFX.h> // Grafisk bibliotek til skærme (basal grafisk funktionalitet).
#include <PubSubClient.h> // MQTT-klientbibliotek til kommunikation med MQTT-brokere.
#include <Adafruit_SSD1306.h> // Bibliotek til at styre SSD1306 OLED-skærme.
#include <DallasTemperature.h> // Bibliotek til at arbejde med Dallas/OneWire temperatursensorer.
// === PIN-konfigurationer ===
#define ONE_WIRE_BUS 4 // Pin til DS18B20 temperatursensoren.
#define SCREEN_WIDTH 128 // Skærmens bredde (for OLED).
#define SCREEN_HEIGHT 64 // Skærmens højde (for OLED).
#define OLED_RESET -1 // Reset pin til OLED (sættes til -1, da vi ikke bruger en fysisk reset pin).
#define DEBUG_ON_PIN 14 // Pin for at få DEBUG skrevet til seriel port under boot og første gennemløb af loop.
#define RST_PIN 13 // Pin for RST-knappen.
#define LED_BUILTIN 2 // Pin for LED.
#define MAX_BOOT_TIMES 3 // Krævede antal boot indenfor RESET_THRESHOLD for at kalde resetConfig()
#define RESET_THRESHOLD 60 // Tids-vindue på 60 sekunder hvor i det er muligt at opnå kald til resetConfig() hvis MAX_BOOT_TIMES opnåes.
// === Globale objekter ===
OneWire oneWire(ONE_WIRE_BUS); // Initialiserer OneWire-bus til DS18B20.
DallasTemperature sensors(&oneWire); // Opretter et objekt til at håndtere temperaturmålinger.
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // OLED-skærm til at vise informationer.
WebServer server(80); // Webserver på port 80 til at håndtere HTTP-anmodninger.
WiFiClient espClient; // WiFi klient til MQTT.
PubSubClient mqttClient(espClient); // MQTT-klient, der bruger WiFi klienten.
Preferences preferences; // Deklaration af Preferences objektet globalt
// === Globale variabler ===
const char FirmwareVersion[8] = "1.08"; // Firmware version
unsigned long lastCommsCheck = 0; // Tidspunkt for sidste Comms-tjek.
unsigned long CommsCheckInterval = 60000; // Tidsinterval for Comms-tjek (60 sekunder) angivet i millisekunder.
unsigned long lastLEDCheck = 0; // Tidspunkt for sidste LED-tjek.
unsigned long LEDCheckInterval = 2000; // Tidsinterval for LED-tjek (2 sekunder) angivet i millisekunder.
unsigned long lastMQTTpublish = 0; // Tidspunkt for sidste MQTT-publish.
unsigned long MQTTPublishInterval = 15000; // Tidsinterval for MQTT publish (15 sekunder) angivet i millisekunder.
bool debugEnabled = false; // Skal der udskrives til seriel port?
bool debugBootEnabled = false; // Skal der udskrives på seriel port under boot?
int updateInterval = 15000; // Variabel til 15 sekunders opdateringsinterval angivet i millisekunder.
int shortUpTime; // Tæller der inkrementeres hvert andet sekund de første 20 sekunder af en boot. Bruges til nulstilling af bootCounter.
int bootCounter; // Tæller der indikerer antallet af korte UpTime boots før nuværende. Bruges til detektering af om der rebootes 3 gange i streg for at aktiverer checkBootResetCondition()
bool offlineMode = false; // Indikation af om offline mode er aktiveret.
// === Netværksindstillinger === // HUSK !! --> NVS nøgler må ikke være mere end 15 chars <-- !!
char ssid[33] = ""; // SSID til WiFi netværket.
char password[65] = ""; // WiFi adgangskode.
// === MQTT-indstillinger === // HUSK !! --> NVS nøgler må ikke være mere end 15 chars <-- !!
char mqtt_server[129] = ""; // MQTT-serverens IP eller domæne.
int mqtt_port = 1883; // MQTT-port (standard 1883).
char mqtt_user[129] = ""; // MQTT-brugernavn.
char mqtt_password[129] = ""; // MQTT-adgangskode.
char mqtt_clientID[129] = ""; // MQTT-clientID.
char mqtt_publTpic[129] = ""; // MQTT-Publish topic.
// === Funktionsdeklarationer === // "forward declarations" laves for funktioner der kaldes FØR compiler har set dem - det sker f.eks. i setup () og loop()
void syncTime(); // Synkroniser tid med NTP-server
void logBootTime(); // Log nuværende tid som boot-tid i NVS
bool checkBootResetCondition(); // Tjekker om systemet skal nulstille konfigurationen som følge af 3 på hinanden følgende korte boot up.
void sendTemperatureToMQTT(float temp); // Sender aktuel temperatur til MQTT server.
void updateOledScreen(float temp); // Opdatrerer OLED skærm med information under normal drift
void blink(); // Ændre tilstand af LED_BUILTIN hvis LEDCheckInterval er overskredet.
void reconnectMQTT(); // Forsøger at oprette forbindelse til MQTT-serveren
void resetConfig(); // Nulstil konfiguration
void readConfig(); // Læs gemte konfigurationer fra NVS
void setupConfigServer(); // Starter konfigurationsserveren, så brugeren kan konfigurere WiFi og MQTT
void connectWiFi(); // Forbind til WiFi
void setup() { // Initiel programmatiske konfigurations kode.
pinMode(RST_PIN, INPUT); // Initialiser RST_PIN som et INPUT.
pinMode(RST_PIN, INPUT_PULLUP); // Aktiver intern pull-up modstand.
pinMode(DEBUG_ON_PIN, INPUT); // Initialiser DEBUG_ON_PIN som et INPUT.
pinMode(DEBUG_ON_PIN, INPUT_PULLUP); // Aktiver intern pull-up modstand.
pinMode(LED_BUILTIN, OUTPUT); // Initialiser den blå LED.
digitalWrite(LED_BUILTIN, LOW); // Sluk den blå LED.
Serial.begin(115200); // Initialiserer seriel kommunikation til debugging.
sensors.begin(); // Starter temperatursensoren.
lastCommsCheck = millis(); // Sætter første tidsværdi af variablen.
lastLEDCheck = millis(); // Sætter første tidsværdi af variablen.
lastMQTTpublish = millis(); // Sætter første tidsværdi af variablen.
if (digitalRead(RST_PIN) == LOW) { // Tjek om RST-knappen er trykket under opstart
Serial.println("\nRST-knappen trykket. Nulstiller WiFi og MQTT konfiguration.\n");
delay(1000);
resetConfig(); // Nulstil konfiguration
}
if (digitalRead(DEBUG_ON_PIN) == LOW) { // Tjek om DEBUG_ON_PIN er LOW under opstart
Serial.println("\nDEBUG_ON_PIN-knappen trykket. Viser derfor debug info på seriel port.\n");
delay(1000);
debugBootEnabled = true;
debugEnabled = true;
}
preferences.begin("bootlog", false); // Åbner et område kaldet "bootlog" i den permanente flash-hukommelse (NVS), som kan skrives til.
if (!preferences.isKey("bootCounter")) {
preferences.putInt("bootCounter", 0);
}
if (!preferences.isKey("shortUpTime")) {
preferences.putInt("shortUpTime", 0);
}
preferences.end();
// OLED-initialisering
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Tjekker om OLED-skærmen er korrekt tilsluttet
Serial.println(F("\nOLED fejl!\n"));
for (;;); // Stopper programmet, hvis skærmen ikke kan initialiseres
}
display.clearDisplay(); // Rydder skærmen
display.setTextSize(1); // Sætter tekststørrelse
display.setTextColor(WHITE); // Sætter tekstfarve til hvid
if (checkBootResetCondition()) { // Tjek om systemet skal nulstille konfigurationen som følge af 3 bootup på under 20 sekunder.
debugEnabled ? (void)Serial.println("\ncheckBootResetCondition TRUE\n") : (void)0;
nulstilConfigResetFeature();
debugEnabled ? (void)Serial.println("\n3 hurtige boot-forsøg inden for krav. Nulstiller konfiguration.\n") : (void)0;
delay(3333);
resetConfig();
} else {
if (debugEnabled) {
Serial.println("\ncheckBootResetCondition FALSE\n");
Serial.print("bootCounter = ");
Serial.println(bootCounter);
Serial.println();
}
}
readConfig(); // Læs gemte konfigurationer fra NVS
Serial.println();
Serial.print("ssid = ");
Serial.println(ssid);
Serial.print("password = ");
Serial.println(password);
Serial.println();
if (strcmp(ssid, "offline") == 0 && strcmp(password, "offline") == 0) { // Undersøg om der skal køres i offline mode uden WiFi forbindelse.
offlineMode = true;
}
Serial.print("offlineMode = ");
Serial.println(offlineMode);
Serial.println();
if (!offlineMode){
if (strlen(ssid) == 0 || strlen(password) == 0) { // Hvis ingen konfiguration er gemt, start konfigurationsmode
Serial.println("\nWiFi konfig mangler!! - Starter konfigurationsserveren, så brugeren kan konfigurere WiFi og MQTT.\n");
setupConfigServer(); // Starter konfigurationsserveren, så brugeren kan konfigurere WiFi og MQTT
} else { // Normal drifts start:
connectWiFi(); // Forbind til WiFi.
if (strlen(mqtt_server) > 0) { // start kun MQTT, hvis "mqtt_server" er konfigureret.
String mac = WiFi.macAddress(); // Eksempel: "24:6F:28:1A:2B:3C".
debugEnabled ? (void)Serial.println("WiFi mac: " + mac) : (void)0;
mac.replace(":",""); // fjern kolon'er.
String lastSixChars = mac.substring(mac.length() - 6); // Gem de sidste 6 tegn - Resultat: "2B:3C"
String mqtt_clientID = "fmjheatsensor-" + lastSixChars; // Generer klient-id baseret på de sidste 6 bogstaver og tal i wifi mac adresse
mqttClient.setServer(mqtt_server, mqtt_port);
if (!mqttClient.connected()) { // Hvis MQTT ikke er forbundet,
reconnectMQTT(); // Forsøg at oprette forbindelse til MQTT.
}
if (debugEnabled) {
PrintGlobalVars2Console();
}
}
}
// Webserver-ruter
server.on("/", handleRoot); // Root URL, der viser temperaturdata
server.on("/temperature", handleTemperature); // URL til at sende temperaturdata til JavaScript opdatering af handleRoot
server.on("/config", handleConfig); // URL til at konfigurere WiFi og MQTT
server.on("/reset", handleReset); // URL til at nulstille WiFi
server.begin(); // Starter webserveren
if (WiFi.status() == WL_CONNECTED) {
syncTime(); // Synkroniser tid med NTP-server
logBootTime(); // Log dette bootup.
}
}
Serial.println("\n- Created by https://munkjensen.net december 2024.");
Serial.print("- Firmware version: ");
Serial.println(FirmwareVersion);
Serial.print("- bootCounter: ");
Serial.println(bootCounter);
Serial.println();
printCmds2Console(); // Beskriv seriel kommandoer i seriel konsol
}
void loop() { // Her placeres koden der skal eksekveres kontinuerligt
sensors.requestTemperatures(); // Anmoder om temperaturmåling.
float tempC = sensors.getTempCByIndex(0); // Hente temperatur fra den første sensor (index 0).
updateOledScreen(tempC); // Opdaterer OLED-skærmen med den nye temperatur m.m.
blink(); // Blinke funktion (2 sek. mellem blink) til indikation af at loop() køre.
if (!offlineMode) { // Hvis der IKKE skal køres i Offline Mode skal de efterfølgende ting håndteres:
if (millis() - lastCommsCheck >= CommsCheckInterval) { // Tjek forbindelser hvis det er tid til det (hver 60. sekunder)
if (WiFi.status() != WL_CONNECTED) { // Hvis WiFi ikke er forbundet,
connectWiFi(); // Forsøg at oprette forbindelse til WiFi.
}
if (strlen(mqtt_server) > 0) { // Tjek kun MQTT, hvis mqtt_server er angivet
if (!mqttClient.connected()) { // HVis MQTT ikke er forbundet,
reconnectMQTT(); // Forsøg at oprette forbindelse til MQTT.
}
}
lastCommsCheck = millis(); // Opdater lastCommsCheck variablen til nu.
}
server.handleClient(); // Lad webserveren behandle indkommende HTTP-anmodninger.
if (millis() - lastMQTTpublish >= MQTTPublishInterval) { // Send MQTT publish hvis det er tid til det (hver 15. sekunder)
if (strlen(mqtt_server) > 0) { // Send kun MQTT, hvis mqtt_server er angivet
if (mqttClient.connected()) { // Send temperaturdata til MQTT, hvis forbindelsen er aktiv
sendTemperatureToMQTT(tempC); // Sender aktuel temperatur til MQTT server.
}
lastMQTTpublish = millis(); // Opdater lastMQTTpublish variablen til nu.
}
}
}
if (Serial.available() > 0) { // Håndter seriel kommando linje hvis den er forbundet
handleSerialInput();
}
if (debugBootEnabled) { // Hvis debugBootEnabled har været true sætter vi den, og debugEnabled false her, efter første gennemløb af loop.
debugBootEnabled = false;
debugEnabled = false;
Serial.println("\nDEBUG_ON_PIN-knappen er/har været LOW ved BOOT. Dette var første gennemløb af loop() og DEBUG slås derfor fra.\n");
}
}
bool checkBootResetCondition() { // Tjekker ved boot up, om systemet skal nulstille konfigurationen som følge af 3 på hinanden følgende shortUpTime.
if (debugEnabled) {
Serial.println("\ncheckBootResetCondition()\n");
}
preferences.begin("bootlog", false);
bootCounter = preferences.getInt("bootCounter");
shortUpTime = preferences.getInt("shortUpTime");
preferences.end();
if (debugEnabled) {
Serial.println("shortUpTime: " + String(shortUpTime));
Serial.println("bootCounter: " + String(bootCounter));
Serial.println();
}
if (shortUpTime == 0) { return false; } // Hvis systemet lige er resat skal der ikke nulstilles igen og ej heller ændres på bootCounter.
if (shortUpTime == 11) { // Forrige boot varede MERE end 20 sekunder og shortUpTime derfor er nået til 11, Dette er en frisk boot så shortUpTime skal sættes til 0.
shortUpTime = 0;
preferences.begin("bootlog", false);
preferences.putInt("shortUpTime", shortUpTime);
preferences.end();
return false;
}
if (shortUpTime <= 10) { // Hvis ShortUpTime MINDRE END eller LIG MED 10, så øg bootCounter med 1, nulstil shortUpTime og gem dem.
bootCounter++;
shortUpTime = 0;
preferences.begin("bootlog", false);
preferences.putInt("bootCounter", bootCounter);
preferences.putInt("shortUpTime", shortUpTime);
preferences.end();
}
if (bootCounter >= 3) { return true; } // Hvis der er optalt 3 korte opstarter i træk, så returner true for at nulstille systemet.
return false; // bootCounter er mindre end 3, så der skal IKKE nulstilles.
}
void blink() { // Øger shortUpTime med 1 de første 20 sekunder af boot og ændre tilstand af LED_BUILTIN hvis LEDCheckInterval er overskredet.
//debugEnabled ? (void)Serial.println("blink()") : (void)0;
if (millis() - lastLEDCheck >= LEDCheckInterval) {
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // Læs den nuværende tilstand af LED_BUILTIN og sæt den modsatte værdi.
lastLEDCheck = millis(); // Opdater nu tidspunktet for LED status ændring.
preferences.begin("bootlog", false);
shortUpTime = preferences.getInt("shortUpTime"); // Hent shortUpTime fra NVS - ignorer fejl hvis den ikke findes.
preferences.end();
if (shortUpTime <= 10) { // Denne boot har endnu ikke varet 20 sekunder.
shortUpTime++; // Øg værdien
preferences.begin("bootlog", false);
preferences.putInt("shortUpTime", shortUpTime); // Gem den nye shortUpTime værdi
preferences.end();
if (debugEnabled) {
Serial.println("\nDenne boot har endnu ikke varet 20 sekunder...\n");
Serial.println("shortUpTime: " + String(shortUpTime));
Serial.println("bootCounter: " + String(bootCounter));
Serial.println();
}
}
if (shortUpTime == 10) { // Denne boot varede MERE end 20 sekunder og shortUpTime derfor er nået til 11, så bootCounter sætes til 0 og gemmes.
debugEnabled ? (void)Serial.println("\nDenne boot varede MERE end 20 sekunder og shortUpTime derfor er nået til 10, så bootCounter sætes til 0 og gemmes.\n") : (void)0;
preferences.begin("bootlog", false);
preferences.putInt("bootCounter", 0);
bootCounter = preferences.getInt("bootCounter");
preferences.end();
if (debugEnabled) {
Serial.println("shortUpTime: " + String(shortUpTime));
Serial.println("bootCounter: " + String(bootCounter));
Serial.println();
}
shortUpTime++; // Forhøj shortUpTime for ikke at gå her ind mere.
}
}
}
void nulstilConfigResetFeature() { // Nulstiller bootCounter og shortUpTime og gemmer dem i NVS.
if (debugEnabled) {
Serial.println("nulstilConfigResetFeature()");
Serial.println("Variaber lige nu:");
Serial.println();
Serial.println("shortUpTime: " + String(shortUpTime));
Serial.println("bootCounter: " + String(bootCounter));
Serial.println();
Serial.println("Nulstiller variaber og gemmer i NVS...");
}
preferences.begin("bootlog", false);
preferences.putInt("bootCounter", 0);
bootCounter = preferences.getInt("bootCounter");
preferences.putInt("shortUpTime", 0);
shortUpTime = preferences.getInt("shortUpTime");
preferences.end();
if (debugEnabled) {
Serial.println("Done!");
Serial.println();
Serial.println("shortUpTime: " + String(shortUpTime));
Serial.println("bootCounter: " + String(bootCounter));
Serial.println();
}
}
void logBootTime() { // Log nuværende tid som boot-tid i NVS
if (debugEnabled) {
Serial.println();
Serial.println("logBootTime()");
Serial.println();
}
preferences.begin("bootlog", false);
time_t now;
time(&now);
// Læs eksisterende boot-tider
String bootTimes = preferences.getString("boot_times", "");
// Tilføj den nyeste boot-tid
if (bootTimes.length() > 0) {
bootTimes += ",";
}
bootTimes += String(now);
// Gem de opdaterede boot-tider
preferences.putString("boot_times", bootTimes);
preferences.end();
}
void syncTime() { // Synkroniser tid med NTP-server
if (debugEnabled) {
Serial.println();
Serial.println("syncTime()");
Serial.println();
}
configTime(3600, 3600, "dk.pool.ntp.org"); // Danmark (CET/CEST)
Serial.print("Venter på tidssynkronisering:");
struct tm timeinfo;
while (!getLocalTime(&timeinfo)) {
Serial.print(".");
delay(500); // Vent lidt, før vi prøver igen
}
Serial.print("\nTid synkroniseret! ");
Serial.print("Aktuel tid: ");
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S"); // Udskriv den synkroniserede tid
}
void handleSerialInput() { // Håndter seriel kommando linje
String input = Serial.readStringUntil('\n'); // Læs indtil linjeskift
input.trim(); // Fjern eventuelle mellemrum eller linjeskift
if (input == "--debugon" || input == "-don") {
debugEnabled = true;
Serial.println("Debug mode aktiveret.");
}
else if (input == "--debugoff" || input == "-doff") {
debugEnabled = false;
Serial.println("Debug mode deaktiveret.");
}
else if (input == "--nullifysystem" || input == "-nulls") {
Serial.println("Systemet nulstilles om 42 sekunder!");
Serial.println("SLUK HVIS DET VAR EN FEJL KOMMANDO!");
int seconds =42;
while (seconds > 0) {
Serial.print("Tid tilbage: ");
Serial.print(seconds);
Serial.println(" sekunder");
display.clearDisplay(); // Rydder skærmen før opdatering
display.setCursor(0, 0); // Sætter cursoren i øverste venstre hjørne
display.setTextSize(4); // Sætter skriftstørrelsen til 3 (standard er 1)
display.print(seconds);
delay(1000); // Vent 1 sekund
seconds--;
}
resetConfig();
}
else if (input == "--globalvars" || input == "-gvars") {
PrintGlobalVars2Console();
}
else if (input == "--reconnectmqtt") {
Serial.println("//");
Serial.println("// Forsøger reconnectMQTT()");
Serial.println("//");
reconnectMQTT();
}
else if (input == "--killswitch") {
Serial.println("//");
Serial.println("// Forsøger resetConfig()");
Serial.println("//");
resetConfig();
}
else if (!input.isEmpty()) {
printCmds2Console(); // Beskriv seriel kommandoer i seriel konsol
}
}
void setupConfigServer() { // Starter soft-AP (WiFi access point) til konfiguration af systemet
String apSSID = "fmjheatsensor_config"; // SSID for AP
// Generer en tilfældig kode mellem 100000000 og 999999999
long randomCode = random(100000000, 1000000000);
String apPassword = String(randomCode); // Konverter tallet til en streng
// Start softAP
WiFi.softAP(apSSID.c_str(), apPassword.c_str());
// Vent et øjeblik for at sikre, at access pointet er startet
delay(1000);
// Print IP-adresse for AP
Serial.println("Soft-AP startet.");
server.on("/config", handleConfig); // Håndterer konfigurationsanmodninger
server.begin(); // Starter webserveren
Serial.println("#############################");
Serial.println("Konfigurationsserver startet.");
Serial.println("SSID: " + apSSID);
Serial.println("Kode: " + apPassword);
Serial.println("Tilgå http://"+ WiFi.softAPIP().toString() + "/config for at konfigurere.");
// Vist information om konfigurationsserveren på OLED-skærmen
display.clearDisplay(); // Rydder skærmen før opdatering
display.setCursor(0, 0); // Sætter cursoren i øverste venstre hjørne
display.print("- Config WiFi online!");
display.setCursor(0, 10);
display.print("SSID: " + apSSID);
display.setCursor(0, 19);
display.print("Kodeord:");
display.setTextSize(2); // Sætter skriftstørrelsen til 2 (standard er 1)
display.setCursor(4, 28);
display.print(apPassword); // Vis den tilfældige kode
display.setTextSize(1); // Sætter skriftstørrelsen til 1 (standard er 1)
display.setCursor(0, 45);
display.print(" IP: " + WiFi.softAPIP().toString()); // Vist IP-adressen på soft-AP mode
display.setCursor(0, 55);
display.print("http://<IP>/config");
display.display(); // Vis information på OLED.
// Vi stopper her og venter på, at brugeren gemmer en gyldig konfiguration
while (strlen(ssid) == 0 || strlen(password) == 0) {
delay(1000); // Fortsæt med at vente, mens vi holder serveren aktiv
server.handleClient(); // Håndter klientanmodninger under ventetid
handleSerialInput(); // Tillader at aktiverer debug mode hvis det ønskes.
}
// Når en gyldig konfiguration er gemt, afslut setup og gå videre til WiFi-forbindelse
displayMessage("Konfiguration gemt. Genstarter ESP32.");
delay(1000);
ESP.restart(); // Genstart for at anvende den gemte konfiguration
}
void connectWiFi() { // Tilslutter til det konfigurerede Wireless netværk med mindre der skal køres offline mode.
if (strlen(ssid) == 0 || strlen(password) == 0) {
displayMessage("Ugyldig WiFi config");
return;
}
displayMessage("Forbinder til WiFi...");
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 10) {
delay(1000);
attempts++;
displayMessage("Forbinder: test " + String(attempts));
Serial.println("Forbinder til WiFi: test " + String(attempts));
}
if (WiFi.status() != WL_CONNECTED) {
display.clearDisplay(); // Rydder skærmen før visning
display.setCursor(0, 0);
display.println("WiFi fejlede");
// Vis det aktuelle SSID på linje 2
display.setCursor(0, 16);
display.print("SSID: ");
display.print(ssid); // Vist det aktuelle SSID
display.display(); // Opdaterer skærmen
Serial.println("Kunne ikke forbinde til WiFi ved opstart.");
} else {
displayMessage("WiFi forbundet!");
Serial.print("Klient IP: ");
Serial.println(WiFi.localIP());
}
}
void reconnectMQTT() { // Forsøger at oprette forbindelse til MQTT-serveren
if (mqttClient.connected()) {
Serial.print("mqttClient.connected = true !!");
}
int attempts = 0;
while (!mqttClient.connected() && attempts < 3) {
if (mqttClient.connect(mqtt_clientID, mqtt_user, mqtt_password)) {
mqttClient.publish(mqtt_publTpic, "Forbundet til MQTT"); // Send en testmeddelelse
} else {
attempts ++;
Serial.println("");
Serial.print("FEJL, RC=");
Serial.print(mqttClient.state());
Serial.println(" venter 1420 msek.");
delay(1420); // Vent lidt før et nyt forsøg
}
}
// Hvis MQTT-forbindelsen mislykkes efter flere forsøg
if (!mqttClient.connected()) {
Serial.println("Kunne ikke forbinde til MQTT efter 3 forsøg.");
int stateCode = mqttClient.state();
// Detaljeret fejlmeddelelse baseret på state koden
switch (stateCode) {
case MQTT_CONNECTION_TIMEOUT:
Serial.println("Fejl: MQTT_CONNECTION_TIMEOUT - Serveren svarede ikke inden for den tilladte tid.");
break;
case MQTT_CONNECTION_LOST:
Serial.println("Fejl: MQTT_CONNECTION_LOST - Netværksforbindelsen blev brudt.");
break;
case MQTT_CONNECT_FAILED:
Serial.println("Fejl: MQTT_CONNECT_FAILED - Netværksforbindelsen mislykkedes.");
break;
case MQTT_DISCONNECTED:
Serial.println("Fejl: MQTT_DISCONNECTED - Klienten er blevet afbrudt korrekt.");
break;
case MQTT_CONNECT_BAD_PROTOCOL:
Serial.println("Fejl: MQTT_CONNECT_BAD_PROTOCOL - Serveren understøtter ikke den ønskede MQTT-version.");
break;
case MQTT_CONNECT_BAD_CLIENT_ID:
Serial.println("Fejl: MQTT_CONNECT_BAD_CLIENT_ID - Serveren afviste klientens ID.");
break;
case MQTT_CONNECT_UNAVAILABLE:
Serial.println("Fejl: MQTT_CONNECT_UNAVAILABLE - Serveren kunne ikke acceptere forbindelsen.");
break;
case MQTT_CONNECT_BAD_CREDENTIALS:
Serial.println("Fejl: MQTT_CONNECT_BAD_CREDENTIALS - Brugernavn/adgangskode blev afvist.");
break;
case MQTT_CONNECT_UNAUTHORIZED:
Serial.println("Fejl: MQTT_CONNECT_UNAUTHORIZED - Klienten blev ikke autoriseret til at oprette forbindelse.");
break;
default:
Serial.println("Ukendt fejl ved oprettelse af forbindelse til MQTT.");
break;
}
/* Fejlkoder:
-4 : MQTT_CONNECTION_TIMEOUT - the server didn't respond within the keepalive time
-3 : MQTT_CONNECTION_LOST - the network connection was broken
-2 : MQTT_CONNECT_FAILED - the network connection failed
-1 : MQTT_DISCONNECTED - the client is disconnected cleanly
0 : MQTT_CONNECTED - the client is connected
1 : MQTT_CONNECT_BAD_PROTOCOL - the server doesn't support the requested version of MQTT
2 : MQTT_CONNECT_BAD_CLIENT_ID - the server rejected the client identifier
3 : MQTT_CONNECT_UNAVAILABLE - the server was unable to accept the connection
4 : MQTT_CONNECT_BAD_CREDENTIALS - the username/password were rejected
5 : MQTT_CONNECT_UNAUTHORIZED - the client was not authorized to connect
*/
// Yderligere handling, som kan være nødvendig, kan også placeres her
}
}
void sendTemperatureToMQTT(float temp) { // Sender aktuel temperatur til MQTT server
if (!mqttClient.connected()) {
return; // Send kun, hvis MQTT er forbundet
}
// Beskyttelse mod utilsigtet dobbelt-send i samme cyklus / kort tid efter hinanden.
static unsigned long lastTempPublishAt = 0;
static char lastTempStr[16] = "";
char tempStr[10]; // Midlertidig buffer
dtostrf(temp, 6, 2, tempStr); // Konverterer float til string med 2 decimaler
unsigned long now = millis();
// Hvis samme payload blev sendt for mindre end 1500 ms siden, så undlad at sende igen.
if (strcmp(tempStr, lastTempStr) == 0 && (now - lastTempPublishAt) < 1500) {
if (debugEnabled) {
Serial.println("MQTT publish skipped (duplicate within 1500ms): " + String(tempStr));
}
return;
}
// Gem info om sidste sendte besked
strncpy(lastTempStr, tempStr, sizeof(lastTempStr));
lastTempPublishAt = now;
mqttClient.publish(mqtt_publTpic, tempStr);
debugEnabled ? (void)Serial.println("MQTT publish: " + String(tempStr)) : (void)0;
}
void updateOledScreen(float temp) { // Opdatrerer OLED skærm med information under normal drift
display.clearDisplay(); // Rydder skærmen før opdatering
display.setCursor(0, 0); // Sætter cursoren i øverste venstre hjørne
// Vist temperatur på den første linje
display.setTextSize(3); // Sætter skriftstørrelsen til 3 (standard er 1)
// display.print(" "); // lav et mellemrum
display.print(temp); // Vis temperatur
//display.print(" "); // lav et mellemrum
display.write(247); // 247 er ASCII-koden for '°' i Adafruit GFX
display.print("C");
display.setTextSize(1); // Gå tilbage til normal skriftstørrelse for de næste linjer
if (debugEnabled) {
char tempStr[10]; // Midlertidig buffer
dtostrf(temp, 6, 2, tempStr); // Konverterer float til string med 2 decimaler
Serial.println("OLED update: Temperatur = " + String(tempStr));
}
// Vis IP-adresse på den næste linje
display.setCursor(0, 28); // Sætter cursoren på næste linje
// Vis WiFi status på næste linje
display.print("WiFi: ");
if (!offlineMode) { // Hvis der IKKE køres i Offline Mode:
if (WiFi.status() == WL_CONNECTED) {
display.print(WiFi.SSID()); // Vis SSID for aktuel WiFi
display.setCursor(0, 41); // Sætter cursoren på den næste linje
display.print("IP: ");
display.print(WiFi.localIP()); // Vist IP-adresse
debugEnabled ? (void)Serial.println("OLED update: SSID: " + WiFi.SSID()) : (void)0;
debugEnabled ? (void)Serial.println(String("OLED update: IP = ") + WiFi.localIP().toString()) : (void)0;
// Vis MQTT informationer på næste linje
display.setCursor(0, 53); // Sætter cursoren på næste linje
display.print("MQTT: ");
if (strlen(mqtt_server) > 0) { // Vis kun MQTT status , hvis mqtt_server er angivet
display.print(mqttClient.connected() ? "Forbundet" : "Ikke forbundet");
debugEnabled ? (void)Serial.println("OLED update: MQTT: " + mqttClient.connected() ? "Forbundet" : "Ikke forbundet") : (void)0;
} else {
display.print("Ikke i brug.");
debugEnabled ? (void)Serial.println("OLED update: MQTT: Ikke i brug.") : (void)0;
}
} else {
display.print("Ikke forbundet");
debugEnabled ? (void)Serial.println("OLED update: WiFi: Ikke forbundet") : (void)0;
}
} else {
display.print("Offline mode!");
}
display.display(); // Opdaterer skærmen med de nye data
}
void displayMessage(String message) { // Skriver tekst ca. midt på OLED skærmen
display.clearDisplay(); // Rydder skærmen
display.setCursor(0, 24); // Sætter cursoren ca. midt på skærmen
display.println(message); // Vist besked på skærmen
display.display(); // Opdaterer skærmen
}
void handleRoot() { // Håndterer web kald til / (root) der viser temperaturdata
sensors.requestTemperatures(); // Anmoder om temperaturmåling
float tempC = sensors.getTempCByIndex(0); // Henter temperatur fra den første sensor (index 0)
String response;
if (tempC <= -127) {
response = "<p id='temperature' class='error'>Fejl: Ingen sensor fundet</p>\n";
} else {
response = "<div class='meter-container'><meter id='temperatur' value='" + String(tempC) + "' min='0' max='45' low='27' high='41' optimum='38'></meter></div>";
response += "<p id='temperature' class='temperature'>Aktuel temperatur: " + String(tempC) + " °C</p>\n";
}
String ipAddress = WiFi.localIP().toString();
response += "<div class='info'>\nGå til <a href='http://" + ipAddress + "/reset'>" + ipAddress + "/reset</a> for at factory resette.<br />\n";
response += "Gå til <a href='http://" + ipAddress + "/config'>" + ipAddress + "/config</a> for at ændre konfig.\n</div>\n";
// Tilføjer HTML, CSS og JavaScript for dark mode og dynamisk temperatur-opdatering
String htmlResponse = "<!DOCTYPE html>\n<html lang='da'>\n<head>\n";
htmlResponse += "<meta charset='UTF-8'>\n";
htmlResponse += "<meta name='viewport' content='width=device-width, initial-scale=1.0'>\n";
htmlResponse += "<title>FmjHeatSensor Temperatur</title>\n";
htmlResponse += "<style>\n";
htmlResponse += "body { font-family: Arial, sans-serif; background-color: lightgray; margin: 0; padding: 0; color: darkslategray; text-align: center; transition: background-color 0.3s, color 0.3s; }\n";
htmlResponse += "h1 { color: green; font-size: 2rem; }\n";
htmlResponse += ".meter-container { margin-top: 20px; }\n";
htmlResponse += "meter { width: 80%; max-width: 400px; height: 20px; }\n";
htmlResponse += ".temperature, .error { font-size: 1.2rem; margin-top: 10px; }\n";
htmlResponse += ".error { color: red; }\n";
htmlResponse += ".info { color: silver; font-size: 1rem; margin-top: 20px; }\n";
htmlResponse += ".info.dark-mode { color: dimgray; }\n";
htmlResponse += "a, a:visited { color: silver; text-decoration: underline; }\n";
htmlResponse += "a.dark-mode, a.dark-mode:visited { color: dimgray; text-decoration: underline; }\n";
htmlResponse += "button { padding: 10px 20px; margin-top: 20px; cursor: pointer; background-color: green; color: white; border: none; border-radius: 5px; font-size: 1rem; }\n";
htmlResponse += "button:hover { background-color: darkgreen; }\n";
htmlResponse += "body.dark-mode { background-color: black; color: green; }\n";
htmlResponse += "body.dark-mode a { color: dimgray; }\n";
htmlResponse += "</style>\n";
htmlResponse += "</head>\n<body>\n";
htmlResponse += "<h1>Temperaturmåling</h1>\n";
htmlResponse += response;
htmlResponse += "<button id='themeToggle' onclick='toggleDarkMode()'>Dark Mode</button>\n";
htmlResponse += "<script>\n";
// Dark mode javascript funktion.
htmlResponse += "function toggleDarkMode() {\n";
htmlResponse += " document.body.classList.toggle('dark-mode');\n";
htmlResponse += " const isDarkMode = document.body.classList.contains('dark-mode');\n";
htmlResponse += " localStorage.setItem('darkMode', isDarkMode);\n";
htmlResponse += " document.getElementById('themeToggle').textContent = isDarkMode ? 'Light Mode' : 'Dark Mode';\n";
htmlResponse += " // Toggler dark-mode for .info elementerne\n";
htmlResponse += " document.querySelectorAll('.info').forEach(function(el) {\n";
htmlResponse += " el.classList.toggle('dark-mode', isDarkMode);\n";
htmlResponse += " });\n";
htmlResponse += " // Toggler dark-mode for <a> elementerne\n";
htmlResponse += " document.querySelectorAll('a').forEach(function(el) {\n";
htmlResponse += " el.classList.toggle('dark-mode', isDarkMode);\n";
htmlResponse += " });\n";
htmlResponse += "}\n";
htmlResponse += "if (localStorage.getItem('darkMode') === 'true') {\n";
htmlResponse += " document.body.classList.add('dark-mode');\n";
htmlResponse += " document.getElementById('themeToggle').textContent = 'Light Mode';\n";
htmlResponse += " // Tilføjer dark-mode til .info elementerne ved sideindlæsning, hvis dark mode er aktivt\n";
htmlResponse += " document.querySelectorAll('.info').forEach(function(el) {\n";
htmlResponse += " el.classList.add('dark-mode');\n";
htmlResponse += " });\n";
htmlResponse += " // Tilføjer dark-mode til <a> elementerne ved sideindlæsning, hvis dark mode er aktivt\n";
htmlResponse += " document.querySelectorAll('a').forEach(function(el) {\n";
htmlResponse += " el.classList.add('dark-mode');\n";
htmlResponse += " });\n";
htmlResponse += "}\n";
// Javascript funktion til at opdatere temperatur dynamisk.
htmlResponse += "function fetchTemperature() {\n";
htmlResponse += " fetch('/temperature').then(response => response.json()).then(data => {\n";
htmlResponse += " document.getElementById('temperature').innerHTML = 'Aktuel temperatur: ' + data.tempC + ' °C';\n";
htmlResponse += " document.getElementById('temperatur').value = data.tempC; // Opdater meter element\n";
htmlResponse += " });\n";
htmlResponse += "}\n";
htmlResponse += "setInterval(fetchTemperature, " + String(updateInterval) + ");\n";
htmlResponse += "</script>\n";
htmlResponse += "<!-- Created by https://munkjensen.net december 2024. -->\n";
htmlResponse += "<!-- Firmware version: " + String(FirmwareVersion) + " -->\n";
htmlResponse += "</body>\n</html>";
server.send(200, "text/html", htmlResponse);
}
void handleTemperature() { // URL til at sende temperaturdata til JavaScript opdatering af handleRoot
sensors.requestTemperatures();
float tempC = sensors.getTempCByIndex(0);
// Tjek for sensorfejl
if (tempC == -127.0) {
String errorResponse = "{ \"error\": \"Ingen sensor fundet\" }";
server.send(200, "application/json", errorResponse); // Sender fejl som JSON
} else {
// Sender temperaturdata som JSON
String jsonResponse = "{ \"tempC\": " + String(tempC) + " }";
server.send(200, "application/json", jsonResponse);
}
}
void handleReset() { // Håndterer web kald til /reset (websiden som kan bruges til at nulstille systemet)
String input = server.arg("reset_input"); // Få input fra formularen
// Tjek om input er "reset", uanset store/små bogstaver
if (input.equalsIgnoreCase("reset")) {
server.send(200, "text/plain", "System nulstilles og genstartes."); // Vist besked om nulstilling
resetConfig(); // Nulstil konfiguration.
delay(3000); // Vent tre sekunder.
ESP.restart(); // Genstart ESP32.
} else {
// Hvis input ikke er korrekt, send en fejlbesked
String htmlResponse = "<!DOCTYPE html>\n<html lang='da'>\n<head>\n";
htmlResponse += "<meta http-equiv='refresh' content='15'>\n";
htmlResponse += "<meta charset='UTF-8'>\n";
htmlResponse += "<meta name='viewport' content='width=device-width, initial-scale=1.0'>\n";
htmlResponse += "<title>FmjHeatSensor Temperatur</title>\n";
htmlResponse += "</head>\n<body style='color: red; text-align: center;'>\n";
htmlResponse += "<h1>Skriv 'reset' i tekstboksen for, at nulstille systemet.</h1>\n";
htmlResponse += "<form method='POST' action='/reset'>\n";
htmlResponse += "<input type='text' name='reset_input' placeholder='Skriv her' required>\n";
htmlResponse += "<input type='submit' value='Nulstil'>\n";
htmlResponse += "</form>\n";
htmlResponse += "</body>\n</html>";
server.send(400, "text/html", htmlResponse); // Send fejlmeddelelse hvis input er forkert
}
}
void handleConfig() { // Håndterer web kald til /config (webside hvor systemet kan konfigureres)
// Hvis det er en POST-anmodning (brugeren indsender data)
if (server.method() == HTTP_POST) {
// Læs konfigurationsdata fra webformularen og gem dem i variablerne
strncpy(ssid, server.arg("ssid").c_str(), sizeof(ssid));
strncpy(password, server.arg("password").c_str(), sizeof(password));
strncpy(mqtt_server, server.arg("mqtt_server").c_str(), sizeof(mqtt_server));
mqtt_port = server.arg("mqtt_port").toInt(); // Konverterer MQTT-porten fra tekst til integer
if (mqtt_port <= 0 || mqtt_port > 65535) { // Validering af portnummer
mqtt_port = 1883; // Standard MQTT-port, hvis den modtagne er ugyldig
}
strncpy(mqtt_user, server.arg("mqtt_user").c_str(), sizeof(mqtt_user));
strncpy(mqtt_password, server.arg("mqtt_password").c_str(), sizeof(mqtt_password));
strncpy(mqtt_publTpic, server.arg("mqtt_publTpic").c_str(), sizeof(mqtt_publTpic));
strlcat(mqtt_publTpic, "/temperatur", sizeof(mqtt_publTpic)); // hardcode af mqtt_publTpic så det altid ender på '/temperatur' uanset hvad der konfigureres før slash af bruger.
saveConfig(); // Gem konfigurationen i NVS
server.send(200, "text/plain", "Konfiguration gemt. Genstartarter..."); // Bekræftelse og information
delay(3000);
ESP.restart(); // Genstart for at anvende den nye konfiguration
} else {
// Returner HTML-formular for konfiguration
String html = "<!DOCTYPE html><html lang='da'><head><meta charset='UTF-8'><meta name='viewport' content='width=device-width, initial-scale=1.0'><title>Konfiguration</title>";
html += "<style>";
html += "body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f4f4f9; color: #333; }";
html += "h1 { text-align: center; color: #4CAF50; }";
html += ".container { max-width: 600px; margin: 20px auto; padding: 20px; background-color: white; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); }";
html += "input[type='text'], input[type='password'], input[type='number'] { width: 100%; padding: 12px; margin: 8px 0; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }";
html += "input[type='submit'] { background-color: #4CAF50; color: white; padding: 14px 20px; border: none; border-radius: 4px; cursor: pointer; width: 100%; }";
html += "input[type='submit']:hover { background-color: #45a049; }";
html += "label { font-weight: bold; margin-top: 10px; display: block; }";
html += ".info { text-align: center; font-size: 14px; color: #777; margin-top: 20px; }";
html += "</style>";
html += "</head><body>";
html += "<div class='container'>";
html += "<h1>Konfiguration</h1>";
html += "<form method='POST'>";
html += "<label for='ssid'>SSID:</label><input id='ssid' name='ssid' type='text' value='";
html += (ssid != "") ? htmlEscape(ssid) : "";
html += "' required><br>";
html += "<label for='password'>Password:</label><input id='password' name='password' type='password' required><br>";
html += "<label for='mqtt_server'>MQTT Server:</label><input id='mqtt_server' name='mqtt_server' type='text' value='";
html += (mqtt_server != "") ? htmlEscape(mqtt_server) : "";
html += "'><br>";
html += "<label for='mqtt_port'>MQTT Port:</label><input id='mqtt_port' name='mqtt_port' type='number' value='";
html += (mqtt_port > 0) ? htmlEscape(String(mqtt_port)) : "1883";
html += "' min='1' max='65535'><br>";
html += "<label for='mqtt_user'>MQTT User:</label><input id='mqtt_user' name='mqtt_user' type='text' value='";
html += (mqtt_user != "") ? htmlEscape(mqtt_user) : "";
html += "'><br>";
html += "<label for='mqtt_password'>MQTT Password:</label><input id='mqtt_password' name='mqtt_password' type='password'><br>";
html += "<label for='mqtt_publTpic'>MQTT Topic (skal ændres hvis flere sensors er på samme netværk):</label><input id='mqtt_publTpic' name='mqtt_publTpic' type='text' value='";
char* pos = strstr(mqtt_publTpic, "/temperatur"); // Fjern /temperatur suffix midlertidigt
if (pos != nullptr) {
*pos = '\0';
}
html += (mqtt_publTpic != "") ? htmlEscape(mqtt_publTpic) : "fmjheatsensor";
strlcat(mqtt_publTpic, "/temperatur", sizeof(mqtt_publTpic)); // Tilføj /temperatur tilbage igen
html += "'><br>";
html += " PS: Ovenstående MQTT topic tilføjes <i>/temperatur</i> i enden.<br>";
html += " <br>";
html += "<input type='submit' value='Gem Konfiguration'>";
html += "</form>";
html += "<div class='info'>Undlad MQTT-information, hvis den ikke skal bruges.</div>";
html += "</div></body></html>";
server.send(200, "text/html", html); // Sender formularen som HTML
}
}
String htmlEscape(const String& str){ // Funktion til konvertering af 'ulovlige' karakterer der ellers kan lave kludder i HTML
String out;
out.reserve(str.length() + 16);
for (size_t i = 0; i < str.length(); i++)
{
switch (str[i])
{
case '&': out += F("&"); break;
case '<': out += F("<"); break;
case '>': out += F(">"); break;
case '"': out += F("""); break;
case '\'': out += F("'"); break;
default: out += str[i];
}
}
return out;
}
void readConfig() { // Læs konfigurationen fra NVS
preferences.begin("config", true); // Åbner NVS i læsetilstand
// Læs WiFi-relaterede indstillinger
String stored_ssid = preferences.getString("ssid", "");
String stored_password = preferences.getString("password", "");
// Kopier værdierne fra String til char arrays
stored_ssid.toCharArray(ssid, sizeof(ssid)); // Konverter String til char array
stored_password.toCharArray(password, sizeof(password)); // Konverter String til char array
// Læs MQTT-relaterede indstillinger
String stored_mqtt_server = preferences.getString("mqtt_server", "");
stored_mqtt_server.toCharArray(mqtt_server, sizeof(mqtt_server)); // Kopier til global variabel
mqtt_port = preferences.getUInt("mqtt_port", 1883); // Læs MQTT port som er dicimal og ikke skal konverteres toCharArray
String stored_mqtt_user = preferences.getString("mqtt_user", "");
stored_mqtt_user.toCharArray(mqtt_user, sizeof(mqtt_user)); // Kopier til global variabel
String stored_mqtt_password = preferences.getString("mqtt_password", "");
stored_mqtt_password.toCharArray(mqtt_password, sizeof(mqtt_password)); // Kopier til global variabel
String stored_mqtt_publTpic = preferences.getString("mqtt_publTpic", "");
stored_mqtt_publTpic.toCharArray(mqtt_publTpic, sizeof(mqtt_publTpic)); // Kopier til global variabel
preferences.end(); // Lukker NVS
//if (debugEnabled) {
Serial.println("--");
Serial.println("Konfiguration læst:");
Serial.println("SSID: " + String(ssid));
Serial.println("Password: " + String(password));
Serial.println("MQTT Server: " + String(mqtt_server));
Serial.println("MQTT Port: " + String(mqtt_port));
Serial.println("MQTT User: " + String(mqtt_user));
Serial.println("MQTT Password: " + String(mqtt_password));
Serial.println("MQTT Topic: " + String(mqtt_publTpic));
Serial.println("--");
//}
}
void saveConfig() { // Gemmer konfigurationen i NVS
preferences.begin("config", false); // Åbner NVS i læse/skrive-tilstand
preferences.putString("ssid", ssid); // Gemmer SSID
preferences.putString("password", password); // Gemmer password
preferences.putString("mqtt_server", mqtt_server); // Gemmer MQTT server
preferences.putUInt("mqtt_port", mqtt_port); // Gemmer MQTT port
preferences.putString("mqtt_user", mqtt_user); // Gemmer MQTT bruger
preferences.putString("mqtt_password", mqtt_password); // Gemmer MQTT password
preferences.putString("mqtt_publTpic", mqtt_publTpic); // Gemmer MQTT publish topic
preferences.end(); // Lukker NVS
//if (debugEnabled) {
Serial.println("++");
Serial.println("Konfiguration gemt:");
Serial.println("SSID: " + String(ssid));
Serial.println("Password: " + String(password));
Serial.println("MQTT Server: " + String(mqtt_server));
Serial.println("MQTT Port: " + String(mqtt_port));
Serial.println("MQTT User: " + String(mqtt_user));
Serial.println("MQTT Password: " + String(mqtt_password));
Serial.println("MQTT Topic: " + String(mqtt_publTpic));
Serial.println("++");
//}
}
void printCmds2Console() { // Beskriv seriel kommandoer i seriel konsol
Serial.println("//");
Serial.println("// Kommandolinje muligheder:");
Serial.println("// -don, --debugon tænd for debug beskeder på seriel port.");
Serial.println("// -doff, --debugoff sluk for debug beskeder på seriel port.");
Serial.println("// -nulls, --nullifysystem nulstiller til fabriksinstillinger efter 42 sek. nedtælling.");
Serial.println("// -gvars, --globalvars udskriv alle gemte indstillinger.");
Serial.println("// --reconnectmqtt forsøger reconnect til MQTT server");
Serial.println("// --killswitch sletter alle gemte indstillinger.");
Serial.println("//");
Serial.println("// Yderligere features:");
Serial.println("// Sæt Pin 14 LOW for, at få DEBUG info skrevet til seriel port under første gennemløb af loop() efter boot.");
Serial.println("// Hvis ssid og password sætes til '0' aktiveres 'offline mode'.");
Serial.println("// Hvis der bootes 3 ganga i træk slettes alle gemte indstillinger.");
Serial.println("//");
}
void PrintGlobalVars2Console() { // Print alle globale variabler til seriel konsol
Serial.println();
Serial.println(String("SSID: = ") + ssid);
Serial.println(String("Password: = ") + password);
Serial.println(String("mqtt_server = ") + mqtt_server);
Serial.println(String("mqtt_port = ") + mqtt_port);
Serial.println(String("mqtt_clientID = ") + mqtt_clientID);
Serial.println(String("mqtt_user = ") + mqtt_user);
Serial.println(String("mqtt_password = ") + mqtt_password);
Serial.println(String("mqtt_publTpic = ") + mqtt_publTpic);
Serial.println();
}
void resetConfig() { // Nulstiller al konfigurations data i NVS
preferences.begin("config", false); // Åbner et område kaldet "config" i den permanente flash-hukommelse (NVS), som kan skrives til.
preferences.clear(); // Fjerner alle data i 'config' namespace
preferences.end(); // Lukker NVS for at gemme ændringer
preferences.begin("bootlog", false); // Åbner et område kaldet "bootlog" i den permanente flash-hukommelse (NVS), som kan skrives til.
preferences.clear(); // Fjerner alle data i 'bootlog' namespace
preferences.end(); // Lukker NVS for at gemme ændringer
Serial.println("Konfiguration nulstillet.");
display.clearDisplay(); // Rydder skærmen
display.setTextSize(2); // Sætter skriftstørrelsen til 2
display.println("SYSTEM");
display.println(" ZERORIZE");
display.println("ACTIVATED!");
display.println("Rebooting_");
display.display();
Serial.println("WiFi og MQTT nulstillet. Genstarter ESP32.");
delay(3000);
ESP.restart(); // Genstarter ESP32
}
Diverse links
- How to set up ESP32-WROOM-329 in Arduino IDE.
Afslutning
Dette projekt giver dig en robust ESP32-baseret temperaturmåler med webinterface og mulighed for MQTT-dataoverførsel. Fejlhåndteringen sikrer, at enheden fungerer stabilt, selv under netværksproblemer. Du kan nemt nulstille Wi-Fi og MQTT-konfiguration via webinterfacet.