Temperaturføler med ESP32, DS18B20 og OLED-skærm
Indledning
Dette projekt beskriver en ESP32-baseret temperaturmåler, der bruger en DS18B20-sensor til at måle temperaturer fra -5 °C til 50 °C. Temperaturen vises på en OLED-skærm og kan tilgås via en webserver. Projektet inkluderer konfiguration af Wi-Fi og mulighed for at sende data til en MQTT-server med fejlhåndtering og konfigurationsmuligheder.
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.
- 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
Kopier denne kode til Arduino IDE --> |
---|
/* README plz... Benyt "uPesy ESP32 Wroom DevKit" som board model til kompilering. Jeg har oplevet, at andre har fejl der får LED til at blinke for hurtigt !?! */ #include <WiFi.h> #include <WebServer.h> #include <OneWire.h> #include <DallasTemperature.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <PubSubClient.h> #include <EEPROM.h> // === 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 EEPROM_SIZE 4096 // Størrelsen af EEPROM (kan gemme op til 4096 byte). #define RST_PIN 13 // Pin for RST-knappen. #define LED_BUILTIN 2 // Pin for LED. // === 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. // === Globale variabler === 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 udskriver til seriel port? int updateInterval = 15000; // Variabel til 15 sekunders opdateringsinterval angivet i millisekunder. // === Netværksindstillinger === char ssid[32] = ""; // SSID til WiFi netværket. char password[32] = ""; // WiFi adgangskode. // === MQTT-indstillinger === char mqtt_server[128] = ""; // MQTT-serverens IP eller domæne. int mqtt_port = 1883; // MQTT-port (standard 1883). char mqtt_user[128] = ""; // MQTT-brugernavn. char mqtt_password[128] = ""; // MQTT-adgangskode. char mqtt_clientID[128] = ""; // MQTT-clientID. void setup() { // Initiel programmatiske konfigurations kode. pinMode(RST_PIN, INPUT); // Initialiser RST_PIN som et INPUT. pinMode(LED_BUILTIN, OUTPUT); // Initialiser den blå LED. pinMode(RST_PIN, INPUT_PULLUP); // Aktiver intern pull-up modstand. digitalWrite(LED_BUILTIN, LOW); // Sluk den blå LED. Serial.begin(115200); // Initialiserer seriel kommunikation til debugging. EEPROM.begin(EEPROM_SIZE); // Initialiserer EEPROM for at gemme konfigurationer. 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. // OLED-initialisering if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Tjekker om OLED-skærmen er korrekt tilsluttet Serial.println(F("OLED fejl!")); 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 // Tjek om RST-knappen er trykket under opstart if (digitalRead(RST_PIN) == LOW) { // Hvis pin er LOW Serial.println("RST-knappen trykket. Nulstiller WiFi og MQTT konfiguration."); delay(1000); resetConfig(); // Nulstil konfiguration } readConfig(); // Læs gemte konfigurationer fra EEPROM if (strlen(ssid) == 0 || strlen(password) == 0) { // Hvis ingen konfiguration er gemt, start konfigurationsmode displayMessage("WiFi konfig mangler.\n\nStarter konfig WiFi."); setupConfigServer(); // Starter konfigurationsserveren, så brugeren kan konfigurere WiFi og MQTT } else { // WiFi-forbindelse connectWiFi(); // Forbinder til WiFi, hvis SSID og adgangskode er gemt } // 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 (strlen(mqtt_server) > 0) { // start kun MQTT, hvis "mqtt_server" er konfigureret og gemt. // Hent MAC-adressen String mac = WiFi.macAddress(); // Eksempel: "24:6F:28:1A:2B:3C" Serial.println(mac); mac.replace(":",""); // fjern kolon'er String lastSixChars = mac.substring(mac.length() - 6); // Gem de sidste 6 tegn - Resultat: "2B:3C" String mqtt_clientID = "hottub-" + lastSixChars; // Generer klient-id baseret på de sidste 6 bogstaver og tal 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. } // Serial.println(String("mqtt_server = ") + mqtt_server); // Serial.println(String("mqtt_port = ") + mqtt_port); // Serial.println(String("mqtt-klientID = ") + mqtt_clientID); // Serial.println(String("mqtt_user = ") + mqtt_user); // Serial.println(String("mqtt_password = ") + mqtt_password); } Serial.println(" - Debug beskeder kan aktiveres på seriel port med 'debug on' eller 'debug off'(standard)."); Serial.println(" - Systemet kan nulstilles ved at sende 'nullify system' på seriel porten."); } void loop() { // Her placeres koden der skal eksekveres kontinuerligt server.handleClient(); // Lad webserveren behandle indkommende HTTP-anmodninger. 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. 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); } lastMQTTpublish = millis(); // Opdater lastMQTTpublish variablen til nu. } } 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. } if (millis() - lastLEDCheck >= LEDCheckInterval) { // Blinke funktion til indikation af at loop() køre // Serial.print(millis()); // Serial.print(" - "); // Serial.print(lastLEDCheck); // Serial.print(" = "); // Serial.println(millis() - lastLEDCheck); // Serial.println(""); 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 tidspunktet for LED status ændring } if (Serial.available() > 0) { // Håndter seriel kommando linje handleSerialInput(); } } 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 == "debug on") { debugEnabled = true; Serial.println("Debug mode aktiveret."); } else if (input == "debug off") { debugEnabled = false; Serial.println("Debug mode deaktiveret."); } else if (input == "nullify system") { 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"); delay(1000); // Vent 1 sekund seconds--; } resetConfig(); } else { Serial.println("--"); Serial.println("Ugyldig kommando!"); Serial.println("--"); Serial.println("'debug on' eller 'debug off' tænder / slukker for debug beskeder."); Serial.println("'nullify system' nulstiller til fabriksinstillinger."); } } void setupConfigServer() { // Starter soft-AP (WiFi access point) til konfiguration af systemet String apSSID = "Hottub_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 } // 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 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: Forsøg " + String(attempts)); Serial.println("Forbinder til WiFi: Forsøg " + 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.println(WiFi.localIP()); } } void reconnectMQTT() { // Forsøger at oprette forbindelse til MQTT-serveren int attempts = 0; while (!mqttClient.connected() && attempts < 3) { if (mqttClient.connect(mqtt_clientID, mqtt_user, mqtt_password)) { mqttClient.publish("hottubsensor/temperature", "Forbundet til MQTT"); // Send en testmeddelelse } else { Serial.print("FEJL, RC="); // RE 5 er f.eks. MQTT_CONNECT_UNAUTHORIZED - se https://pubsubclient.knolleary.net/api for flere fejl beskeder. Serial.print(mqttClient.state()); Serial.println(" venter 1420 msek."); delay(1420); // Vent lidt før et nyt forsøg attempts++; } } // Hvis MQTT-forbindelsen mislykkes efter flere forsøg if (!mqttClient.connected()) { Serial.println("Kunne ikke forbinde til MQTT efter 3 forsøg."); } } void sendTemperatureToMQTT(float temp) { // Sender aktuel temperatur til MQTT server if (!mqttClient.connected()) { return; // Send kun, hvis MQTT er forbundet } char tempStr[10]; // Midlertidig buffer dtostrf(temp, 6, 2, tempStr); // Konverterer float til string med 2 decimaler mqttClient.publish("hottubsensor/temperature", 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: " + String(tempStr)); } // Vist IP-adresse på den næste linje display.setCursor(0, 28); // Sætter cursoren på næste linje display.print("IP: "); display.print(WiFi.localIP()); // Vist IP-adresse debugEnabled ? (void)Serial.println(String("OLED update: IP = ") + WiFi.localIP().toString()) : (void)0; // Vist WiFi status på næste linje display.setCursor(0, 41); // Sætter cursoren på den næste linje display.print("WiFi: "); if (WiFi.status() == WL_CONNECTED) { display.print(WiFi.SSID()); // Vis SSID for aktuel WiFi debugEnabled ? (void)Serial.println("OLED update: SSID: " + WiFi.SSID()) : (void)0; } else { display.print("Ikke forbundet"); debugEnabled ? (void)Serial.println("OLED update: WiFi: Ikke forbundet") : (void)0; } // Vis MQTT informationer på næste linje display.setCursor(0, 53); // Sætter cursoren på næste linje display.print("MQTT: "); // Vis kun MQTT status , hvis mqtt_server er angivet if (strlen(mqtt_server) > 0) { // Hvis MQTT er aktiveret, vis status, ellers "Ikke forbundet" 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; } 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() { // Root URL, 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.0) { 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='5' high='37' optimum='40'></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>Hottub 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.text()).then(data => {\n"; htmlResponse += " document.getElementById('temperature').innerHTML = data;\n"; htmlResponse += " });\n"; htmlResponse += "}\n"; htmlResponse += "setInterval(fetchTemperature, " + String(updateInterval) + ");\n"; htmlResponse += "</script>\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); if (tempC == -127.0) { server.send(200, "text/html", "<p class='error'>Fejl: Ingen sensor fundet</p>"); } else { server.send(200, "text/html", "Aktuel temperatur: " + String(tempC) + " °C"); } } 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>Hottub 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 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)); // Gem konfigurationen i EEPROM saveConfig(); // Bekræftelse og information server.send(200, "text/plain", "Konfiguration gemt. Genstart ESP32."); delay(1000); 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' 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'><br>"; html += "<label for='mqtt_port'>MQTT Port:</label><input id='mqtt_port' name='mqtt_port' type='number' value='1883' min='1' max='65535'><br>"; html += "<label for='mqtt_user'>MQTT User:</label><input id='mqtt_user' name='mqtt_user' type='text'><br>"; html += "<label for='mqtt_password'>MQTT Password:</label><input id='mqtt_password' name='mqtt_password' type='password'><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 } } void resetConfig() { // Nulstiller al konfigurations data memset(ssid, 0, sizeof(ssid)); memset(password, 0, sizeof(password)); memset(mqtt_server, 0, sizeof(mqtt_server)); memset(mqtt_user, 0, sizeof(mqtt_user)); memset(mqtt_password, 0, sizeof(mqtt_password)); mqtt_port = 1883; // Default port // Initialiser EEPROM med den ønskede størrelse if (!EEPROM.begin(EEPROM_SIZE)) { Serial.println("EEPROM initialization failed."); return; } // Rydder EEPROM ved at skrive 0xFF til hver byte (fjerner gamle data) for (int i = 0; i < EEPROM_SIZE; i++) { EEPROM.write(i, 0xFF); // Write 0xFF to each byte } Serial.println("Skriver til alle adresser."); saveConfig(); // Funktion til at gemme den opdaterede (tomme) konfiguration til EEPROM display.clearDisplay(); // Rydder skærmen display.setTextSize(2); // Sætter skriftstørrelsen til 2 display.println("WiFi og MQTT"); display.println(" nulstillet."); display.println("Genstarter!"); Serial.println("WiFi og MQTT nulstillet. Genstarter ESP32."); delay(3000); ESP.restart(); // Restart the ESP32 } void saveConfig() { // Gemmer konfigurationen i EEPROM EEPROM.put(0, ssid); EEPROM.put(32, password); EEPROM.put(64, mqtt_server); EEPROM.put(128, mqtt_port); EEPROM.put(132, mqtt_user); EEPROM.put(164, mqtt_password); EEPROM.commit(); // Bekræft gemt data } void readConfig() { // Læs konfigurationen fra EEPROM EEPROM.get(0, ssid); EEPROM.get(32, password); EEPROM.get(64, mqtt_server); EEPROM.get(128, mqtt_port); EEPROM.get(132, mqtt_user); EEPROM.get(164, mqtt_password); } |
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.