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, 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.
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.