<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://munkjensen.net/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Admin</id>
	<title>munkjensen.net/wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://munkjensen.net/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Admin"/>
	<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Special:Contributions/Admin"/>
	<updated>2026-05-28T08:12:07Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2083</id>
		<title>Temperaturføler med ESP32, DS18B20 og OLED-skærm</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2083"/>
		<updated>2026-05-27T14:21:07Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Kode til ESP32 */ Fjernet kolaps af afsnit fordi det ikke virker...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Indledning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Projektet inkluderer nem konfiguration af opkobling til Wi-Fi og der er mulighed for, at sende data til en MQTT-server.&lt;br /&gt;
&lt;br /&gt;
== Indkøbsliste ==&lt;br /&gt;
* [https://arduinotech.dk/shop/esp32-usb-c-iot-mainboard-devkitc-cp2102-38pin/ ESP32 USB-C IoT bundkort DevKitC CP2102 38PIN]&lt;br /&gt;
* [https://arduinotech.dk/shop/0-96%e2%80%b3-i2c-iic-spi-serial-128x64-oled-new-model/ 0,96&amp;quot; I2C 128x64 OLED-display]&lt;br /&gt;
* [https://arduinotech.dk/shop/ds18b20-waterproof-digital-thermal-probe-with-3-5mm-mini-audio-jack-1-meter-cable/ DS18B20 vandtæt digital termisk sonde (1 meter kabel)]&lt;br /&gt;
* [https://arduinotech.dk/shop/tsp-xx-replace-hlk-pmxx-ac-dc-220v-to-12v-buck-step-down-power-supply-module-converter/ HLK-PMxx 220VAC til 5VDC Step Down Converter]&lt;br /&gt;
* [https://arduinotech.dk/shop/sonoff-ip66-waterproof-case/ Sonoff IP66 vandtæt etui]&lt;br /&gt;
* [https://arduinotech.dk/shop/prototype-pcb-print/ Prototype PCB print - Type 1]&lt;br /&gt;
&lt;br /&gt;
== Byggevejledning ==&lt;br /&gt;
&lt;br /&gt;
1. Tilslutninger:  &lt;br /&gt;
   - DS18B20:  &lt;br /&gt;
     - VCC (rød)  → 3.3V på ESP32  &lt;br /&gt;
     - GND (sort) → GND på ESP32  &lt;br /&gt;
     - Data (gul) → GPIO 4 på ESP32  &lt;br /&gt;
     - Tilføj en 4,7kΩ pull-up modstand mellem Data og VCC.&lt;br /&gt;
&lt;br /&gt;
   - OLED-skærm (I2C):  &lt;br /&gt;
     - VCC → 3.3V på ESP32  &lt;br /&gt;
     - GND → GND  på ESP32  &lt;br /&gt;
     - SDA → GPIO 21 på ESP32  &lt;br /&gt;
     - SCL → GPIO 22 på ESP32  &lt;br /&gt;
&lt;br /&gt;
2. Strømforsyning:  &lt;br /&gt;
   Tilslut ESP32 og OLED til 5V strømforsyning via HLK-PMxx-modulet.&lt;br /&gt;
&lt;br /&gt;
3. Montering:  &lt;br /&gt;
   - Montér komponenterne på prototype-PCB.  &lt;br /&gt;
   - Placér hele opsætningen i Sonoff IP66 vandtæt etui for beskyttelse mod vejr og støv.&lt;br /&gt;
&lt;br /&gt;
== Kode til ESP32 ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* README plz...&lt;br /&gt;
   Benyt &amp;quot;uPesy ESP32 Wroom DevKit&amp;quot; som board model til kompilering.&lt;br /&gt;
   Jeg har oplevet, at andre har fejl der får LED til at blinke for hurtigt !?!&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;WiFi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;WebServer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;OneWire.h&amp;gt;&lt;br /&gt;
#include &amp;lt;DallasTemperature.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_GFX.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_SSD1306.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;&lt;br /&gt;
#include &amp;lt;EEPROM.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// === PIN-konfigurationer ===&lt;br /&gt;
#define ONE_WIRE_BUS   4  // Pin til DS18B20 temperatursensoren.&lt;br /&gt;
#define SCREEN_WIDTH 128  // Skærmens bredde (for OLED).&lt;br /&gt;
#define SCREEN_HEIGHT 64  // Skærmens højde (for OLED).&lt;br /&gt;
#define OLED_RESET    -1  // Reset pin til OLED (sættes til -1, da vi ikke bruger en fysisk reset pin).&lt;br /&gt;
#define EEPROM_SIZE 4096  // Størrelsen af EEPROM (kan gemme op til 4096 byte).&lt;br /&gt;
#define RST_PIN       13  // Pin for RST-knappen.&lt;br /&gt;
#define LED_BUILTIN    2  // Pin for LED.&lt;br /&gt;
&lt;br /&gt;
// === Globale objekter ===&lt;br /&gt;
OneWire oneWire(ONE_WIRE_BUS);        // Initialiserer OneWire-bus til DS18B20.&lt;br /&gt;
DallasTemperature sensors(&amp;amp;oneWire);  // Opretter et objekt til at håndtere temperaturmålinger.&lt;br /&gt;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &amp;amp;Wire, OLED_RESET);  // OLED-skærm til at vise informationer.&lt;br /&gt;
WebServer server(80);                 // Webserver på port 80 til at håndtere HTTP-anmodninger.&lt;br /&gt;
WiFiClient espClient;                 // WiFi klient til MQTT.&lt;br /&gt;
PubSubClient mqttClient(espClient);   // MQTT-klient, der bruger WiFi klienten.&lt;br /&gt;
&lt;br /&gt;
// === Globale variabler ===&lt;br /&gt;
unsigned long lastCommsCheck = 0;          // Tidspunkt for sidste Comms-tjek.&lt;br /&gt;
unsigned long CommsCheckInterval = 60000;  // Tidsinterval for Comms-tjek (60 sekunder) angivet i millisekunder.&lt;br /&gt;
unsigned long lastLEDCheck = 0;            // Tidspunkt for sidste LED-tjek.&lt;br /&gt;
unsigned long LEDCheckInterval = 2000;     // Tidsinterval for LED-tjek (2 sekunder) angivet i millisekunder.&lt;br /&gt;
unsigned long lastMQTTpublish = 0;         // Tidspunkt for sidste MQTT-publish.&lt;br /&gt;
unsigned long MQTTPublishInterval = 15000; // Tidsinterval for MQTT publish (15 sekunder) angivet i millisekunder.&lt;br /&gt;
bool debugEnabled = false;                 // Skal der udskriver til seriel port?&lt;br /&gt;
int updateInterval = 15000;                // Variabel til 15 sekunders opdateringsinterval angivet i millisekunder.&lt;br /&gt;
&lt;br /&gt;
// === Netværksindstillinger ===&lt;br /&gt;
char ssid[32] = &amp;quot;&amp;quot;;      // SSID til WiFi netværket.&lt;br /&gt;
char password[32] = &amp;quot;&amp;quot;;  // WiFi adgangskode.&lt;br /&gt;
&lt;br /&gt;
// === MQTT-indstillinger ===&lt;br /&gt;
char mqtt_server[128] = &amp;quot;&amp;quot;;    // MQTT-serverens IP eller domæne.&lt;br /&gt;
int mqtt_port = 1883;          // MQTT-port (standard 1883).&lt;br /&gt;
char mqtt_user[128] = &amp;quot;&amp;quot;;      // MQTT-brugernavn.&lt;br /&gt;
char mqtt_password[128] = &amp;quot;&amp;quot;;  // MQTT-adgangskode.&lt;br /&gt;
char mqtt_clientID[128] = &amp;quot;&amp;quot;;  // MQTT-clientID.&lt;br /&gt;
&lt;br /&gt;
void setup() { // Initiel programmatiske konfigurations kode. &lt;br /&gt;
  pinMode(RST_PIN, INPUT);        // Initialiser RST_PIN som et INPUT.&lt;br /&gt;
  pinMode(LED_BUILTIN, OUTPUT);   // Initialiser den blå LED.&lt;br /&gt;
  pinMode(RST_PIN, INPUT_PULLUP); // Aktiver intern pull-up modstand.&lt;br /&gt;
  digitalWrite(LED_BUILTIN, LOW); // Sluk den blå LED.&lt;br /&gt;
  Serial.begin(115200);           // Initialiserer seriel kommunikation til debugging.&lt;br /&gt;
  EEPROM.begin(EEPROM_SIZE);      // Initialiserer EEPROM for at gemme konfigurationer.&lt;br /&gt;
  sensors.begin();                // Starter temperatursensoren.&lt;br /&gt;
  lastCommsCheck = millis();      // Sætter første tidsværdi af variablen.&lt;br /&gt;
  lastLEDCheck = millis();        // Sætter første tidsværdi af variablen.&lt;br /&gt;
  lastMQTTpublish = millis();     // Sætter første tidsværdi af variablen.&lt;br /&gt;
  &lt;br /&gt;
  // OLED-initialisering&lt;br /&gt;
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {  // Tjekker om OLED-skærmen er korrekt tilsluttet&lt;br /&gt;
    Serial.println(F(&amp;quot;OLED fejl!&amp;quot;));&lt;br /&gt;
    for (;;);  // Stopper programmet, hvis skærmen ikke kan initialiseres&lt;br /&gt;
  }&lt;br /&gt;
  display.clearDisplay();  // Rydder skærmen&lt;br /&gt;
  display.setTextSize(1);  // Sætter tekststørrelse&lt;br /&gt;
  display.setTextColor(WHITE);  // Sætter tekstfarve til hvid&lt;br /&gt;
&lt;br /&gt;
  // Tjek om RST-knappen er trykket under opstart&lt;br /&gt;
  if (digitalRead(RST_PIN) == LOW) {  // Hvis pin er LOW&lt;br /&gt;
    Serial.println(&amp;quot;RST-knappen trykket. Nulstiller WiFi og MQTT konfiguration.&amp;quot;);&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    resetConfig();  // Nulstil konfiguration&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readConfig();                   // Læs gemte konfigurationer fra EEPROM&lt;br /&gt;
  if (strlen(ssid) == 0 || strlen(password) == 0) { // Hvis ingen konfiguration er gemt, start konfigurationsmode&lt;br /&gt;
    displayMessage(&amp;quot;WiFi konfig mangler.\n\nStarter konfig WiFi.&amp;quot;);&lt;br /&gt;
    setupConfigServer();          // Starter konfigurationsserveren, så brugeren kan konfigurere WiFi og MQTT&lt;br /&gt;
    } else {&lt;br /&gt;
    // WiFi-forbindelse&lt;br /&gt;
    connectWiFi();                // Forbinder til WiFi, hvis SSID og adgangskode er gemt&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Webserver-ruter&lt;br /&gt;
  server.on(&amp;quot;/&amp;quot;, handleRoot);                   // Root URL, der viser temperaturdata&lt;br /&gt;
  server.on(&amp;quot;/temperature&amp;quot;, handleTemperature); // URL til at sende temperaturdata til JavaScript opdatering af handleRoot&lt;br /&gt;
  server.on(&amp;quot;/config&amp;quot;, handleConfig);           // URL til at konfigurere WiFi og MQTT&lt;br /&gt;
  server.on(&amp;quot;/reset&amp;quot;, handleReset);             // URL til at nulstille WiFi&lt;br /&gt;
  server.begin();  // Starter webserveren&lt;br /&gt;
&lt;br /&gt;
  if (strlen(mqtt_server) &amp;gt; 0) { // start kun MQTT, hvis &amp;quot;mqtt_server&amp;quot; er konfigureret og gemt.&lt;br /&gt;
    // Hent MAC-adressen&lt;br /&gt;
    String mac = WiFi.macAddress();                         // Eksempel: &amp;quot;24:6F:28:1A:2B:3C&amp;quot;&lt;br /&gt;
    Serial.println(mac);&lt;br /&gt;
    mac.replace(&amp;quot;:&amp;quot;,&amp;quot;&amp;quot;);                                    // fjern kolon&#039;er&lt;br /&gt;
    String lastSixChars = mac.substring(mac.length() - 6);  // Gem de sidste 6 tegn - Resultat: &amp;quot;2B:3C&amp;quot;&lt;br /&gt;
    String mqtt_clientID = &amp;quot;hottub-&amp;quot; + lastSixChars;        // Generer klient-id baseret på de sidste 6 bogstaver og tal wifi mac adresse&lt;br /&gt;
    mqttClient.setServer(mqtt_server, mqtt_port);&lt;br /&gt;
    if (!mqttClient.connected()) {                          // HVis MQTT ikke er forbundet,&lt;br /&gt;
      reconnectMQTT();                                      // Forsøg at oprette forbindelse til MQTT.&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_server   = &amp;quot;) + mqtt_server);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_port     = &amp;quot;) + mqtt_port);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt-klientID = &amp;quot;) + mqtt_clientID);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_user     = &amp;quot;) + mqtt_user);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_password = &amp;quot;) + mqtt_password);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot; - Debug beskeder kan aktiveres på seriel port med &#039;debug on&#039; eller &#039;debug off&#039;(standard).&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot; - Systemet kan nulstilles ved at sende &#039;nullify system&#039; på seriel porten.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() { // Her placeres koden der skal eksekveres kontinuerligt&lt;br /&gt;
  server.handleClient();                                   // Lad webserveren behandle indkommende HTTP-anmodninger.&lt;br /&gt;
  sensors.requestTemperatures();                           // Anmoder om temperaturmåling.&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);                // Hente temperatur fra den første sensor (index 0).&lt;br /&gt;
  updateOledScreen(tempC);                                 // Opdaterer OLED-skærmen.&lt;br /&gt;
  &lt;br /&gt;
  if (millis() - lastMQTTpublish &amp;gt;= MQTTPublishInterval) { // Send MQTT publish hvis det er tid til det (hver 15. sekunder)&lt;br /&gt;
    if (strlen(mqtt_server) &amp;gt; 0) {                         // Send kun MQTT, hvis mqtt_server er angivet&lt;br /&gt;
      if (mqttClient.connected()) {                        // Send temperaturdata til MQTT, hvis forbindelsen er aktiv&lt;br /&gt;
        sendTemperatureToMQTT(tempC);&lt;br /&gt;
      }&lt;br /&gt;
      lastMQTTpublish = millis();                          // Opdater lastMQTTpublish variablen til nu.&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if (millis() - lastCommsCheck &amp;gt;= CommsCheckInterval) {   // Tjek forbindelser hvis det er tid til det (hver 60. sekunder)&lt;br /&gt;
    if (WiFi.status() != WL_CONNECTED) {                   // Hvis WiFi ikke er forbundet,&lt;br /&gt;
      connectWiFi();                                       // Forsøg at oprette forbindelse til WiFi.&lt;br /&gt;
    }&lt;br /&gt;
    if (strlen(mqtt_server) &amp;gt; 0) {                         // Tjek kun MQTT, hvis mqtt_server er angivet&lt;br /&gt;
      if (!mqttClient.connected()) {                       // HVis MQTT ikke er forbundet,&lt;br /&gt;
        reconnectMQTT();                                   // Forsøg at oprette forbindelse til MQTT.&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    lastCommsCheck = millis();                             // Opdater lastCommsCheck variablen til nu.&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (millis() - lastLEDCheck &amp;gt;= LEDCheckInterval) {      // Blinke funktion til indikation af at loop() køre&lt;br /&gt;
&lt;br /&gt;
    // Serial.print(millis());&lt;br /&gt;
    // Serial.print(&amp;quot; - &amp;quot;);&lt;br /&gt;
    // Serial.print(lastLEDCheck);&lt;br /&gt;
    // Serial.print(&amp;quot; = &amp;quot;);&lt;br /&gt;
    // Serial.println(millis() - lastLEDCheck);&lt;br /&gt;
    // Serial.println(&amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // Læs den nuværende tilstand af LED_BUILTIN og sæt den modsatte værdi.&lt;br /&gt;
    lastLEDCheck = millis();                              // Opdater tidspunktet for LED status ændring&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (Serial.available() &amp;gt; 0) {                           // Håndter seriel kommando linje&lt;br /&gt;
    handleSerialInput();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleSerialInput() { // Håndter seriel kommando linje&lt;br /&gt;
  String input = Serial.readStringUntil(&#039;\n&#039;);  // Læs indtil linjeskift&lt;br /&gt;
  input.trim();  // Fjern eventuelle mellemrum eller linjeskift&lt;br /&gt;
&lt;br /&gt;
  if (input == &amp;quot;debug on&amp;quot;) {&lt;br /&gt;
    debugEnabled = true;&lt;br /&gt;
    Serial.println(&amp;quot;Debug mode aktiveret.&amp;quot;);&lt;br /&gt;
  } &lt;br /&gt;
  else if (input == &amp;quot;debug off&amp;quot;) {&lt;br /&gt;
    debugEnabled = false;&lt;br /&gt;
    Serial.println(&amp;quot;Debug mode deaktiveret.&amp;quot;);&lt;br /&gt;
  } &lt;br /&gt;
  else if (input == &amp;quot;nullify system&amp;quot;) {&lt;br /&gt;
    Serial.println(&amp;quot;Systemet nulstilles om 42 sekunder!&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;SLUK HVIS DET VAR EN FEJL-KOMMANDO!&amp;quot;);&lt;br /&gt;
    int seconds =42;&lt;br /&gt;
    while (seconds &amp;gt; 0) {&lt;br /&gt;
      Serial.print(&amp;quot;Tid tilbage: &amp;quot;);&lt;br /&gt;
      Serial.print(seconds);&lt;br /&gt;
      Serial.println(&amp;quot; sekunder&amp;quot;);&lt;br /&gt;
      delay(1000);  // Vent 1 sekund&lt;br /&gt;
      seconds--;&lt;br /&gt;
    }&lt;br /&gt;
    resetConfig();&lt;br /&gt;
  } &lt;br /&gt;
  else {&lt;br /&gt;
    Serial.println(&amp;quot;--&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;Ugyldig kommando!&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;--&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;&#039;debug on&#039; eller &#039;debug off&#039; tænder / slukker for debug beskeder.&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;&#039;nullify system&#039; nulstiller til fabriksinstillinger.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupConfigServer() { // Starter soft-AP (WiFi access point) til konfiguration af systemet&lt;br /&gt;
  String apSSID = &amp;quot;Hottub_Config&amp;quot;;  // SSID for AP&lt;br /&gt;
  &lt;br /&gt;
  // Generer en tilfældig kode mellem 100000000 og 999999999&lt;br /&gt;
  long randomCode = random(100000000, 1000000000);&lt;br /&gt;
  String apPassword = String(randomCode);  // Konverter tallet til en streng&lt;br /&gt;
  &lt;br /&gt;
  // Start softAP&lt;br /&gt;
  WiFi.softAP(apSSID.c_str(), apPassword.c_str());&lt;br /&gt;
  &lt;br /&gt;
  // Vent et øjeblik for at sikre, at access pointet er startet&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  &lt;br /&gt;
  // Print IP-adresse for AP&lt;br /&gt;
  Serial.println(&amp;quot;Soft-AP startet.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  server.on(&amp;quot;/config&amp;quot;, handleConfig);  // Håndterer konfigurationsanmodninger&lt;br /&gt;
  server.begin();  // Starter webserveren&lt;br /&gt;
  Serial.println(&amp;quot;#############################&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;Konfigurationsserver startet.&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;SSID: &amp;quot; + apSSID);&lt;br /&gt;
  Serial.println(&amp;quot;Kode: &amp;quot; + apPassword);&lt;br /&gt;
  Serial.println(&amp;quot;Tilgå http://&amp;quot;+ WiFi.softAPIP().toString() + &amp;quot;/config for at konfigurere.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  // Vist information om konfigurationsserveren på OLED-skærmen&lt;br /&gt;
  display.clearDisplay();   // Rydder skærmen før opdatering&lt;br /&gt;
  display.setCursor(0, 0);  // Sætter cursoren i øverste venstre hjørne&lt;br /&gt;
  display.print(&amp;quot;- Config WiFi online!&amp;quot;);&lt;br /&gt;
  display.setCursor(0, 10);&lt;br /&gt;
  display.print(&amp;quot;SSID: &amp;quot; + apSSID);&lt;br /&gt;
  display.setCursor(0, 19);&lt;br /&gt;
  display.print(&amp;quot;Kodeord:&amp;quot;);&lt;br /&gt;
  display.setTextSize(2);    // Sætter skriftstørrelsen til 2 (standard er 1)&lt;br /&gt;
  display.setCursor(4, 28);&lt;br /&gt;
  display.print(apPassword); // Vis den tilfældige kode&lt;br /&gt;
  display.setTextSize(1);    // Sætter skriftstørrelsen til 1 (standard er 1)&lt;br /&gt;
  display.setCursor(0, 45);&lt;br /&gt;
  display.print(&amp;quot; IP:  &amp;quot; + WiFi.softAPIP().toString()); // Vist IP-adressen på soft-AP mode&lt;br /&gt;
  display.setCursor(0, 55);&lt;br /&gt;
  display.print(&amp;quot;http://&amp;lt;IP&amp;gt;/config&amp;quot;);&lt;br /&gt;
  display.display();        // Vis information på OLED.&lt;br /&gt;
&lt;br /&gt;
  // Vi stopper her og venter på, at brugeren gemmer en gyldig konfiguration&lt;br /&gt;
  while (strlen(ssid) == 0 || strlen(password) == 0) {&lt;br /&gt;
    delay(1000);  // Fortsæt med at vente, mens vi holder serveren aktiv&lt;br /&gt;
    server.handleClient();  // Håndter klientanmodninger under ventetid&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  // Når en gyldig konfiguration er gemt, afslut setup og gå videre til WiFi-forbindelse&lt;br /&gt;
  displayMessage(&amp;quot;Konfiguration gemt. Genstarter ESP32.&amp;quot;);&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  ESP.restart();  // Genstart for at anvende den gemte konfiguration&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void connectWiFi() { // Tilslutter til det konfigurerede Wireless netværk&lt;br /&gt;
  if (strlen(ssid) == 0 || strlen(password) == 0) {&lt;br /&gt;
    displayMessage(&amp;quot;Ugyldig WiFi config&amp;quot;);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  displayMessage(&amp;quot;Forbinder til WiFi...&amp;quot;);&lt;br /&gt;
  WiFi.begin(ssid, password);&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED &amp;amp;&amp;amp; attempts &amp;lt; 10) {&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    attempts++;&lt;br /&gt;
    displayMessage(&amp;quot;Forbinder: Forsøg &amp;quot; + String(attempts));&lt;br /&gt;
    Serial.println(&amp;quot;Forbinder til WiFi: Forsøg &amp;quot; + String(attempts));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (WiFi.status() != WL_CONNECTED) {&lt;br /&gt;
    display.clearDisplay();  // Rydder skærmen før visning&lt;br /&gt;
    display.setCursor(0, 0);&lt;br /&gt;
    display.println(&amp;quot;WiFi fejlede&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // Vis det aktuelle SSID på linje 2&lt;br /&gt;
    display.setCursor(0, 16);&lt;br /&gt;
    display.print(&amp;quot;SSID: &amp;quot;);&lt;br /&gt;
    display.print(ssid);  // Vist det aktuelle SSID&lt;br /&gt;
    &lt;br /&gt;
    display.display();  // Opdaterer skærmen&lt;br /&gt;
    Serial.println(&amp;quot;Kunne ikke forbinde til WiFi ved opstart.&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    displayMessage(&amp;quot;WiFi forbundet!&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void reconnectMQTT() { // Forsøger at oprette forbindelse til MQTT-serveren&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (!mqttClient.connected() &amp;amp;&amp;amp; attempts &amp;lt; 3) {&lt;br /&gt;
    if (mqttClient.connect(mqtt_clientID, mqtt_user, mqtt_password)) {&lt;br /&gt;
      mqttClient.publish(&amp;quot;hottubsensor/temperature&amp;quot;, &amp;quot;Forbundet til MQTT&amp;quot;);  // Send en testmeddelelse&lt;br /&gt;
    } else {&lt;br /&gt;
      Serial.print(&amp;quot;FEJL, RC=&amp;quot;);&lt;br /&gt;
      // RE 5 er f.eks. MQTT_CONNECT_UNAUTHORIZED - se https://pubsubclient.knolleary.net/api for flere fejl beskeder.&lt;br /&gt;
      Serial.print(mqttClient.state());&lt;br /&gt;
      Serial.println(&amp;quot; venter 1420 msek.&amp;quot;);&lt;br /&gt;
      delay(1420);  // Vent lidt før et nyt forsøg&lt;br /&gt;
      attempts++;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  // Hvis MQTT-forbindelsen mislykkes efter flere forsøg&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    Serial.println(&amp;quot;Kunne ikke forbinde til MQTT efter 3 forsøg.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void sendTemperatureToMQTT(float temp) { // Sender aktuel temperatur til MQTT server&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    return;  // Send kun, hvis MQTT er forbundet&lt;br /&gt;
  }&lt;br /&gt;
  char tempStr[10];                 // Midlertidig buffer&lt;br /&gt;
  dtostrf(temp, 6, 2, tempStr);     // Konverterer float til string med 2 decimaler&lt;br /&gt;
  mqttClient.publish(&amp;quot;hottubsensor/temperature&amp;quot;, tempStr);&lt;br /&gt;
  debugEnabled ? (void)Serial.println(&amp;quot;MQTT publish: &amp;quot; + String(tempStr)) : (void)0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void updateOledScreen(float temp) { // Opdatrerer OLED skærm med information under normal drift&lt;br /&gt;
  display.clearDisplay();   // Rydder skærmen før opdatering&lt;br /&gt;
  display.setCursor(0, 0);  // Sætter cursoren i øverste venstre hjørne&lt;br /&gt;
  &lt;br /&gt;
  // Vist temperatur på den første linje&lt;br /&gt;
  display.setTextSize(3);   // Sætter skriftstørrelsen til 3 (standard er 1)&lt;br /&gt;
  // display.print(&amp;quot; &amp;quot;);       // lav et mellemrum&lt;br /&gt;
  display.print(temp);      // Vis temperatur&lt;br /&gt;
  //display.print(&amp;quot; &amp;quot;);       // lav et mellemrum&lt;br /&gt;
  display.write(247);       // 247 er ASCII-koden for &#039;°&#039; i Adafruit GFX&lt;br /&gt;
  display.print(&amp;quot;C&amp;quot;);&lt;br /&gt;
  display.setTextSize(1);   // Gå tilbage til normal skriftstørrelse for de næste linjer&lt;br /&gt;
  if (debugEnabled) {&lt;br /&gt;
    char tempStr[10];                 // Midlertidig buffer&lt;br /&gt;
    dtostrf(temp, 6, 2, tempStr);     // Konverterer float til string med 2 decimaler&lt;br /&gt;
    Serial.println(&amp;quot;OLED update: &amp;quot; + String(tempStr));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Vist IP-adresse på den næste linje&lt;br /&gt;
  display.setCursor(0, 28);  // Sætter cursoren på næste linje&lt;br /&gt;
  display.print(&amp;quot;IP:   &amp;quot;);&lt;br /&gt;
  display.print(WiFi.localIP());  // Vist IP-adresse&lt;br /&gt;
  debugEnabled ? (void)Serial.println(String(&amp;quot;OLED update: IP = &amp;quot;) + WiFi.localIP().toString()) : (void)0;&lt;br /&gt;
  &lt;br /&gt;
  // Vist WiFi status på næste linje&lt;br /&gt;
  display.setCursor(0, 41);  // Sætter cursoren på den næste linje&lt;br /&gt;
  display.print(&amp;quot;WiFi: &amp;quot;);&lt;br /&gt;
  if (WiFi.status() == WL_CONNECTED) {&lt;br /&gt;
    display.print(WiFi.SSID()); // Vis SSID for aktuel WiFi&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: SSID: &amp;quot; + WiFi.SSID()) : (void)0;&lt;br /&gt;
  } else {&lt;br /&gt;
    display.print(&amp;quot;Ikke forbundet&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: WiFi: Ikke forbundet&amp;quot;) : (void)0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Vis MQTT informationer på næste linje&lt;br /&gt;
  display.setCursor(0, 53);  // Sætter cursoren på næste linje&lt;br /&gt;
  display.print(&amp;quot;MQTT: &amp;quot;);&lt;br /&gt;
  // Vis kun MQTT status , hvis mqtt_server er angivet&lt;br /&gt;
  if (strlen(mqtt_server) &amp;gt; 0) {&lt;br /&gt;
    // Hvis MQTT er aktiveret, vis status, ellers &amp;quot;Ikke forbundet&amp;quot;&lt;br /&gt;
    display.print(mqttClient.connected() ? &amp;quot;Forbundet&amp;quot; : &amp;quot;Ikke forbundet&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: MQTT: &amp;quot; + mqttClient.connected() ? &amp;quot;Forbundet&amp;quot; : &amp;quot;Ikke forbundet&amp;quot;) : (void)0;&lt;br /&gt;
  } else {&lt;br /&gt;
    display.print(&amp;quot;Ikke i brug.&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: MQTT: Ikke i brug.&amp;quot;) : (void)0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  display.display();  // Opdaterer skærmen med de nye data&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayMessage(String message) { // Skriver tekst ca. midt på OLED skærmen&lt;br /&gt;
  display.clearDisplay();             // Rydder skærmen&lt;br /&gt;
  display.setCursor(0, 24);           // Sætter cursoren ca. midt på skærmen&lt;br /&gt;
  display.println(message);           // Vist besked på skærmen&lt;br /&gt;
  display.display();                  // Opdaterer skærmen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleRoot() { // Root URL, der viser temperaturdata&lt;br /&gt;
  sensors.requestTemperatures();  // Anmoder om temperaturmåling&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);  // Henter temperatur fra den første sensor (index 0)&lt;br /&gt;
  String response;&lt;br /&gt;
&lt;br /&gt;
  if (tempC == -127.0) {&lt;br /&gt;
    response = &amp;quot;&amp;lt;p id=&#039;temperature&#039; class=&#039;error&#039;&amp;gt;Fejl: Ingen sensor fundet&amp;lt;/p&amp;gt;\n&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    response = &amp;quot;&amp;lt;div class=&#039;meter-container&#039;&amp;gt;&amp;lt;meter id=&#039;temperatur&#039; value=&#039;&amp;quot; + String(tempC) + &amp;quot;&#039; min=&#039;0&#039; max=&#039;45&#039; low=&#039;5&#039; high=&#039;37&#039; optimum=&#039;40&#039;&amp;gt;&amp;lt;/meter&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
    response += &amp;quot;&amp;lt;p id=&#039;temperature&#039; class=&#039;temperature&#039;&amp;gt;Aktuel temperatur: &amp;quot; + String(tempC) + &amp;quot; &amp;amp;deg;C&amp;lt;/p&amp;gt;\n&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String ipAddress = WiFi.localIP().toString();&lt;br /&gt;
&lt;br /&gt;
  response += &amp;quot;&amp;lt;div class=&#039;info&#039;&amp;gt;\nGå til &amp;lt;a href=&#039;http://&amp;quot; + ipAddress + &amp;quot;/reset&#039;&amp;gt;&amp;quot; + ipAddress + &amp;quot;/reset&amp;lt;/a&amp;gt; for at factory resette.&amp;lt;br /&amp;gt;\n&amp;quot;;&lt;br /&gt;
  response += &amp;quot;Gå til &amp;lt;a href=&#039;http://&amp;quot; + ipAddress + &amp;quot;/config&#039;&amp;gt;&amp;quot; + ipAddress + &amp;quot;/config&amp;lt;/a&amp;gt; for at ændre konfig.\n&amp;lt;/div&amp;gt;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  // Tilføjer HTML, CSS og JavaScript for dark mode og dynamisk temperatur-opdatering&lt;br /&gt;
  String htmlResponse = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n&amp;lt;html lang=&#039;da&#039;&amp;gt;\n&amp;lt;head&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;title&amp;gt;Hottub Temperatur&amp;lt;/title&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;style&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;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&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;h1 { color: green; font-size: 2rem; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.meter-container { margin-top: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;meter { width: 80%; max-width: 400px; height: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.temperature, .error { font-size: 1.2rem; margin-top: 10px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.error { color: red; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.info { color: silver; font-size: 1rem; margin-top: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.info.dark-mode { color: dimgray; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;a, a:visited { color: silver; text-decoration: underline; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;a.dark-mode, a.dark-mode:visited { color: dimgray; text-decoration: underline; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;button { padding: 10px 20px; margin-top: 20px; cursor: pointer; background-color: green; color: white; border: none; border-radius: 5px; font-size: 1rem; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;button:hover { background-color: darkgreen; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;body.dark-mode { background-color: black; color: green; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;body.dark-mode a { color: dimgray; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/style&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/head&amp;gt;\n&amp;lt;body&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;h1&amp;gt;Temperaturmåling&amp;lt;/h1&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += response;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;button id=&#039;themeToggle&#039; onclick=&#039;toggleDarkMode()&#039;&amp;gt;Dark Mode&amp;lt;/button&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;script&amp;gt;\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  // Dark mode javascript funktion.&lt;br /&gt;
  htmlResponse += &amp;quot;function toggleDarkMode() {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.body.classList.toggle(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  const isDarkMode = document.body.classList.contains(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  localStorage.setItem(&#039;darkMode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.getElementById(&#039;themeToggle&#039;).textContent = isDarkMode ? &#039;Light Mode&#039; : &#039;Dark Mode&#039;;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Toggler dark-mode for .info elementerne\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;.info&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.toggle(&#039;dark-mode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Toggler dark-mode for &amp;lt;a&amp;gt; elementerne\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;a&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.toggle(&#039;dark-mode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;if (localStorage.getItem(&#039;darkMode&#039;) === &#039;true&#039;) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.body.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.getElementById(&#039;themeToggle&#039;).textContent = &#039;Light Mode&#039;;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Tilføjer dark-mode til .info elementerne ved sideindlæsning, hvis dark mode er aktivt\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;.info&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Tilføjer dark-mode til &amp;lt;a&amp;gt; elementerne ved sideindlæsning, hvis dark mode er aktivt\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;a&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  // Javascript funktion til at opdatere temperatur dynamisk.&lt;br /&gt;
  htmlResponse += &amp;quot;function fetchTemperature() {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  fetch(&#039;/temperature&#039;).then(response =&amp;gt; response.text()).then(data =&amp;gt; {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    document.getElementById(&#039;temperature&#039;).innerHTML = data;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;setInterval(fetchTemperature, &amp;quot; + String(updateInterval) + &amp;quot;);\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/script&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/body&amp;gt;\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  server.send(200, &amp;quot;text/html&amp;quot;, htmlResponse);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleTemperature() { // URL til at sende temperaturdata til JavaScript opdatering af handleRoot&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  if (tempC == -127.0) {&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, &amp;quot;&amp;lt;p class=&#039;error&#039;&amp;gt;Fejl: Ingen sensor fundet&amp;lt;/p&amp;gt;&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, &amp;quot;Aktuel temperatur: &amp;quot; + String(tempC) + &amp;quot; &amp;amp;deg;C&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleReset() { // Håndterer web kald til /reset (websiden som kan bruges til at nulstille systemet)&lt;br /&gt;
  String input = server.arg(&amp;quot;reset_input&amp;quot;);  // Få input fra formularen&lt;br /&gt;
  &lt;br /&gt;
  // Tjek om input er &amp;quot;reset&amp;quot;, uanset store/små bogstaver&lt;br /&gt;
  if (input.equalsIgnoreCase(&amp;quot;reset&amp;quot;)) {&lt;br /&gt;
    server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;System nulstilles og genstartes.&amp;quot;);  // Vist besked om nulstilling&lt;br /&gt;
    resetConfig();  // Nulstil konfiguration.&lt;br /&gt;
    delay(3000);    // Vent tre sekunder.&lt;br /&gt;
    ESP.restart();  // Genstart ESP32.&lt;br /&gt;
  } else {&lt;br /&gt;
    // Hvis input ikke er korrekt, send en fejlbesked&lt;br /&gt;
    String htmlResponse = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n&amp;lt;html lang=&#039;da&#039;&amp;gt;\n&amp;lt;head&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta http-equiv=&#039;refresh&#039; content=&#039;15&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;title&amp;gt;Hottub Temperatur&amp;lt;/title&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/head&amp;gt;\n&amp;lt;body style=&#039;color: red; text-align: center;&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;h1&amp;gt;Skriv &#039;reset&#039; i tekstboksen for, at nulstille systemet.&amp;lt;/h1&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;form method=&#039;POST&#039; action=&#039;/reset&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;input type=&#039;text&#039; name=&#039;reset_input&#039; placeholder=&#039;Skriv her&#039; required&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;input type=&#039;submit&#039; value=&#039;Nulstil&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/form&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/body&amp;gt;\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
    server.send(400, &amp;quot;text/html&amp;quot;, htmlResponse);  // Send fejlmeddelelse hvis input er forkert&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleConfig() { // Håndterer web kald til /config (webside hvor systemet kan konfigureres)&lt;br /&gt;
  // Hvis det er en POST-anmodning (brugeren indsender data)&lt;br /&gt;
  if (server.method() == HTTP_POST) {&lt;br /&gt;
    // Læs konfigurationsdata fra webformularen og gem dem i variablerne&lt;br /&gt;
    strncpy(ssid, server.arg(&amp;quot;ssid&amp;quot;).c_str(), sizeof(ssid));&lt;br /&gt;
    strncpy(password, server.arg(&amp;quot;password&amp;quot;).c_str(), sizeof(password));&lt;br /&gt;
    strncpy(mqtt_server, server.arg(&amp;quot;mqtt_server&amp;quot;).c_str(), sizeof(mqtt_server));&lt;br /&gt;
    mqtt_port = server.arg(&amp;quot;mqtt_port&amp;quot;).toInt();  // Konverterer MQTT-porten fra tekst til integer&lt;br /&gt;
    if (mqtt_port &amp;lt;= 0 || mqtt_port &amp;gt; 65535) {  // Validering af portnummer&lt;br /&gt;
      mqtt_port = 1883;  // Standard MQTT-port, hvis den er ugyldig&lt;br /&gt;
    }&lt;br /&gt;
    strncpy(mqtt_user, server.arg(&amp;quot;mqtt_user&amp;quot;).c_str(), sizeof(mqtt_user));&lt;br /&gt;
    strncpy(mqtt_password, server.arg(&amp;quot;mqtt_password&amp;quot;).c_str(), sizeof(mqtt_password));&lt;br /&gt;
    &lt;br /&gt;
    // Gem konfigurationen i EEPROM&lt;br /&gt;
    saveConfig();&lt;br /&gt;
    &lt;br /&gt;
    // Bekræftelse og information&lt;br /&gt;
    server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Konfiguration gemt. Genstart ESP32.&amp;quot;);&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    ESP.restart();  // Genstart for at anvende den nye konfiguration&lt;br /&gt;
  } else {&lt;br /&gt;
    // Returner HTML-formular for konfiguration&lt;br /&gt;
    String html = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html lang=&#039;da&#039;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;&amp;lt;title&amp;gt;Konfiguration&amp;lt;/title&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;style&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f4f4f9; color: #333; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;h1 { text-align: center; color: #4CAF50; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;.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); }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;text&#039;], input[type=&#039;password&#039;], input[type=&#039;number&#039;] { width: 100%; padding: 12px; margin: 8px 0; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;submit&#039;] { background-color: #4CAF50; color: white; padding: 14px 20px; border: none; border-radius: 4px; cursor: pointer; width: 100%; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;submit&#039;]:hover { background-color: #45a049; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;label { font-weight: bold; margin-top: 10px; display: block; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;.info { text-align: center; font-size: 14px; color: #777; margin-top: 20px; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;div class=&#039;container&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;h1&amp;gt;Konfiguration&amp;lt;/h1&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;form method=&#039;POST&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;ssid&#039;&amp;gt;SSID:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;ssid&#039; name=&#039;ssid&#039; type=&#039;text&#039; required&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;password&#039;&amp;gt;Password:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;password&#039; name=&#039;password&#039; type=&#039;password&#039; required&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_server&#039;&amp;gt;MQTT Server:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_server&#039; name=&#039;mqtt_server&#039; type=&#039;text&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_port&#039;&amp;gt;MQTT Port:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_port&#039; name=&#039;mqtt_port&#039; type=&#039;number&#039; value=&#039;1883&#039; min=&#039;1&#039; max=&#039;65535&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_user&#039;&amp;gt;MQTT User:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_user&#039; name=&#039;mqtt_user&#039; type=&#039;text&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_password&#039;&amp;gt;MQTT Password:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_password&#039; name=&#039;mqtt_password&#039; type=&#039;password&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;input type=&#039;submit&#039; value=&#039;Gem Konfiguration&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/form&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;div class=&#039;info&#039;&amp;gt;Undlad MQTT-information, hvis den ikke skal bruges.&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, html);  // Sender formularen som HTML&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void resetConfig() { // Nulstiller al konfigurations data&lt;br /&gt;
  memset(ssid, 0, sizeof(ssid));&lt;br /&gt;
  memset(password, 0, sizeof(password));&lt;br /&gt;
  memset(mqtt_server, 0, sizeof(mqtt_server));&lt;br /&gt;
  memset(mqtt_user, 0, sizeof(mqtt_user));&lt;br /&gt;
  memset(mqtt_password, 0, sizeof(mqtt_password));&lt;br /&gt;
  mqtt_port = 1883;  // Default port&lt;br /&gt;
&lt;br /&gt;
  // Initialiser EEPROM med den ønskede størrelse&lt;br /&gt;
  if (!EEPROM.begin(EEPROM_SIZE)) {&lt;br /&gt;
    Serial.println(&amp;quot;EEPROM initialization failed.&amp;quot;);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Rydder EEPROM ved at skrive 0xFF til hver byte (fjerner gamle data)&lt;br /&gt;
  for (int i = 0; i &amp;lt; EEPROM_SIZE; i++) {&lt;br /&gt;
    EEPROM.write(i, 0xFF);  // Write 0xFF to each byte&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  Serial.println(&amp;quot;Skriver til alle adresser.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  saveConfig();  // Funktion til at gemme den opdaterede (tomme) konfiguration til EEPROM&lt;br /&gt;
&lt;br /&gt;
  display.clearDisplay();  // Rydder skærmen&lt;br /&gt;
  display.setTextSize(2);   // Sætter skriftstørrelsen til 2&lt;br /&gt;
  display.println(&amp;quot;WiFi og MQTT&amp;quot;);&lt;br /&gt;
  display.println(&amp;quot; nulstillet.&amp;quot;);&lt;br /&gt;
  display.println(&amp;quot;Genstarter!&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;WiFi og MQTT nulstillet. Genstarter ESP32.&amp;quot;);&lt;br /&gt;
  delay(3000);&lt;br /&gt;
  ESP.restart();  // Restart the ESP32&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void saveConfig() { // Gemmer konfigurationen i EEPROM&lt;br /&gt;
  EEPROM.put(0, ssid);&lt;br /&gt;
  EEPROM.put(32, password);&lt;br /&gt;
  EEPROM.put(64, mqtt_server);&lt;br /&gt;
  EEPROM.put(128, mqtt_port);&lt;br /&gt;
  EEPROM.put(132, mqtt_user);&lt;br /&gt;
  EEPROM.put(164, mqtt_password);&lt;br /&gt;
  EEPROM.commit();  // Bekræft gemt data&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void readConfig() { // Læs konfigurationen fra EEPROM&lt;br /&gt;
  EEPROM.get(0, ssid);&lt;br /&gt;
  EEPROM.get(32, password);&lt;br /&gt;
  EEPROM.get(64, mqtt_server);&lt;br /&gt;
  EEPROM.get(128, mqtt_port);&lt;br /&gt;
  EEPROM.get(132, mqtt_user);&lt;br /&gt;
  EEPROM.get(164, mqtt_password);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Diverse links ==&lt;br /&gt;
* [https://samueladesola.medium.com/how-to-set-up-esp32-wroom-32-b2100060470c How to set up ESP32-WROOM-329] in Arduino IDE.&lt;br /&gt;
&lt;br /&gt;
== Afslutning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Arduino]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2082</id>
		<title>Temperaturføler med ESP32, DS18B20 og OLED-skærm</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2082"/>
		<updated>2026-05-07T14:43:11Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Kode til ESP32 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Indledning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Projektet inkluderer nem konfiguration af opkobling til Wi-Fi og der er mulighed for, at sende data til en MQTT-server.&lt;br /&gt;
&lt;br /&gt;
== Indkøbsliste ==&lt;br /&gt;
* [https://arduinotech.dk/shop/esp32-usb-c-iot-mainboard-devkitc-cp2102-38pin/ ESP32 USB-C IoT bundkort DevKitC CP2102 38PIN]&lt;br /&gt;
* [https://arduinotech.dk/shop/0-96%e2%80%b3-i2c-iic-spi-serial-128x64-oled-new-model/ 0,96&amp;quot; I2C 128x64 OLED-display]&lt;br /&gt;
* [https://arduinotech.dk/shop/ds18b20-waterproof-digital-thermal-probe-with-3-5mm-mini-audio-jack-1-meter-cable/ DS18B20 vandtæt digital termisk sonde (1 meter kabel)]&lt;br /&gt;
* [https://arduinotech.dk/shop/tsp-xx-replace-hlk-pmxx-ac-dc-220v-to-12v-buck-step-down-power-supply-module-converter/ HLK-PMxx 220VAC til 5VDC Step Down Converter]&lt;br /&gt;
* [https://arduinotech.dk/shop/sonoff-ip66-waterproof-case/ Sonoff IP66 vandtæt etui]&lt;br /&gt;
* [https://arduinotech.dk/shop/prototype-pcb-print/ Prototype PCB print - Type 1]&lt;br /&gt;
&lt;br /&gt;
== Byggevejledning ==&lt;br /&gt;
&lt;br /&gt;
1. Tilslutninger:  &lt;br /&gt;
   - DS18B20:  &lt;br /&gt;
     - VCC (rød)  → 3.3V på ESP32  &lt;br /&gt;
     - GND (sort) → GND på ESP32  &lt;br /&gt;
     - Data (gul) → GPIO 4 på ESP32  &lt;br /&gt;
     - Tilføj en 4,7kΩ pull-up modstand mellem Data og VCC.&lt;br /&gt;
&lt;br /&gt;
   - OLED-skærm (I2C):  &lt;br /&gt;
     - VCC → 3.3V på ESP32  &lt;br /&gt;
     - GND → GND  på ESP32  &lt;br /&gt;
     - SDA → GPIO 21 på ESP32  &lt;br /&gt;
     - SCL → GPIO 22 på ESP32  &lt;br /&gt;
&lt;br /&gt;
2. Strømforsyning:  &lt;br /&gt;
   Tilslut ESP32 og OLED til 5V strømforsyning via HLK-PMxx-modulet.&lt;br /&gt;
&lt;br /&gt;
3. Montering:  &lt;br /&gt;
   - Montér komponenterne på prototype-PCB.  &lt;br /&gt;
   - Placér hele opsætningen i Sonoff IP66 vandtæt etui for beskyttelse mod vejr og støv.&lt;br /&gt;
&lt;br /&gt;
== Kode til ESP32 ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed wikitable&amp;quot;&lt;br /&gt;
! Kopier denne kode til Arduino IDE:&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
/* README plz...&lt;br /&gt;
   Benyt &amp;quot;uPesy ESP32 Wroom DevKit&amp;quot; som board model til kompilering.&lt;br /&gt;
   Jeg har oplevet, at andre har fejl der får LED til at blinke for hurtigt !?!&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;WiFi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;WebServer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;OneWire.h&amp;gt;&lt;br /&gt;
#include &amp;lt;DallasTemperature.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_GFX.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_SSD1306.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;&lt;br /&gt;
#include &amp;lt;EEPROM.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// === PIN-konfigurationer ===&lt;br /&gt;
#define ONE_WIRE_BUS   4  // Pin til DS18B20 temperatursensoren.&lt;br /&gt;
#define SCREEN_WIDTH 128  // Skærmens bredde (for OLED).&lt;br /&gt;
#define SCREEN_HEIGHT 64  // Skærmens højde (for OLED).&lt;br /&gt;
#define OLED_RESET    -1  // Reset pin til OLED (sættes til -1, da vi ikke bruger en fysisk reset pin).&lt;br /&gt;
#define EEPROM_SIZE 4096  // Størrelsen af EEPROM (kan gemme op til 4096 byte).&lt;br /&gt;
#define RST_PIN       13  // Pin for RST-knappen.&lt;br /&gt;
#define LED_BUILTIN    2  // Pin for LED.&lt;br /&gt;
&lt;br /&gt;
// === Globale objekter ===&lt;br /&gt;
OneWire oneWire(ONE_WIRE_BUS);        // Initialiserer OneWire-bus til DS18B20.&lt;br /&gt;
DallasTemperature sensors(&amp;amp;oneWire);  // Opretter et objekt til at håndtere temperaturmålinger.&lt;br /&gt;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &amp;amp;Wire, OLED_RESET);  // OLED-skærm til at vise informationer.&lt;br /&gt;
WebServer server(80);                 // Webserver på port 80 til at håndtere HTTP-anmodninger.&lt;br /&gt;
WiFiClient espClient;                 // WiFi klient til MQTT.&lt;br /&gt;
PubSubClient mqttClient(espClient);   // MQTT-klient, der bruger WiFi klienten.&lt;br /&gt;
&lt;br /&gt;
// === Globale variabler ===&lt;br /&gt;
unsigned long lastCommsCheck = 0;          // Tidspunkt for sidste Comms-tjek.&lt;br /&gt;
unsigned long CommsCheckInterval = 60000;  // Tidsinterval for Comms-tjek (60 sekunder) angivet i millisekunder.&lt;br /&gt;
unsigned long lastLEDCheck = 0;            // Tidspunkt for sidste LED-tjek.&lt;br /&gt;
unsigned long LEDCheckInterval = 2000;     // Tidsinterval for LED-tjek (2 sekunder) angivet i millisekunder.&lt;br /&gt;
unsigned long lastMQTTpublish = 0;         // Tidspunkt for sidste MQTT-publish.&lt;br /&gt;
unsigned long MQTTPublishInterval = 15000; // Tidsinterval for MQTT publish (15 sekunder) angivet i millisekunder.&lt;br /&gt;
bool debugEnabled = false;                 // Skal der udskriver til seriel port?&lt;br /&gt;
int updateInterval = 15000;                // Variabel til 15 sekunders opdateringsinterval angivet i millisekunder.&lt;br /&gt;
&lt;br /&gt;
// === Netværksindstillinger ===&lt;br /&gt;
char ssid[32] = &amp;quot;&amp;quot;;      // SSID til WiFi netværket.&lt;br /&gt;
char password[32] = &amp;quot;&amp;quot;;  // WiFi adgangskode.&lt;br /&gt;
&lt;br /&gt;
// === MQTT-indstillinger ===&lt;br /&gt;
char mqtt_server[128] = &amp;quot;&amp;quot;;    // MQTT-serverens IP eller domæne.&lt;br /&gt;
int mqtt_port = 1883;          // MQTT-port (standard 1883).&lt;br /&gt;
char mqtt_user[128] = &amp;quot;&amp;quot;;      // MQTT-brugernavn.&lt;br /&gt;
char mqtt_password[128] = &amp;quot;&amp;quot;;  // MQTT-adgangskode.&lt;br /&gt;
char mqtt_clientID[128] = &amp;quot;&amp;quot;;  // MQTT-clientID.&lt;br /&gt;
&lt;br /&gt;
void setup() { // Initiel programmatiske konfigurations kode. &lt;br /&gt;
  pinMode(RST_PIN, INPUT);        // Initialiser RST_PIN som et INPUT.&lt;br /&gt;
  pinMode(LED_BUILTIN, OUTPUT);   // Initialiser den blå LED.&lt;br /&gt;
  pinMode(RST_PIN, INPUT_PULLUP); // Aktiver intern pull-up modstand.&lt;br /&gt;
  digitalWrite(LED_BUILTIN, LOW); // Sluk den blå LED.&lt;br /&gt;
  Serial.begin(115200);           // Initialiserer seriel kommunikation til debugging.&lt;br /&gt;
  EEPROM.begin(EEPROM_SIZE);      // Initialiserer EEPROM for at gemme konfigurationer.&lt;br /&gt;
  sensors.begin();                // Starter temperatursensoren.&lt;br /&gt;
  lastCommsCheck = millis();      // Sætter første tidsværdi af variablen.&lt;br /&gt;
  lastLEDCheck = millis();        // Sætter første tidsværdi af variablen.&lt;br /&gt;
  lastMQTTpublish = millis();     // Sætter første tidsværdi af variablen.&lt;br /&gt;
  &lt;br /&gt;
  // OLED-initialisering&lt;br /&gt;
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {  // Tjekker om OLED-skærmen er korrekt tilsluttet&lt;br /&gt;
    Serial.println(F(&amp;quot;OLED fejl!&amp;quot;));&lt;br /&gt;
    for (;;);  // Stopper programmet, hvis skærmen ikke kan initialiseres&lt;br /&gt;
  }&lt;br /&gt;
  display.clearDisplay();  // Rydder skærmen&lt;br /&gt;
  display.setTextSize(1);  // Sætter tekststørrelse&lt;br /&gt;
  display.setTextColor(WHITE);  // Sætter tekstfarve til hvid&lt;br /&gt;
&lt;br /&gt;
  // Tjek om RST-knappen er trykket under opstart&lt;br /&gt;
  if (digitalRead(RST_PIN) == LOW) {  // Hvis pin er LOW&lt;br /&gt;
    Serial.println(&amp;quot;RST-knappen trykket. Nulstiller WiFi og MQTT konfiguration.&amp;quot;);&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    resetConfig();  // Nulstil konfiguration&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readConfig();                   // Læs gemte konfigurationer fra EEPROM&lt;br /&gt;
  if (strlen(ssid) == 0 || strlen(password) == 0) { // Hvis ingen konfiguration er gemt, start konfigurationsmode&lt;br /&gt;
    displayMessage(&amp;quot;WiFi konfig mangler.\n\nStarter konfig WiFi.&amp;quot;);&lt;br /&gt;
    setupConfigServer();          // Starter konfigurationsserveren, så brugeren kan konfigurere WiFi og MQTT&lt;br /&gt;
    } else {&lt;br /&gt;
    // WiFi-forbindelse&lt;br /&gt;
    connectWiFi();                // Forbinder til WiFi, hvis SSID og adgangskode er gemt&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Webserver-ruter&lt;br /&gt;
  server.on(&amp;quot;/&amp;quot;, handleRoot);                   // Root URL, der viser temperaturdata&lt;br /&gt;
  server.on(&amp;quot;/temperature&amp;quot;, handleTemperature); // URL til at sende temperaturdata til JavaScript opdatering af handleRoot&lt;br /&gt;
  server.on(&amp;quot;/config&amp;quot;, handleConfig);           // URL til at konfigurere WiFi og MQTT&lt;br /&gt;
  server.on(&amp;quot;/reset&amp;quot;, handleReset);             // URL til at nulstille WiFi&lt;br /&gt;
  server.begin();  // Starter webserveren&lt;br /&gt;
&lt;br /&gt;
  if (strlen(mqtt_server) &amp;gt; 0) { // start kun MQTT, hvis &amp;quot;mqtt_server&amp;quot; er konfigureret og gemt.&lt;br /&gt;
    // Hent MAC-adressen&lt;br /&gt;
    String mac = WiFi.macAddress();                         // Eksempel: &amp;quot;24:6F:28:1A:2B:3C&amp;quot;&lt;br /&gt;
    Serial.println(mac);&lt;br /&gt;
    mac.replace(&amp;quot;:&amp;quot;,&amp;quot;&amp;quot;);                                    // fjern kolon&#039;er&lt;br /&gt;
    String lastSixChars = mac.substring(mac.length() - 6);  // Gem de sidste 6 tegn - Resultat: &amp;quot;2B:3C&amp;quot;&lt;br /&gt;
    String mqtt_clientID = &amp;quot;hottub-&amp;quot; + lastSixChars;        // Generer klient-id baseret på de sidste 6 bogstaver og tal wifi mac adresse&lt;br /&gt;
    mqttClient.setServer(mqtt_server, mqtt_port);&lt;br /&gt;
    if (!mqttClient.connected()) {                          // HVis MQTT ikke er forbundet,&lt;br /&gt;
      reconnectMQTT();                                      // Forsøg at oprette forbindelse til MQTT.&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_server   = &amp;quot;) + mqtt_server);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_port     = &amp;quot;) + mqtt_port);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt-klientID = &amp;quot;) + mqtt_clientID);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_user     = &amp;quot;) + mqtt_user);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_password = &amp;quot;) + mqtt_password);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot; - Debug beskeder kan aktiveres på seriel port med &#039;debug on&#039; eller &#039;debug off&#039;(standard).&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot; - Systemet kan nulstilles ved at sende &#039;nullify system&#039; på seriel porten.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() { // Her placeres koden der skal eksekveres kontinuerligt&lt;br /&gt;
  server.handleClient();                                   // Lad webserveren behandle indkommende HTTP-anmodninger.&lt;br /&gt;
  sensors.requestTemperatures();                           // Anmoder om temperaturmåling.&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);                // Hente temperatur fra den første sensor (index 0).&lt;br /&gt;
  updateOledScreen(tempC);                                 // Opdaterer OLED-skærmen.&lt;br /&gt;
  &lt;br /&gt;
  if (millis() - lastMQTTpublish &amp;gt;= MQTTPublishInterval) { // Send MQTT publish hvis det er tid til det (hver 15. sekunder)&lt;br /&gt;
    if (strlen(mqtt_server) &amp;gt; 0) {                         // Send kun MQTT, hvis mqtt_server er angivet&lt;br /&gt;
      if (mqttClient.connected()) {                        // Send temperaturdata til MQTT, hvis forbindelsen er aktiv&lt;br /&gt;
        sendTemperatureToMQTT(tempC);&lt;br /&gt;
      }&lt;br /&gt;
      lastMQTTpublish = millis();                          // Opdater lastMQTTpublish variablen til nu.&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if (millis() - lastCommsCheck &amp;gt;= CommsCheckInterval) {   // Tjek forbindelser hvis det er tid til det (hver 60. sekunder)&lt;br /&gt;
    if (WiFi.status() != WL_CONNECTED) {                   // Hvis WiFi ikke er forbundet,&lt;br /&gt;
      connectWiFi();                                       // Forsøg at oprette forbindelse til WiFi.&lt;br /&gt;
    }&lt;br /&gt;
    if (strlen(mqtt_server) &amp;gt; 0) {                         // Tjek kun MQTT, hvis mqtt_server er angivet&lt;br /&gt;
      if (!mqttClient.connected()) {                       // HVis MQTT ikke er forbundet,&lt;br /&gt;
        reconnectMQTT();                                   // Forsøg at oprette forbindelse til MQTT.&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    lastCommsCheck = millis();                             // Opdater lastCommsCheck variablen til nu.&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (millis() - lastLEDCheck &amp;gt;= LEDCheckInterval) {      // Blinke funktion til indikation af at loop() køre&lt;br /&gt;
&lt;br /&gt;
    // Serial.print(millis());&lt;br /&gt;
    // Serial.print(&amp;quot; - &amp;quot;);&lt;br /&gt;
    // Serial.print(lastLEDCheck);&lt;br /&gt;
    // Serial.print(&amp;quot; = &amp;quot;);&lt;br /&gt;
    // Serial.println(millis() - lastLEDCheck);&lt;br /&gt;
    // Serial.println(&amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // Læs den nuværende tilstand af LED_BUILTIN og sæt den modsatte værdi.&lt;br /&gt;
    lastLEDCheck = millis();                              // Opdater tidspunktet for LED status ændring&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (Serial.available() &amp;gt; 0) {                           // Håndter seriel kommando linje&lt;br /&gt;
    handleSerialInput();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleSerialInput() { // Håndter seriel kommando linje&lt;br /&gt;
  String input = Serial.readStringUntil(&#039;\n&#039;);  // Læs indtil linjeskift&lt;br /&gt;
  input.trim();  // Fjern eventuelle mellemrum eller linjeskift&lt;br /&gt;
&lt;br /&gt;
  if (input == &amp;quot;debug on&amp;quot;) {&lt;br /&gt;
    debugEnabled = true;&lt;br /&gt;
    Serial.println(&amp;quot;Debug mode aktiveret.&amp;quot;);&lt;br /&gt;
  } &lt;br /&gt;
  else if (input == &amp;quot;debug off&amp;quot;) {&lt;br /&gt;
    debugEnabled = false;&lt;br /&gt;
    Serial.println(&amp;quot;Debug mode deaktiveret.&amp;quot;);&lt;br /&gt;
  } &lt;br /&gt;
  else if (input == &amp;quot;nullify system&amp;quot;) {&lt;br /&gt;
    Serial.println(&amp;quot;Systemet nulstilles om 42 sekunder!&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;SLUK HVIS DET VAR EN FEJL-KOMMANDO!&amp;quot;);&lt;br /&gt;
    int seconds =42;&lt;br /&gt;
    while (seconds &amp;gt; 0) {&lt;br /&gt;
      Serial.print(&amp;quot;Tid tilbage: &amp;quot;);&lt;br /&gt;
      Serial.print(seconds);&lt;br /&gt;
      Serial.println(&amp;quot; sekunder&amp;quot;);&lt;br /&gt;
      delay(1000);  // Vent 1 sekund&lt;br /&gt;
      seconds--;&lt;br /&gt;
    }&lt;br /&gt;
    resetConfig();&lt;br /&gt;
  } &lt;br /&gt;
  else {&lt;br /&gt;
    Serial.println(&amp;quot;--&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;Ugyldig kommando!&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;--&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;&#039;debug on&#039; eller &#039;debug off&#039; tænder / slukker for debug beskeder.&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;&#039;nullify system&#039; nulstiller til fabriksinstillinger.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupConfigServer() { // Starter soft-AP (WiFi access point) til konfiguration af systemet&lt;br /&gt;
  String apSSID = &amp;quot;Hottub_Config&amp;quot;;  // SSID for AP&lt;br /&gt;
  &lt;br /&gt;
  // Generer en tilfældig kode mellem 100000000 og 999999999&lt;br /&gt;
  long randomCode = random(100000000, 1000000000);&lt;br /&gt;
  String apPassword = String(randomCode);  // Konverter tallet til en streng&lt;br /&gt;
  &lt;br /&gt;
  // Start softAP&lt;br /&gt;
  WiFi.softAP(apSSID.c_str(), apPassword.c_str());&lt;br /&gt;
  &lt;br /&gt;
  // Vent et øjeblik for at sikre, at access pointet er startet&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  &lt;br /&gt;
  // Print IP-adresse for AP&lt;br /&gt;
  Serial.println(&amp;quot;Soft-AP startet.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  server.on(&amp;quot;/config&amp;quot;, handleConfig);  // Håndterer konfigurationsanmodninger&lt;br /&gt;
  server.begin();  // Starter webserveren&lt;br /&gt;
  Serial.println(&amp;quot;#############################&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;Konfigurationsserver startet.&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;SSID: &amp;quot; + apSSID);&lt;br /&gt;
  Serial.println(&amp;quot;Kode: &amp;quot; + apPassword);&lt;br /&gt;
  Serial.println(&amp;quot;Tilgå http://&amp;quot;+ WiFi.softAPIP().toString() + &amp;quot;/config for at konfigurere.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  // Vist information om konfigurationsserveren på OLED-skærmen&lt;br /&gt;
  display.clearDisplay();   // Rydder skærmen før opdatering&lt;br /&gt;
  display.setCursor(0, 0);  // Sætter cursoren i øverste venstre hjørne&lt;br /&gt;
  display.print(&amp;quot;- Config WiFi online!&amp;quot;);&lt;br /&gt;
  display.setCursor(0, 10);&lt;br /&gt;
  display.print(&amp;quot;SSID: &amp;quot; + apSSID);&lt;br /&gt;
  display.setCursor(0, 19);&lt;br /&gt;
  display.print(&amp;quot;Kodeord:&amp;quot;);&lt;br /&gt;
  display.setTextSize(2);    // Sætter skriftstørrelsen til 2 (standard er 1)&lt;br /&gt;
  display.setCursor(4, 28);&lt;br /&gt;
  display.print(apPassword); // Vis den tilfældige kode&lt;br /&gt;
  display.setTextSize(1);    // Sætter skriftstørrelsen til 1 (standard er 1)&lt;br /&gt;
  display.setCursor(0, 45);&lt;br /&gt;
  display.print(&amp;quot; IP:  &amp;quot; + WiFi.softAPIP().toString()); // Vist IP-adressen på soft-AP mode&lt;br /&gt;
  display.setCursor(0, 55);&lt;br /&gt;
  display.print(&amp;quot;http://&amp;lt;IP&amp;gt;/config&amp;quot;);&lt;br /&gt;
  display.display();        // Vis information på OLED.&lt;br /&gt;
&lt;br /&gt;
  // Vi stopper her og venter på, at brugeren gemmer en gyldig konfiguration&lt;br /&gt;
  while (strlen(ssid) == 0 || strlen(password) == 0) {&lt;br /&gt;
    delay(1000);  // Fortsæt med at vente, mens vi holder serveren aktiv&lt;br /&gt;
    server.handleClient();  // Håndter klientanmodninger under ventetid&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  // Når en gyldig konfiguration er gemt, afslut setup og gå videre til WiFi-forbindelse&lt;br /&gt;
  displayMessage(&amp;quot;Konfiguration gemt. Genstarter ESP32.&amp;quot;);&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  ESP.restart();  // Genstart for at anvende den gemte konfiguration&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void connectWiFi() { // Tilslutter til det konfigurerede Wireless netværk&lt;br /&gt;
  if (strlen(ssid) == 0 || strlen(password) == 0) {&lt;br /&gt;
    displayMessage(&amp;quot;Ugyldig WiFi config&amp;quot;);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  displayMessage(&amp;quot;Forbinder til WiFi...&amp;quot;);&lt;br /&gt;
  WiFi.begin(ssid, password);&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED &amp;amp;&amp;amp; attempts &amp;lt; 10) {&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    attempts++;&lt;br /&gt;
    displayMessage(&amp;quot;Forbinder: Forsøg &amp;quot; + String(attempts));&lt;br /&gt;
    Serial.println(&amp;quot;Forbinder til WiFi: Forsøg &amp;quot; + String(attempts));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (WiFi.status() != WL_CONNECTED) {&lt;br /&gt;
    display.clearDisplay();  // Rydder skærmen før visning&lt;br /&gt;
    display.setCursor(0, 0);&lt;br /&gt;
    display.println(&amp;quot;WiFi fejlede&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // Vis det aktuelle SSID på linje 2&lt;br /&gt;
    display.setCursor(0, 16);&lt;br /&gt;
    display.print(&amp;quot;SSID: &amp;quot;);&lt;br /&gt;
    display.print(ssid);  // Vist det aktuelle SSID&lt;br /&gt;
    &lt;br /&gt;
    display.display();  // Opdaterer skærmen&lt;br /&gt;
    Serial.println(&amp;quot;Kunne ikke forbinde til WiFi ved opstart.&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    displayMessage(&amp;quot;WiFi forbundet!&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void reconnectMQTT() { // Forsøger at oprette forbindelse til MQTT-serveren&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (!mqttClient.connected() &amp;amp;&amp;amp; attempts &amp;lt; 3) {&lt;br /&gt;
    if (mqttClient.connect(mqtt_clientID, mqtt_user, mqtt_password)) {&lt;br /&gt;
      mqttClient.publish(&amp;quot;hottubsensor/temperature&amp;quot;, &amp;quot;Forbundet til MQTT&amp;quot;);  // Send en testmeddelelse&lt;br /&gt;
    } else {&lt;br /&gt;
      Serial.print(&amp;quot;FEJL, RC=&amp;quot;);&lt;br /&gt;
      // RE 5 er f.eks. MQTT_CONNECT_UNAUTHORIZED - se https://pubsubclient.knolleary.net/api for flere fejl beskeder.&lt;br /&gt;
      Serial.print(mqttClient.state());&lt;br /&gt;
      Serial.println(&amp;quot; venter 1420 msek.&amp;quot;);&lt;br /&gt;
      delay(1420);  // Vent lidt før et nyt forsøg&lt;br /&gt;
      attempts++;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  // Hvis MQTT-forbindelsen mislykkes efter flere forsøg&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    Serial.println(&amp;quot;Kunne ikke forbinde til MQTT efter 3 forsøg.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void sendTemperatureToMQTT(float temp) { // Sender aktuel temperatur til MQTT server&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    return;  // Send kun, hvis MQTT er forbundet&lt;br /&gt;
  }&lt;br /&gt;
  char tempStr[10];                 // Midlertidig buffer&lt;br /&gt;
  dtostrf(temp, 6, 2, tempStr);     // Konverterer float til string med 2 decimaler&lt;br /&gt;
  mqttClient.publish(&amp;quot;hottubsensor/temperature&amp;quot;, tempStr);&lt;br /&gt;
  debugEnabled ? (void)Serial.println(&amp;quot;MQTT publish: &amp;quot; + String(tempStr)) : (void)0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void updateOledScreen(float temp) { // Opdatrerer OLED skærm med information under normal drift&lt;br /&gt;
  display.clearDisplay();   // Rydder skærmen før opdatering&lt;br /&gt;
  display.setCursor(0, 0);  // Sætter cursoren i øverste venstre hjørne&lt;br /&gt;
  &lt;br /&gt;
  // Vist temperatur på den første linje&lt;br /&gt;
  display.setTextSize(3);   // Sætter skriftstørrelsen til 3 (standard er 1)&lt;br /&gt;
  // display.print(&amp;quot; &amp;quot;);       // lav et mellemrum&lt;br /&gt;
  display.print(temp);      // Vis temperatur&lt;br /&gt;
  //display.print(&amp;quot; &amp;quot;);       // lav et mellemrum&lt;br /&gt;
  display.write(247);       // 247 er ASCII-koden for &#039;°&#039; i Adafruit GFX&lt;br /&gt;
  display.print(&amp;quot;C&amp;quot;);&lt;br /&gt;
  display.setTextSize(1);   // Gå tilbage til normal skriftstørrelse for de næste linjer&lt;br /&gt;
  if (debugEnabled) {&lt;br /&gt;
    char tempStr[10];                 // Midlertidig buffer&lt;br /&gt;
    dtostrf(temp, 6, 2, tempStr);     // Konverterer float til string med 2 decimaler&lt;br /&gt;
    Serial.println(&amp;quot;OLED update: &amp;quot; + String(tempStr));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Vist IP-adresse på den næste linje&lt;br /&gt;
  display.setCursor(0, 28);  // Sætter cursoren på næste linje&lt;br /&gt;
  display.print(&amp;quot;IP:   &amp;quot;);&lt;br /&gt;
  display.print(WiFi.localIP());  // Vist IP-adresse&lt;br /&gt;
  debugEnabled ? (void)Serial.println(String(&amp;quot;OLED update: IP = &amp;quot;) + WiFi.localIP().toString()) : (void)0;&lt;br /&gt;
  &lt;br /&gt;
  // Vist WiFi status på næste linje&lt;br /&gt;
  display.setCursor(0, 41);  // Sætter cursoren på den næste linje&lt;br /&gt;
  display.print(&amp;quot;WiFi: &amp;quot;);&lt;br /&gt;
  if (WiFi.status() == WL_CONNECTED) {&lt;br /&gt;
    display.print(WiFi.SSID()); // Vis SSID for aktuel WiFi&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: SSID: &amp;quot; + WiFi.SSID()) : (void)0;&lt;br /&gt;
  } else {&lt;br /&gt;
    display.print(&amp;quot;Ikke forbundet&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: WiFi: Ikke forbundet&amp;quot;) : (void)0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Vis MQTT informationer på næste linje&lt;br /&gt;
  display.setCursor(0, 53);  // Sætter cursoren på næste linje&lt;br /&gt;
  display.print(&amp;quot;MQTT: &amp;quot;);&lt;br /&gt;
  // Vis kun MQTT status , hvis mqtt_server er angivet&lt;br /&gt;
  if (strlen(mqtt_server) &amp;gt; 0) {&lt;br /&gt;
    // Hvis MQTT er aktiveret, vis status, ellers &amp;quot;Ikke forbundet&amp;quot;&lt;br /&gt;
    display.print(mqttClient.connected() ? &amp;quot;Forbundet&amp;quot; : &amp;quot;Ikke forbundet&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: MQTT: &amp;quot; + mqttClient.connected() ? &amp;quot;Forbundet&amp;quot; : &amp;quot;Ikke forbundet&amp;quot;) : (void)0;&lt;br /&gt;
  } else {&lt;br /&gt;
    display.print(&amp;quot;Ikke i brug.&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: MQTT: Ikke i brug.&amp;quot;) : (void)0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  display.display();  // Opdaterer skærmen med de nye data&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayMessage(String message) { // Skriver tekst ca. midt på OLED skærmen&lt;br /&gt;
  display.clearDisplay();             // Rydder skærmen&lt;br /&gt;
  display.setCursor(0, 24);           // Sætter cursoren ca. midt på skærmen&lt;br /&gt;
  display.println(message);           // Vist besked på skærmen&lt;br /&gt;
  display.display();                  // Opdaterer skærmen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleRoot() { // Root URL, der viser temperaturdata&lt;br /&gt;
  sensors.requestTemperatures();  // Anmoder om temperaturmåling&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);  // Henter temperatur fra den første sensor (index 0)&lt;br /&gt;
  String response;&lt;br /&gt;
&lt;br /&gt;
  if (tempC == -127.0) {&lt;br /&gt;
    response = &amp;quot;&amp;lt;p id=&#039;temperature&#039; class=&#039;error&#039;&amp;gt;Fejl: Ingen sensor fundet&amp;lt;/p&amp;gt;\n&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    response = &amp;quot;&amp;lt;div class=&#039;meter-container&#039;&amp;gt;&amp;lt;meter id=&#039;temperatur&#039; value=&#039;&amp;quot; + String(tempC) + &amp;quot;&#039; min=&#039;0&#039; max=&#039;45&#039; low=&#039;5&#039; high=&#039;37&#039; optimum=&#039;40&#039;&amp;gt;&amp;lt;/meter&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
    response += &amp;quot;&amp;lt;p id=&#039;temperature&#039; class=&#039;temperature&#039;&amp;gt;Aktuel temperatur: &amp;quot; + String(tempC) + &amp;quot; &amp;amp;deg;C&amp;lt;/p&amp;gt;\n&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String ipAddress = WiFi.localIP().toString();&lt;br /&gt;
&lt;br /&gt;
  response += &amp;quot;&amp;lt;div class=&#039;info&#039;&amp;gt;\nGå til &amp;lt;a href=&#039;http://&amp;quot; + ipAddress + &amp;quot;/reset&#039;&amp;gt;&amp;quot; + ipAddress + &amp;quot;/reset&amp;lt;/a&amp;gt; for at factory resette.&amp;lt;br /&amp;gt;\n&amp;quot;;&lt;br /&gt;
  response += &amp;quot;Gå til &amp;lt;a href=&#039;http://&amp;quot; + ipAddress + &amp;quot;/config&#039;&amp;gt;&amp;quot; + ipAddress + &amp;quot;/config&amp;lt;/a&amp;gt; for at ændre konfig.\n&amp;lt;/div&amp;gt;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  // Tilføjer HTML, CSS og JavaScript for dark mode og dynamisk temperatur-opdatering&lt;br /&gt;
  String htmlResponse = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n&amp;lt;html lang=&#039;da&#039;&amp;gt;\n&amp;lt;head&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;title&amp;gt;Hottub Temperatur&amp;lt;/title&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;style&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;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&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;h1 { color: green; font-size: 2rem; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.meter-container { margin-top: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;meter { width: 80%; max-width: 400px; height: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.temperature, .error { font-size: 1.2rem; margin-top: 10px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.error { color: red; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.info { color: silver; font-size: 1rem; margin-top: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.info.dark-mode { color: dimgray; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;a, a:visited { color: silver; text-decoration: underline; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;a.dark-mode, a.dark-mode:visited { color: dimgray; text-decoration: underline; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;button { padding: 10px 20px; margin-top: 20px; cursor: pointer; background-color: green; color: white; border: none; border-radius: 5px; font-size: 1rem; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;button:hover { background-color: darkgreen; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;body.dark-mode { background-color: black; color: green; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;body.dark-mode a { color: dimgray; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/style&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/head&amp;gt;\n&amp;lt;body&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;h1&amp;gt;Temperaturmåling&amp;lt;/h1&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += response;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;button id=&#039;themeToggle&#039; onclick=&#039;toggleDarkMode()&#039;&amp;gt;Dark Mode&amp;lt;/button&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;script&amp;gt;\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  // Dark mode javascript funktion.&lt;br /&gt;
  htmlResponse += &amp;quot;function toggleDarkMode() {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.body.classList.toggle(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  const isDarkMode = document.body.classList.contains(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  localStorage.setItem(&#039;darkMode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.getElementById(&#039;themeToggle&#039;).textContent = isDarkMode ? &#039;Light Mode&#039; : &#039;Dark Mode&#039;;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Toggler dark-mode for .info elementerne\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;.info&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.toggle(&#039;dark-mode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Toggler dark-mode for &amp;lt;a&amp;gt; elementerne\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;a&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.toggle(&#039;dark-mode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;if (localStorage.getItem(&#039;darkMode&#039;) === &#039;true&#039;) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.body.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.getElementById(&#039;themeToggle&#039;).textContent = &#039;Light Mode&#039;;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Tilføjer dark-mode til .info elementerne ved sideindlæsning, hvis dark mode er aktivt\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;.info&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Tilføjer dark-mode til &amp;lt;a&amp;gt; elementerne ved sideindlæsning, hvis dark mode er aktivt\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;a&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  // Javascript funktion til at opdatere temperatur dynamisk.&lt;br /&gt;
  htmlResponse += &amp;quot;function fetchTemperature() {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  fetch(&#039;/temperature&#039;).then(response =&amp;gt; response.text()).then(data =&amp;gt; {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    document.getElementById(&#039;temperature&#039;).innerHTML = data;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;setInterval(fetchTemperature, &amp;quot; + String(updateInterval) + &amp;quot;);\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/script&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/body&amp;gt;\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  server.send(200, &amp;quot;text/html&amp;quot;, htmlResponse);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleTemperature() { // URL til at sende temperaturdata til JavaScript opdatering af handleRoot&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  if (tempC == -127.0) {&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, &amp;quot;&amp;lt;p class=&#039;error&#039;&amp;gt;Fejl: Ingen sensor fundet&amp;lt;/p&amp;gt;&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, &amp;quot;Aktuel temperatur: &amp;quot; + String(tempC) + &amp;quot; &amp;amp;deg;C&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleReset() { // Håndterer web kald til /reset (websiden som kan bruges til at nulstille systemet)&lt;br /&gt;
  String input = server.arg(&amp;quot;reset_input&amp;quot;);  // Få input fra formularen&lt;br /&gt;
  &lt;br /&gt;
  // Tjek om input er &amp;quot;reset&amp;quot;, uanset store/små bogstaver&lt;br /&gt;
  if (input.equalsIgnoreCase(&amp;quot;reset&amp;quot;)) {&lt;br /&gt;
    server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;System nulstilles og genstartes.&amp;quot;);  // Vist besked om nulstilling&lt;br /&gt;
    resetConfig();  // Nulstil konfiguration.&lt;br /&gt;
    delay(3000);    // Vent tre sekunder.&lt;br /&gt;
    ESP.restart();  // Genstart ESP32.&lt;br /&gt;
  } else {&lt;br /&gt;
    // Hvis input ikke er korrekt, send en fejlbesked&lt;br /&gt;
    String htmlResponse = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n&amp;lt;html lang=&#039;da&#039;&amp;gt;\n&amp;lt;head&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta http-equiv=&#039;refresh&#039; content=&#039;15&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;title&amp;gt;Hottub Temperatur&amp;lt;/title&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/head&amp;gt;\n&amp;lt;body style=&#039;color: red; text-align: center;&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;h1&amp;gt;Skriv &#039;reset&#039; i tekstboksen for, at nulstille systemet.&amp;lt;/h1&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;form method=&#039;POST&#039; action=&#039;/reset&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;input type=&#039;text&#039; name=&#039;reset_input&#039; placeholder=&#039;Skriv her&#039; required&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;input type=&#039;submit&#039; value=&#039;Nulstil&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/form&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/body&amp;gt;\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
    server.send(400, &amp;quot;text/html&amp;quot;, htmlResponse);  // Send fejlmeddelelse hvis input er forkert&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleConfig() { // Håndterer web kald til /config (webside hvor systemet kan konfigureres)&lt;br /&gt;
  // Hvis det er en POST-anmodning (brugeren indsender data)&lt;br /&gt;
  if (server.method() == HTTP_POST) {&lt;br /&gt;
    // Læs konfigurationsdata fra webformularen og gem dem i variablerne&lt;br /&gt;
    strncpy(ssid, server.arg(&amp;quot;ssid&amp;quot;).c_str(), sizeof(ssid));&lt;br /&gt;
    strncpy(password, server.arg(&amp;quot;password&amp;quot;).c_str(), sizeof(password));&lt;br /&gt;
    strncpy(mqtt_server, server.arg(&amp;quot;mqtt_server&amp;quot;).c_str(), sizeof(mqtt_server));&lt;br /&gt;
    mqtt_port = server.arg(&amp;quot;mqtt_port&amp;quot;).toInt();  // Konverterer MQTT-porten fra tekst til integer&lt;br /&gt;
    if (mqtt_port &amp;lt;= 0 || mqtt_port &amp;gt; 65535) {  // Validering af portnummer&lt;br /&gt;
      mqtt_port = 1883;  // Standard MQTT-port, hvis den er ugyldig&lt;br /&gt;
    }&lt;br /&gt;
    strncpy(mqtt_user, server.arg(&amp;quot;mqtt_user&amp;quot;).c_str(), sizeof(mqtt_user));&lt;br /&gt;
    strncpy(mqtt_password, server.arg(&amp;quot;mqtt_password&amp;quot;).c_str(), sizeof(mqtt_password));&lt;br /&gt;
    &lt;br /&gt;
    // Gem konfigurationen i EEPROM&lt;br /&gt;
    saveConfig();&lt;br /&gt;
    &lt;br /&gt;
    // Bekræftelse og information&lt;br /&gt;
    server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Konfiguration gemt. Genstart ESP32.&amp;quot;);&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    ESP.restart();  // Genstart for at anvende den nye konfiguration&lt;br /&gt;
  } else {&lt;br /&gt;
    // Returner HTML-formular for konfiguration&lt;br /&gt;
    String html = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html lang=&#039;da&#039;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;&amp;lt;title&amp;gt;Konfiguration&amp;lt;/title&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;style&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f4f4f9; color: #333; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;h1 { text-align: center; color: #4CAF50; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;.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); }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;text&#039;], input[type=&#039;password&#039;], input[type=&#039;number&#039;] { width: 100%; padding: 12px; margin: 8px 0; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;submit&#039;] { background-color: #4CAF50; color: white; padding: 14px 20px; border: none; border-radius: 4px; cursor: pointer; width: 100%; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;submit&#039;]:hover { background-color: #45a049; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;label { font-weight: bold; margin-top: 10px; display: block; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;.info { text-align: center; font-size: 14px; color: #777; margin-top: 20px; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;div class=&#039;container&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;h1&amp;gt;Konfiguration&amp;lt;/h1&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;form method=&#039;POST&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;ssid&#039;&amp;gt;SSID:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;ssid&#039; name=&#039;ssid&#039; type=&#039;text&#039; required&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;password&#039;&amp;gt;Password:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;password&#039; name=&#039;password&#039; type=&#039;password&#039; required&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_server&#039;&amp;gt;MQTT Server:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_server&#039; name=&#039;mqtt_server&#039; type=&#039;text&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_port&#039;&amp;gt;MQTT Port:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_port&#039; name=&#039;mqtt_port&#039; type=&#039;number&#039; value=&#039;1883&#039; min=&#039;1&#039; max=&#039;65535&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_user&#039;&amp;gt;MQTT User:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_user&#039; name=&#039;mqtt_user&#039; type=&#039;text&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_password&#039;&amp;gt;MQTT Password:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_password&#039; name=&#039;mqtt_password&#039; type=&#039;password&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;input type=&#039;submit&#039; value=&#039;Gem Konfiguration&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/form&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;div class=&#039;info&#039;&amp;gt;Undlad MQTT-information, hvis den ikke skal bruges.&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, html);  // Sender formularen som HTML&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void resetConfig() { // Nulstiller al konfigurations data&lt;br /&gt;
  memset(ssid, 0, sizeof(ssid));&lt;br /&gt;
  memset(password, 0, sizeof(password));&lt;br /&gt;
  memset(mqtt_server, 0, sizeof(mqtt_server));&lt;br /&gt;
  memset(mqtt_user, 0, sizeof(mqtt_user));&lt;br /&gt;
  memset(mqtt_password, 0, sizeof(mqtt_password));&lt;br /&gt;
  mqtt_port = 1883;  // Default port&lt;br /&gt;
&lt;br /&gt;
  // Initialiser EEPROM med den ønskede størrelse&lt;br /&gt;
  if (!EEPROM.begin(EEPROM_SIZE)) {&lt;br /&gt;
    Serial.println(&amp;quot;EEPROM initialization failed.&amp;quot;);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Rydder EEPROM ved at skrive 0xFF til hver byte (fjerner gamle data)&lt;br /&gt;
  for (int i = 0; i &amp;lt; EEPROM_SIZE; i++) {&lt;br /&gt;
    EEPROM.write(i, 0xFF);  // Write 0xFF to each byte&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  Serial.println(&amp;quot;Skriver til alle adresser.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  saveConfig();  // Funktion til at gemme den opdaterede (tomme) konfiguration til EEPROM&lt;br /&gt;
&lt;br /&gt;
  display.clearDisplay();  // Rydder skærmen&lt;br /&gt;
  display.setTextSize(2);   // Sætter skriftstørrelsen til 2&lt;br /&gt;
  display.println(&amp;quot;WiFi og MQTT&amp;quot;);&lt;br /&gt;
  display.println(&amp;quot; nulstillet.&amp;quot;);&lt;br /&gt;
  display.println(&amp;quot;Genstarter!&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;WiFi og MQTT nulstillet. Genstarter ESP32.&amp;quot;);&lt;br /&gt;
  delay(3000);&lt;br /&gt;
  ESP.restart();  // Restart the ESP32&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void saveConfig() { // Gemmer konfigurationen i EEPROM&lt;br /&gt;
  EEPROM.put(0, ssid);&lt;br /&gt;
  EEPROM.put(32, password);&lt;br /&gt;
  EEPROM.put(64, mqtt_server);&lt;br /&gt;
  EEPROM.put(128, mqtt_port);&lt;br /&gt;
  EEPROM.put(132, mqtt_user);&lt;br /&gt;
  EEPROM.put(164, mqtt_password);&lt;br /&gt;
  EEPROM.commit();  // Bekræft gemt data&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void readConfig() { // Læs konfigurationen fra EEPROM&lt;br /&gt;
  EEPROM.get(0, ssid);&lt;br /&gt;
  EEPROM.get(32, password);&lt;br /&gt;
  EEPROM.get(64, mqtt_server);&lt;br /&gt;
  EEPROM.get(128, mqtt_port);&lt;br /&gt;
  EEPROM.get(132, mqtt_user);&lt;br /&gt;
  EEPROM.get(164, mqtt_password);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diverse links ==&lt;br /&gt;
* [https://samueladesola.medium.com/how-to-set-up-esp32-wroom-32-b2100060470c How to set up ESP32-WROOM-329] in Arduino IDE.&lt;br /&gt;
&lt;br /&gt;
== Afslutning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Arduino]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2081</id>
		<title>Temperaturføler med ESP32, DS18B20 og OLED-skærm</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2081"/>
		<updated>2026-05-07T14:33:37Z</updated>

		<summary type="html">&lt;p&gt;Admin: roll back til mw-collapsible er med&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Indledning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Projektet inkluderer nem konfiguration af opkobling til Wi-Fi og der er mulighed for, at sende data til en MQTT-server.&lt;br /&gt;
&lt;br /&gt;
== Indkøbsliste ==&lt;br /&gt;
* [https://arduinotech.dk/shop/esp32-usb-c-iot-mainboard-devkitc-cp2102-38pin/ ESP32 USB-C IoT bundkort DevKitC CP2102 38PIN]&lt;br /&gt;
* [https://arduinotech.dk/shop/0-96%e2%80%b3-i2c-iic-spi-serial-128x64-oled-new-model/ 0,96&amp;quot; I2C 128x64 OLED-display]&lt;br /&gt;
* [https://arduinotech.dk/shop/ds18b20-waterproof-digital-thermal-probe-with-3-5mm-mini-audio-jack-1-meter-cable/ DS18B20 vandtæt digital termisk sonde (1 meter kabel)]&lt;br /&gt;
* [https://arduinotech.dk/shop/tsp-xx-replace-hlk-pmxx-ac-dc-220v-to-12v-buck-step-down-power-supply-module-converter/ HLK-PMxx 220VAC til 5VDC Step Down Converter]&lt;br /&gt;
* [https://arduinotech.dk/shop/sonoff-ip66-waterproof-case/ Sonoff IP66 vandtæt etui]&lt;br /&gt;
* [https://arduinotech.dk/shop/prototype-pcb-print/ Prototype PCB print - Type 1]&lt;br /&gt;
&lt;br /&gt;
== Byggevejledning ==&lt;br /&gt;
&lt;br /&gt;
1. Tilslutninger:  &lt;br /&gt;
   - DS18B20:  &lt;br /&gt;
     - VCC (rød)  → 3.3V på ESP32  &lt;br /&gt;
     - GND (sort) → GND på ESP32  &lt;br /&gt;
     - Data (gul) → GPIO 4 på ESP32  &lt;br /&gt;
     - Tilføj en 4,7kΩ pull-up modstand mellem Data og VCC.&lt;br /&gt;
&lt;br /&gt;
   - OLED-skærm (I2C):  &lt;br /&gt;
     - VCC → 3.3V på ESP32  &lt;br /&gt;
     - GND → GND  på ESP32  &lt;br /&gt;
     - SDA → GPIO 21 på ESP32  &lt;br /&gt;
     - SCL → GPIO 22 på ESP32  &lt;br /&gt;
&lt;br /&gt;
2. Strømforsyning:  &lt;br /&gt;
   Tilslut ESP32 og OLED til 5V strømforsyning via HLK-PMxx-modulet.&lt;br /&gt;
&lt;br /&gt;
3. Montering:  &lt;br /&gt;
   - Montér komponenterne på prototype-PCB.  &lt;br /&gt;
   - Placér hele opsætningen i Sonoff IP66 vandtæt etui for beskyttelse mod vejr og støv.&lt;br /&gt;
&lt;br /&gt;
== Kode til ESP32 ==&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed wikitable&amp;quot;&lt;br /&gt;
! Kopier denne kode til Arduino IDE --&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
/* README plz...&lt;br /&gt;
   Benyt &amp;quot;uPesy ESP32 Wroom DevKit&amp;quot; som board model til kompilering.&lt;br /&gt;
   Jeg har oplevet, at andre har fejl der får LED til at blinke for hurtigt !?!&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;WiFi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;WebServer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;OneWire.h&amp;gt;&lt;br /&gt;
#include &amp;lt;DallasTemperature.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_GFX.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_SSD1306.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;&lt;br /&gt;
#include &amp;lt;EEPROM.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// === PIN-konfigurationer ===&lt;br /&gt;
#define ONE_WIRE_BUS   4  // Pin til DS18B20 temperatursensoren.&lt;br /&gt;
#define SCREEN_WIDTH 128  // Skærmens bredde (for OLED).&lt;br /&gt;
#define SCREEN_HEIGHT 64  // Skærmens højde (for OLED).&lt;br /&gt;
#define OLED_RESET    -1  // Reset pin til OLED (sættes til -1, da vi ikke bruger en fysisk reset pin).&lt;br /&gt;
#define EEPROM_SIZE 4096  // Størrelsen af EEPROM (kan gemme op til 4096 byte).&lt;br /&gt;
#define RST_PIN       13  // Pin for RST-knappen.&lt;br /&gt;
#define LED_BUILTIN    2  // Pin for LED.&lt;br /&gt;
&lt;br /&gt;
// === Globale objekter ===&lt;br /&gt;
OneWire oneWire(ONE_WIRE_BUS);        // Initialiserer OneWire-bus til DS18B20.&lt;br /&gt;
DallasTemperature sensors(&amp;amp;oneWire);  // Opretter et objekt til at håndtere temperaturmålinger.&lt;br /&gt;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &amp;amp;Wire, OLED_RESET);  // OLED-skærm til at vise informationer.&lt;br /&gt;
WebServer server(80);                 // Webserver på port 80 til at håndtere HTTP-anmodninger.&lt;br /&gt;
WiFiClient espClient;                 // WiFi klient til MQTT.&lt;br /&gt;
PubSubClient mqttClient(espClient);   // MQTT-klient, der bruger WiFi klienten.&lt;br /&gt;
&lt;br /&gt;
// === Globale variabler ===&lt;br /&gt;
unsigned long lastCommsCheck = 0;          // Tidspunkt for sidste Comms-tjek.&lt;br /&gt;
unsigned long CommsCheckInterval = 60000;  // Tidsinterval for Comms-tjek (60 sekunder) angivet i millisekunder.&lt;br /&gt;
unsigned long lastLEDCheck = 0;            // Tidspunkt for sidste LED-tjek.&lt;br /&gt;
unsigned long LEDCheckInterval = 2000;     // Tidsinterval for LED-tjek (2 sekunder) angivet i millisekunder.&lt;br /&gt;
unsigned long lastMQTTpublish = 0;         // Tidspunkt for sidste MQTT-publish.&lt;br /&gt;
unsigned long MQTTPublishInterval = 15000; // Tidsinterval for MQTT publish (15 sekunder) angivet i millisekunder.&lt;br /&gt;
bool debugEnabled = false;                 // Skal der udskriver til seriel port?&lt;br /&gt;
int updateInterval = 15000;                // Variabel til 15 sekunders opdateringsinterval angivet i millisekunder.&lt;br /&gt;
&lt;br /&gt;
// === Netværksindstillinger ===&lt;br /&gt;
char ssid[32] = &amp;quot;&amp;quot;;      // SSID til WiFi netværket.&lt;br /&gt;
char password[32] = &amp;quot;&amp;quot;;  // WiFi adgangskode.&lt;br /&gt;
&lt;br /&gt;
// === MQTT-indstillinger ===&lt;br /&gt;
char mqtt_server[128] = &amp;quot;&amp;quot;;    // MQTT-serverens IP eller domæne.&lt;br /&gt;
int mqtt_port = 1883;          // MQTT-port (standard 1883).&lt;br /&gt;
char mqtt_user[128] = &amp;quot;&amp;quot;;      // MQTT-brugernavn.&lt;br /&gt;
char mqtt_password[128] = &amp;quot;&amp;quot;;  // MQTT-adgangskode.&lt;br /&gt;
char mqtt_clientID[128] = &amp;quot;&amp;quot;;  // MQTT-clientID.&lt;br /&gt;
&lt;br /&gt;
void setup() { // Initiel programmatiske konfigurations kode. &lt;br /&gt;
  pinMode(RST_PIN, INPUT);        // Initialiser RST_PIN som et INPUT.&lt;br /&gt;
  pinMode(LED_BUILTIN, OUTPUT);   // Initialiser den blå LED.&lt;br /&gt;
  pinMode(RST_PIN, INPUT_PULLUP); // Aktiver intern pull-up modstand.&lt;br /&gt;
  digitalWrite(LED_BUILTIN, LOW); // Sluk den blå LED.&lt;br /&gt;
  Serial.begin(115200);           // Initialiserer seriel kommunikation til debugging.&lt;br /&gt;
  EEPROM.begin(EEPROM_SIZE);      // Initialiserer EEPROM for at gemme konfigurationer.&lt;br /&gt;
  sensors.begin();                // Starter temperatursensoren.&lt;br /&gt;
  lastCommsCheck = millis();      // Sætter første tidsværdi af variablen.&lt;br /&gt;
  lastLEDCheck = millis();        // Sætter første tidsværdi af variablen.&lt;br /&gt;
  lastMQTTpublish = millis();     // Sætter første tidsværdi af variablen.&lt;br /&gt;
  &lt;br /&gt;
  // OLED-initialisering&lt;br /&gt;
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {  // Tjekker om OLED-skærmen er korrekt tilsluttet&lt;br /&gt;
    Serial.println(F(&amp;quot;OLED fejl!&amp;quot;));&lt;br /&gt;
    for (;;);  // Stopper programmet, hvis skærmen ikke kan initialiseres&lt;br /&gt;
  }&lt;br /&gt;
  display.clearDisplay();  // Rydder skærmen&lt;br /&gt;
  display.setTextSize(1);  // Sætter tekststørrelse&lt;br /&gt;
  display.setTextColor(WHITE);  // Sætter tekstfarve til hvid&lt;br /&gt;
&lt;br /&gt;
  // Tjek om RST-knappen er trykket under opstart&lt;br /&gt;
  if (digitalRead(RST_PIN) == LOW) {  // Hvis pin er LOW&lt;br /&gt;
    Serial.println(&amp;quot;RST-knappen trykket. Nulstiller WiFi og MQTT konfiguration.&amp;quot;);&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    resetConfig();  // Nulstil konfiguration&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readConfig();                   // Læs gemte konfigurationer fra EEPROM&lt;br /&gt;
  if (strlen(ssid) == 0 || strlen(password) == 0) { // Hvis ingen konfiguration er gemt, start konfigurationsmode&lt;br /&gt;
    displayMessage(&amp;quot;WiFi konfig mangler.\n\nStarter konfig WiFi.&amp;quot;);&lt;br /&gt;
    setupConfigServer();          // Starter konfigurationsserveren, så brugeren kan konfigurere WiFi og MQTT&lt;br /&gt;
    } else {&lt;br /&gt;
    // WiFi-forbindelse&lt;br /&gt;
    connectWiFi();                // Forbinder til WiFi, hvis SSID og adgangskode er gemt&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Webserver-ruter&lt;br /&gt;
  server.on(&amp;quot;/&amp;quot;, handleRoot);                   // Root URL, der viser temperaturdata&lt;br /&gt;
  server.on(&amp;quot;/temperature&amp;quot;, handleTemperature); // URL til at sende temperaturdata til JavaScript opdatering af handleRoot&lt;br /&gt;
  server.on(&amp;quot;/config&amp;quot;, handleConfig);           // URL til at konfigurere WiFi og MQTT&lt;br /&gt;
  server.on(&amp;quot;/reset&amp;quot;, handleReset);             // URL til at nulstille WiFi&lt;br /&gt;
  server.begin();  // Starter webserveren&lt;br /&gt;
&lt;br /&gt;
  if (strlen(mqtt_server) &amp;gt; 0) { // start kun MQTT, hvis &amp;quot;mqtt_server&amp;quot; er konfigureret og gemt.&lt;br /&gt;
    // Hent MAC-adressen&lt;br /&gt;
    String mac = WiFi.macAddress();                         // Eksempel: &amp;quot;24:6F:28:1A:2B:3C&amp;quot;&lt;br /&gt;
    Serial.println(mac);&lt;br /&gt;
    mac.replace(&amp;quot;:&amp;quot;,&amp;quot;&amp;quot;);                                    // fjern kolon&#039;er&lt;br /&gt;
    String lastSixChars = mac.substring(mac.length() - 6);  // Gem de sidste 6 tegn - Resultat: &amp;quot;2B:3C&amp;quot;&lt;br /&gt;
    String mqtt_clientID = &amp;quot;hottub-&amp;quot; + lastSixChars;        // Generer klient-id baseret på de sidste 6 bogstaver og tal wifi mac adresse&lt;br /&gt;
    mqttClient.setServer(mqtt_server, mqtt_port);&lt;br /&gt;
    if (!mqttClient.connected()) {                          // HVis MQTT ikke er forbundet,&lt;br /&gt;
      reconnectMQTT();                                      // Forsøg at oprette forbindelse til MQTT.&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_server   = &amp;quot;) + mqtt_server);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_port     = &amp;quot;) + mqtt_port);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt-klientID = &amp;quot;) + mqtt_clientID);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_user     = &amp;quot;) + mqtt_user);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_password = &amp;quot;) + mqtt_password);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot; - Debug beskeder kan aktiveres på seriel port med &#039;debug on&#039; eller &#039;debug off&#039;(standard).&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot; - Systemet kan nulstilles ved at sende &#039;nullify system&#039; på seriel porten.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() { // Her placeres koden der skal eksekveres kontinuerligt&lt;br /&gt;
  server.handleClient();                                   // Lad webserveren behandle indkommende HTTP-anmodninger.&lt;br /&gt;
  sensors.requestTemperatures();                           // Anmoder om temperaturmåling.&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);                // Hente temperatur fra den første sensor (index 0).&lt;br /&gt;
  updateOledScreen(tempC);                                 // Opdaterer OLED-skærmen.&lt;br /&gt;
  &lt;br /&gt;
  if (millis() - lastMQTTpublish &amp;gt;= MQTTPublishInterval) { // Send MQTT publish hvis det er tid til det (hver 15. sekunder)&lt;br /&gt;
    if (strlen(mqtt_server) &amp;gt; 0) {                         // Send kun MQTT, hvis mqtt_server er angivet&lt;br /&gt;
      if (mqttClient.connected()) {                        // Send temperaturdata til MQTT, hvis forbindelsen er aktiv&lt;br /&gt;
        sendTemperatureToMQTT(tempC);&lt;br /&gt;
      }&lt;br /&gt;
      lastMQTTpublish = millis();                          // Opdater lastMQTTpublish variablen til nu.&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if (millis() - lastCommsCheck &amp;gt;= CommsCheckInterval) {   // Tjek forbindelser hvis det er tid til det (hver 60. sekunder)&lt;br /&gt;
    if (WiFi.status() != WL_CONNECTED) {                   // Hvis WiFi ikke er forbundet,&lt;br /&gt;
      connectWiFi();                                       // Forsøg at oprette forbindelse til WiFi.&lt;br /&gt;
    }&lt;br /&gt;
    if (strlen(mqtt_server) &amp;gt; 0) {                         // Tjek kun MQTT, hvis mqtt_server er angivet&lt;br /&gt;
      if (!mqttClient.connected()) {                       // HVis MQTT ikke er forbundet,&lt;br /&gt;
        reconnectMQTT();                                   // Forsøg at oprette forbindelse til MQTT.&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    lastCommsCheck = millis();                             // Opdater lastCommsCheck variablen til nu.&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (millis() - lastLEDCheck &amp;gt;= LEDCheckInterval) {      // Blinke funktion til indikation af at loop() køre&lt;br /&gt;
&lt;br /&gt;
    // Serial.print(millis());&lt;br /&gt;
    // Serial.print(&amp;quot; - &amp;quot;);&lt;br /&gt;
    // Serial.print(lastLEDCheck);&lt;br /&gt;
    // Serial.print(&amp;quot; = &amp;quot;);&lt;br /&gt;
    // Serial.println(millis() - lastLEDCheck);&lt;br /&gt;
    // Serial.println(&amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // Læs den nuværende tilstand af LED_BUILTIN og sæt den modsatte værdi.&lt;br /&gt;
    lastLEDCheck = millis();                              // Opdater tidspunktet for LED status ændring&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (Serial.available() &amp;gt; 0) {                           // Håndter seriel kommando linje&lt;br /&gt;
    handleSerialInput();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleSerialInput() { // Håndter seriel kommando linje&lt;br /&gt;
  String input = Serial.readStringUntil(&#039;\n&#039;);  // Læs indtil linjeskift&lt;br /&gt;
  input.trim();  // Fjern eventuelle mellemrum eller linjeskift&lt;br /&gt;
&lt;br /&gt;
  if (input == &amp;quot;debug on&amp;quot;) {&lt;br /&gt;
    debugEnabled = true;&lt;br /&gt;
    Serial.println(&amp;quot;Debug mode aktiveret.&amp;quot;);&lt;br /&gt;
  } &lt;br /&gt;
  else if (input == &amp;quot;debug off&amp;quot;) {&lt;br /&gt;
    debugEnabled = false;&lt;br /&gt;
    Serial.println(&amp;quot;Debug mode deaktiveret.&amp;quot;);&lt;br /&gt;
  } &lt;br /&gt;
  else if (input == &amp;quot;nullify system&amp;quot;) {&lt;br /&gt;
    Serial.println(&amp;quot;Systemet nulstilles om 42 sekunder!&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;SLUK HVIS DET VAR EN FEJL-KOMMANDO!&amp;quot;);&lt;br /&gt;
    int seconds =42;&lt;br /&gt;
    while (seconds &amp;gt; 0) {&lt;br /&gt;
      Serial.print(&amp;quot;Tid tilbage: &amp;quot;);&lt;br /&gt;
      Serial.print(seconds);&lt;br /&gt;
      Serial.println(&amp;quot; sekunder&amp;quot;);&lt;br /&gt;
      delay(1000);  // Vent 1 sekund&lt;br /&gt;
      seconds--;&lt;br /&gt;
    }&lt;br /&gt;
    resetConfig();&lt;br /&gt;
  } &lt;br /&gt;
  else {&lt;br /&gt;
    Serial.println(&amp;quot;--&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;Ugyldig kommando!&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;--&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;&#039;debug on&#039; eller &#039;debug off&#039; tænder / slukker for debug beskeder.&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;&#039;nullify system&#039; nulstiller til fabriksinstillinger.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupConfigServer() { // Starter soft-AP (WiFi access point) til konfiguration af systemet&lt;br /&gt;
  String apSSID = &amp;quot;Hottub_Config&amp;quot;;  // SSID for AP&lt;br /&gt;
  &lt;br /&gt;
  // Generer en tilfældig kode mellem 100000000 og 999999999&lt;br /&gt;
  long randomCode = random(100000000, 1000000000);&lt;br /&gt;
  String apPassword = String(randomCode);  // Konverter tallet til en streng&lt;br /&gt;
  &lt;br /&gt;
  // Start softAP&lt;br /&gt;
  WiFi.softAP(apSSID.c_str(), apPassword.c_str());&lt;br /&gt;
  &lt;br /&gt;
  // Vent et øjeblik for at sikre, at access pointet er startet&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  &lt;br /&gt;
  // Print IP-adresse for AP&lt;br /&gt;
  Serial.println(&amp;quot;Soft-AP startet.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  server.on(&amp;quot;/config&amp;quot;, handleConfig);  // Håndterer konfigurationsanmodninger&lt;br /&gt;
  server.begin();  // Starter webserveren&lt;br /&gt;
  Serial.println(&amp;quot;#############################&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;Konfigurationsserver startet.&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;SSID: &amp;quot; + apSSID);&lt;br /&gt;
  Serial.println(&amp;quot;Kode: &amp;quot; + apPassword);&lt;br /&gt;
  Serial.println(&amp;quot;Tilgå http://&amp;quot;+ WiFi.softAPIP().toString() + &amp;quot;/config for at konfigurere.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  // Vist information om konfigurationsserveren på OLED-skærmen&lt;br /&gt;
  display.clearDisplay();   // Rydder skærmen før opdatering&lt;br /&gt;
  display.setCursor(0, 0);  // Sætter cursoren i øverste venstre hjørne&lt;br /&gt;
  display.print(&amp;quot;- Config WiFi online!&amp;quot;);&lt;br /&gt;
  display.setCursor(0, 10);&lt;br /&gt;
  display.print(&amp;quot;SSID: &amp;quot; + apSSID);&lt;br /&gt;
  display.setCursor(0, 19);&lt;br /&gt;
  display.print(&amp;quot;Kodeord:&amp;quot;);&lt;br /&gt;
  display.setTextSize(2);    // Sætter skriftstørrelsen til 2 (standard er 1)&lt;br /&gt;
  display.setCursor(4, 28);&lt;br /&gt;
  display.print(apPassword); // Vis den tilfældige kode&lt;br /&gt;
  display.setTextSize(1);    // Sætter skriftstørrelsen til 1 (standard er 1)&lt;br /&gt;
  display.setCursor(0, 45);&lt;br /&gt;
  display.print(&amp;quot; IP:  &amp;quot; + WiFi.softAPIP().toString()); // Vist IP-adressen på soft-AP mode&lt;br /&gt;
  display.setCursor(0, 55);&lt;br /&gt;
  display.print(&amp;quot;http://&amp;lt;IP&amp;gt;/config&amp;quot;);&lt;br /&gt;
  display.display();        // Vis information på OLED.&lt;br /&gt;
&lt;br /&gt;
  // Vi stopper her og venter på, at brugeren gemmer en gyldig konfiguration&lt;br /&gt;
  while (strlen(ssid) == 0 || strlen(password) == 0) {&lt;br /&gt;
    delay(1000);  // Fortsæt med at vente, mens vi holder serveren aktiv&lt;br /&gt;
    server.handleClient();  // Håndter klientanmodninger under ventetid&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  // Når en gyldig konfiguration er gemt, afslut setup og gå videre til WiFi-forbindelse&lt;br /&gt;
  displayMessage(&amp;quot;Konfiguration gemt. Genstarter ESP32.&amp;quot;);&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  ESP.restart();  // Genstart for at anvende den gemte konfiguration&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void connectWiFi() { // Tilslutter til det konfigurerede Wireless netværk&lt;br /&gt;
  if (strlen(ssid) == 0 || strlen(password) == 0) {&lt;br /&gt;
    displayMessage(&amp;quot;Ugyldig WiFi config&amp;quot;);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  displayMessage(&amp;quot;Forbinder til WiFi...&amp;quot;);&lt;br /&gt;
  WiFi.begin(ssid, password);&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED &amp;amp;&amp;amp; attempts &amp;lt; 10) {&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    attempts++;&lt;br /&gt;
    displayMessage(&amp;quot;Forbinder: Forsøg &amp;quot; + String(attempts));&lt;br /&gt;
    Serial.println(&amp;quot;Forbinder til WiFi: Forsøg &amp;quot; + String(attempts));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (WiFi.status() != WL_CONNECTED) {&lt;br /&gt;
    display.clearDisplay();  // Rydder skærmen før visning&lt;br /&gt;
    display.setCursor(0, 0);&lt;br /&gt;
    display.println(&amp;quot;WiFi fejlede&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // Vis det aktuelle SSID på linje 2&lt;br /&gt;
    display.setCursor(0, 16);&lt;br /&gt;
    display.print(&amp;quot;SSID: &amp;quot;);&lt;br /&gt;
    display.print(ssid);  // Vist det aktuelle SSID&lt;br /&gt;
    &lt;br /&gt;
    display.display();  // Opdaterer skærmen&lt;br /&gt;
    Serial.println(&amp;quot;Kunne ikke forbinde til WiFi ved opstart.&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    displayMessage(&amp;quot;WiFi forbundet!&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void reconnectMQTT() { // Forsøger at oprette forbindelse til MQTT-serveren&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (!mqttClient.connected() &amp;amp;&amp;amp; attempts &amp;lt; 3) {&lt;br /&gt;
    if (mqttClient.connect(mqtt_clientID, mqtt_user, mqtt_password)) {&lt;br /&gt;
      mqttClient.publish(&amp;quot;hottubsensor/temperature&amp;quot;, &amp;quot;Forbundet til MQTT&amp;quot;);  // Send en testmeddelelse&lt;br /&gt;
    } else {&lt;br /&gt;
      Serial.print(&amp;quot;FEJL, RC=&amp;quot;);&lt;br /&gt;
      // RE 5 er f.eks. MQTT_CONNECT_UNAUTHORIZED - se https://pubsubclient.knolleary.net/api for flere fejl beskeder.&lt;br /&gt;
      Serial.print(mqttClient.state());&lt;br /&gt;
      Serial.println(&amp;quot; venter 1420 msek.&amp;quot;);&lt;br /&gt;
      delay(1420);  // Vent lidt før et nyt forsøg&lt;br /&gt;
      attempts++;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  // Hvis MQTT-forbindelsen mislykkes efter flere forsøg&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    Serial.println(&amp;quot;Kunne ikke forbinde til MQTT efter 3 forsøg.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void sendTemperatureToMQTT(float temp) { // Sender aktuel temperatur til MQTT server&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    return;  // Send kun, hvis MQTT er forbundet&lt;br /&gt;
  }&lt;br /&gt;
  char tempStr[10];                 // Midlertidig buffer&lt;br /&gt;
  dtostrf(temp, 6, 2, tempStr);     // Konverterer float til string med 2 decimaler&lt;br /&gt;
  mqttClient.publish(&amp;quot;hottubsensor/temperature&amp;quot;, tempStr);&lt;br /&gt;
  debugEnabled ? (void)Serial.println(&amp;quot;MQTT publish: &amp;quot; + String(tempStr)) : (void)0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void updateOledScreen(float temp) { // Opdatrerer OLED skærm med information under normal drift&lt;br /&gt;
  display.clearDisplay();   // Rydder skærmen før opdatering&lt;br /&gt;
  display.setCursor(0, 0);  // Sætter cursoren i øverste venstre hjørne&lt;br /&gt;
  &lt;br /&gt;
  // Vist temperatur på den første linje&lt;br /&gt;
  display.setTextSize(3);   // Sætter skriftstørrelsen til 3 (standard er 1)&lt;br /&gt;
  // display.print(&amp;quot; &amp;quot;);       // lav et mellemrum&lt;br /&gt;
  display.print(temp);      // Vis temperatur&lt;br /&gt;
  //display.print(&amp;quot; &amp;quot;);       // lav et mellemrum&lt;br /&gt;
  display.write(247);       // 247 er ASCII-koden for &#039;°&#039; i Adafruit GFX&lt;br /&gt;
  display.print(&amp;quot;C&amp;quot;);&lt;br /&gt;
  display.setTextSize(1);   // Gå tilbage til normal skriftstørrelse for de næste linjer&lt;br /&gt;
  if (debugEnabled) {&lt;br /&gt;
    char tempStr[10];                 // Midlertidig buffer&lt;br /&gt;
    dtostrf(temp, 6, 2, tempStr);     // Konverterer float til string med 2 decimaler&lt;br /&gt;
    Serial.println(&amp;quot;OLED update: &amp;quot; + String(tempStr));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Vist IP-adresse på den næste linje&lt;br /&gt;
  display.setCursor(0, 28);  // Sætter cursoren på næste linje&lt;br /&gt;
  display.print(&amp;quot;IP:   &amp;quot;);&lt;br /&gt;
  display.print(WiFi.localIP());  // Vist IP-adresse&lt;br /&gt;
  debugEnabled ? (void)Serial.println(String(&amp;quot;OLED update: IP = &amp;quot;) + WiFi.localIP().toString()) : (void)0;&lt;br /&gt;
  &lt;br /&gt;
  // Vist WiFi status på næste linje&lt;br /&gt;
  display.setCursor(0, 41);  // Sætter cursoren på den næste linje&lt;br /&gt;
  display.print(&amp;quot;WiFi: &amp;quot;);&lt;br /&gt;
  if (WiFi.status() == WL_CONNECTED) {&lt;br /&gt;
    display.print(WiFi.SSID()); // Vis SSID for aktuel WiFi&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: SSID: &amp;quot; + WiFi.SSID()) : (void)0;&lt;br /&gt;
  } else {&lt;br /&gt;
    display.print(&amp;quot;Ikke forbundet&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: WiFi: Ikke forbundet&amp;quot;) : (void)0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Vis MQTT informationer på næste linje&lt;br /&gt;
  display.setCursor(0, 53);  // Sætter cursoren på næste linje&lt;br /&gt;
  display.print(&amp;quot;MQTT: &amp;quot;);&lt;br /&gt;
  // Vis kun MQTT status , hvis mqtt_server er angivet&lt;br /&gt;
  if (strlen(mqtt_server) &amp;gt; 0) {&lt;br /&gt;
    // Hvis MQTT er aktiveret, vis status, ellers &amp;quot;Ikke forbundet&amp;quot;&lt;br /&gt;
    display.print(mqttClient.connected() ? &amp;quot;Forbundet&amp;quot; : &amp;quot;Ikke forbundet&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: MQTT: &amp;quot; + mqttClient.connected() ? &amp;quot;Forbundet&amp;quot; : &amp;quot;Ikke forbundet&amp;quot;) : (void)0;&lt;br /&gt;
  } else {&lt;br /&gt;
    display.print(&amp;quot;Ikke i brug.&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: MQTT: Ikke i brug.&amp;quot;) : (void)0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  display.display();  // Opdaterer skærmen med de nye data&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayMessage(String message) { // Skriver tekst ca. midt på OLED skærmen&lt;br /&gt;
  display.clearDisplay();             // Rydder skærmen&lt;br /&gt;
  display.setCursor(0, 24);           // Sætter cursoren ca. midt på skærmen&lt;br /&gt;
  display.println(message);           // Vist besked på skærmen&lt;br /&gt;
  display.display();                  // Opdaterer skærmen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleRoot() { // Root URL, der viser temperaturdata&lt;br /&gt;
  sensors.requestTemperatures();  // Anmoder om temperaturmåling&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);  // Henter temperatur fra den første sensor (index 0)&lt;br /&gt;
  String response;&lt;br /&gt;
&lt;br /&gt;
  if (tempC == -127.0) {&lt;br /&gt;
    response = &amp;quot;&amp;lt;p id=&#039;temperature&#039; class=&#039;error&#039;&amp;gt;Fejl: Ingen sensor fundet&amp;lt;/p&amp;gt;\n&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    response = &amp;quot;&amp;lt;div class=&#039;meter-container&#039;&amp;gt;&amp;lt;meter id=&#039;temperatur&#039; value=&#039;&amp;quot; + String(tempC) + &amp;quot;&#039; min=&#039;0&#039; max=&#039;45&#039; low=&#039;5&#039; high=&#039;37&#039; optimum=&#039;40&#039;&amp;gt;&amp;lt;/meter&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
    response += &amp;quot;&amp;lt;p id=&#039;temperature&#039; class=&#039;temperature&#039;&amp;gt;Aktuel temperatur: &amp;quot; + String(tempC) + &amp;quot; &amp;amp;deg;C&amp;lt;/p&amp;gt;\n&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String ipAddress = WiFi.localIP().toString();&lt;br /&gt;
&lt;br /&gt;
  response += &amp;quot;&amp;lt;div class=&#039;info&#039;&amp;gt;\nGå til &amp;lt;a href=&#039;http://&amp;quot; + ipAddress + &amp;quot;/reset&#039;&amp;gt;&amp;quot; + ipAddress + &amp;quot;/reset&amp;lt;/a&amp;gt; for at factory resette.&amp;lt;br /&amp;gt;\n&amp;quot;;&lt;br /&gt;
  response += &amp;quot;Gå til &amp;lt;a href=&#039;http://&amp;quot; + ipAddress + &amp;quot;/config&#039;&amp;gt;&amp;quot; + ipAddress + &amp;quot;/config&amp;lt;/a&amp;gt; for at ændre konfig.\n&amp;lt;/div&amp;gt;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  // Tilføjer HTML, CSS og JavaScript for dark mode og dynamisk temperatur-opdatering&lt;br /&gt;
  String htmlResponse = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n&amp;lt;html lang=&#039;da&#039;&amp;gt;\n&amp;lt;head&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;title&amp;gt;Hottub Temperatur&amp;lt;/title&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;style&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;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&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;h1 { color: green; font-size: 2rem; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.meter-container { margin-top: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;meter { width: 80%; max-width: 400px; height: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.temperature, .error { font-size: 1.2rem; margin-top: 10px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.error { color: red; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.info { color: silver; font-size: 1rem; margin-top: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.info.dark-mode { color: dimgray; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;a, a:visited { color: silver; text-decoration: underline; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;a.dark-mode, a.dark-mode:visited { color: dimgray; text-decoration: underline; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;button { padding: 10px 20px; margin-top: 20px; cursor: pointer; background-color: green; color: white; border: none; border-radius: 5px; font-size: 1rem; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;button:hover { background-color: darkgreen; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;body.dark-mode { background-color: black; color: green; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;body.dark-mode a { color: dimgray; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/style&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/head&amp;gt;\n&amp;lt;body&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;h1&amp;gt;Temperaturmåling&amp;lt;/h1&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += response;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;button id=&#039;themeToggle&#039; onclick=&#039;toggleDarkMode()&#039;&amp;gt;Dark Mode&amp;lt;/button&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;script&amp;gt;\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  // Dark mode javascript funktion.&lt;br /&gt;
  htmlResponse += &amp;quot;function toggleDarkMode() {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.body.classList.toggle(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  const isDarkMode = document.body.classList.contains(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  localStorage.setItem(&#039;darkMode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.getElementById(&#039;themeToggle&#039;).textContent = isDarkMode ? &#039;Light Mode&#039; : &#039;Dark Mode&#039;;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Toggler dark-mode for .info elementerne\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;.info&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.toggle(&#039;dark-mode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Toggler dark-mode for &amp;lt;a&amp;gt; elementerne\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;a&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.toggle(&#039;dark-mode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;if (localStorage.getItem(&#039;darkMode&#039;) === &#039;true&#039;) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.body.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.getElementById(&#039;themeToggle&#039;).textContent = &#039;Light Mode&#039;;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Tilføjer dark-mode til .info elementerne ved sideindlæsning, hvis dark mode er aktivt\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;.info&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Tilføjer dark-mode til &amp;lt;a&amp;gt; elementerne ved sideindlæsning, hvis dark mode er aktivt\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;a&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  // Javascript funktion til at opdatere temperatur dynamisk.&lt;br /&gt;
  htmlResponse += &amp;quot;function fetchTemperature() {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  fetch(&#039;/temperature&#039;).then(response =&amp;gt; response.text()).then(data =&amp;gt; {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    document.getElementById(&#039;temperature&#039;).innerHTML = data;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;setInterval(fetchTemperature, &amp;quot; + String(updateInterval) + &amp;quot;);\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/script&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/body&amp;gt;\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  server.send(200, &amp;quot;text/html&amp;quot;, htmlResponse);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleTemperature() { // URL til at sende temperaturdata til JavaScript opdatering af handleRoot&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  if (tempC == -127.0) {&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, &amp;quot;&amp;lt;p class=&#039;error&#039;&amp;gt;Fejl: Ingen sensor fundet&amp;lt;/p&amp;gt;&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, &amp;quot;Aktuel temperatur: &amp;quot; + String(tempC) + &amp;quot; &amp;amp;deg;C&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleReset() { // Håndterer web kald til /reset (websiden som kan bruges til at nulstille systemet)&lt;br /&gt;
  String input = server.arg(&amp;quot;reset_input&amp;quot;);  // Få input fra formularen&lt;br /&gt;
  &lt;br /&gt;
  // Tjek om input er &amp;quot;reset&amp;quot;, uanset store/små bogstaver&lt;br /&gt;
  if (input.equalsIgnoreCase(&amp;quot;reset&amp;quot;)) {&lt;br /&gt;
    server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;System nulstilles og genstartes.&amp;quot;);  // Vist besked om nulstilling&lt;br /&gt;
    resetConfig();  // Nulstil konfiguration.&lt;br /&gt;
    delay(3000);    // Vent tre sekunder.&lt;br /&gt;
    ESP.restart();  // Genstart ESP32.&lt;br /&gt;
  } else {&lt;br /&gt;
    // Hvis input ikke er korrekt, send en fejlbesked&lt;br /&gt;
    String htmlResponse = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n&amp;lt;html lang=&#039;da&#039;&amp;gt;\n&amp;lt;head&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta http-equiv=&#039;refresh&#039; content=&#039;15&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;title&amp;gt;Hottub Temperatur&amp;lt;/title&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/head&amp;gt;\n&amp;lt;body style=&#039;color: red; text-align: center;&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;h1&amp;gt;Skriv &#039;reset&#039; i tekstboksen for, at nulstille systemet.&amp;lt;/h1&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;form method=&#039;POST&#039; action=&#039;/reset&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;input type=&#039;text&#039; name=&#039;reset_input&#039; placeholder=&#039;Skriv her&#039; required&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;input type=&#039;submit&#039; value=&#039;Nulstil&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/form&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/body&amp;gt;\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
    server.send(400, &amp;quot;text/html&amp;quot;, htmlResponse);  // Send fejlmeddelelse hvis input er forkert&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleConfig() { // Håndterer web kald til /config (webside hvor systemet kan konfigureres)&lt;br /&gt;
  // Hvis det er en POST-anmodning (brugeren indsender data)&lt;br /&gt;
  if (server.method() == HTTP_POST) {&lt;br /&gt;
    // Læs konfigurationsdata fra webformularen og gem dem i variablerne&lt;br /&gt;
    strncpy(ssid, server.arg(&amp;quot;ssid&amp;quot;).c_str(), sizeof(ssid));&lt;br /&gt;
    strncpy(password, server.arg(&amp;quot;password&amp;quot;).c_str(), sizeof(password));&lt;br /&gt;
    strncpy(mqtt_server, server.arg(&amp;quot;mqtt_server&amp;quot;).c_str(), sizeof(mqtt_server));&lt;br /&gt;
    mqtt_port = server.arg(&amp;quot;mqtt_port&amp;quot;).toInt();  // Konverterer MQTT-porten fra tekst til integer&lt;br /&gt;
    if (mqtt_port &amp;lt;= 0 || mqtt_port &amp;gt; 65535) {  // Validering af portnummer&lt;br /&gt;
      mqtt_port = 1883;  // Standard MQTT-port, hvis den er ugyldig&lt;br /&gt;
    }&lt;br /&gt;
    strncpy(mqtt_user, server.arg(&amp;quot;mqtt_user&amp;quot;).c_str(), sizeof(mqtt_user));&lt;br /&gt;
    strncpy(mqtt_password, server.arg(&amp;quot;mqtt_password&amp;quot;).c_str(), sizeof(mqtt_password));&lt;br /&gt;
    &lt;br /&gt;
    // Gem konfigurationen i EEPROM&lt;br /&gt;
    saveConfig();&lt;br /&gt;
    &lt;br /&gt;
    // Bekræftelse og information&lt;br /&gt;
    server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Konfiguration gemt. Genstart ESP32.&amp;quot;);&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    ESP.restart();  // Genstart for at anvende den nye konfiguration&lt;br /&gt;
  } else {&lt;br /&gt;
    // Returner HTML-formular for konfiguration&lt;br /&gt;
    String html = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html lang=&#039;da&#039;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;&amp;lt;title&amp;gt;Konfiguration&amp;lt;/title&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;style&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f4f4f9; color: #333; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;h1 { text-align: center; color: #4CAF50; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;.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); }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;text&#039;], input[type=&#039;password&#039;], input[type=&#039;number&#039;] { width: 100%; padding: 12px; margin: 8px 0; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;submit&#039;] { background-color: #4CAF50; color: white; padding: 14px 20px; border: none; border-radius: 4px; cursor: pointer; width: 100%; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;submit&#039;]:hover { background-color: #45a049; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;label { font-weight: bold; margin-top: 10px; display: block; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;.info { text-align: center; font-size: 14px; color: #777; margin-top: 20px; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;div class=&#039;container&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;h1&amp;gt;Konfiguration&amp;lt;/h1&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;form method=&#039;POST&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;ssid&#039;&amp;gt;SSID:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;ssid&#039; name=&#039;ssid&#039; type=&#039;text&#039; required&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;password&#039;&amp;gt;Password:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;password&#039; name=&#039;password&#039; type=&#039;password&#039; required&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_server&#039;&amp;gt;MQTT Server:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_server&#039; name=&#039;mqtt_server&#039; type=&#039;text&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_port&#039;&amp;gt;MQTT Port:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_port&#039; name=&#039;mqtt_port&#039; type=&#039;number&#039; value=&#039;1883&#039; min=&#039;1&#039; max=&#039;65535&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_user&#039;&amp;gt;MQTT User:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_user&#039; name=&#039;mqtt_user&#039; type=&#039;text&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_password&#039;&amp;gt;MQTT Password:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_password&#039; name=&#039;mqtt_password&#039; type=&#039;password&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;input type=&#039;submit&#039; value=&#039;Gem Konfiguration&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/form&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;div class=&#039;info&#039;&amp;gt;Undlad MQTT-information, hvis den ikke skal bruges.&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, html);  // Sender formularen som HTML&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void resetConfig() { // Nulstiller al konfigurations data&lt;br /&gt;
  memset(ssid, 0, sizeof(ssid));&lt;br /&gt;
  memset(password, 0, sizeof(password));&lt;br /&gt;
  memset(mqtt_server, 0, sizeof(mqtt_server));&lt;br /&gt;
  memset(mqtt_user, 0, sizeof(mqtt_user));&lt;br /&gt;
  memset(mqtt_password, 0, sizeof(mqtt_password));&lt;br /&gt;
  mqtt_port = 1883;  // Default port&lt;br /&gt;
&lt;br /&gt;
  // Initialiser EEPROM med den ønskede størrelse&lt;br /&gt;
  if (!EEPROM.begin(EEPROM_SIZE)) {&lt;br /&gt;
    Serial.println(&amp;quot;EEPROM initialization failed.&amp;quot;);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Rydder EEPROM ved at skrive 0xFF til hver byte (fjerner gamle data)&lt;br /&gt;
  for (int i = 0; i &amp;lt; EEPROM_SIZE; i++) {&lt;br /&gt;
    EEPROM.write(i, 0xFF);  // Write 0xFF to each byte&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  Serial.println(&amp;quot;Skriver til alle adresser.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  saveConfig();  // Funktion til at gemme den opdaterede (tomme) konfiguration til EEPROM&lt;br /&gt;
&lt;br /&gt;
  display.clearDisplay();  // Rydder skærmen&lt;br /&gt;
  display.setTextSize(2);   // Sætter skriftstørrelsen til 2&lt;br /&gt;
  display.println(&amp;quot;WiFi og MQTT&amp;quot;);&lt;br /&gt;
  display.println(&amp;quot; nulstillet.&amp;quot;);&lt;br /&gt;
  display.println(&amp;quot;Genstarter!&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;WiFi og MQTT nulstillet. Genstarter ESP32.&amp;quot;);&lt;br /&gt;
  delay(3000);&lt;br /&gt;
  ESP.restart();  // Restart the ESP32&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void saveConfig() { // Gemmer konfigurationen i EEPROM&lt;br /&gt;
  EEPROM.put(0, ssid);&lt;br /&gt;
  EEPROM.put(32, password);&lt;br /&gt;
  EEPROM.put(64, mqtt_server);&lt;br /&gt;
  EEPROM.put(128, mqtt_port);&lt;br /&gt;
  EEPROM.put(132, mqtt_user);&lt;br /&gt;
  EEPROM.put(164, mqtt_password);&lt;br /&gt;
  EEPROM.commit();  // Bekræft gemt data&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void readConfig() { // Læs konfigurationen fra EEPROM&lt;br /&gt;
  EEPROM.get(0, ssid);&lt;br /&gt;
  EEPROM.get(32, password);&lt;br /&gt;
  EEPROM.get(64, mqtt_server);&lt;br /&gt;
  EEPROM.get(128, mqtt_port);&lt;br /&gt;
  EEPROM.get(132, mqtt_user);&lt;br /&gt;
  EEPROM.get(164, mqtt_password);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diverse links ==&lt;br /&gt;
* [https://samueladesola.medium.com/how-to-set-up-esp32-wroom-32-b2100060470c How to set up ESP32-WROOM-329] in Arduino IDE.&lt;br /&gt;
&lt;br /&gt;
== Afslutning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Arduino]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2080</id>
		<title>Temperaturføler med ESP32, DS18B20 og OLED-skærm</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2080"/>
		<updated>2026-05-07T14:26:44Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Kode til ESP32 */  vises nu korrekt uden mw-collapsible som ikke virkede mere??&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Indledning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Projektet inkluderer nem konfiguration af opkobling til Wi-Fi og der er mulighed for, at sende data til en MQTT-server.&lt;br /&gt;
&lt;br /&gt;
== Indkøbsliste ==&lt;br /&gt;
* [https://arduinotech.dk/shop/esp32-usb-c-iot-mainboard-devkitc-cp2102-38pin/ ESP32 USB-C IoT bundkort DevKitC CP2102 38PIN]&lt;br /&gt;
* [https://arduinotech.dk/shop/0-96%e2%80%b3-i2c-iic-spi-serial-128x64-oled-new-model/ 0,96&amp;quot; I2C 128x64 OLED-display]&lt;br /&gt;
* [https://arduinotech.dk/shop/ds18b20-waterproof-digital-thermal-probe-with-3-5mm-mini-audio-jack-1-meter-cable/ DS18B20 vandtæt digital termisk sonde (1 meter kabel)]&lt;br /&gt;
* [https://arduinotech.dk/shop/tsp-xx-replace-hlk-pmxx-ac-dc-220v-to-12v-buck-step-down-power-supply-module-converter/ HLK-PMxx 220VAC til 5VDC Step Down Converter]&lt;br /&gt;
* [https://arduinotech.dk/shop/sonoff-ip66-waterproof-case/ Sonoff IP66 vandtæt etui]&lt;br /&gt;
* [https://arduinotech.dk/shop/prototype-pcb-print/ Prototype PCB print - Type 1]&lt;br /&gt;
&lt;br /&gt;
== Byggevejledning ==&lt;br /&gt;
&lt;br /&gt;
1. Tilslutninger:  &lt;br /&gt;
   - DS18B20:  &lt;br /&gt;
     - VCC (rød)  → 3.3V på ESP32  &lt;br /&gt;
     - GND (sort) → GND på ESP32  &lt;br /&gt;
     - Data (gul) → GPIO 4 på ESP32  &lt;br /&gt;
     - Tilføj en 4,7kΩ pull-up modstand mellem Data og VCC.&lt;br /&gt;
&lt;br /&gt;
   - OLED-skærm (I2C):  &lt;br /&gt;
     - VCC → 3.3V på ESP32  &lt;br /&gt;
     - GND → GND  på ESP32  &lt;br /&gt;
     - SDA → GPIO 21 på ESP32  &lt;br /&gt;
     - SCL → GPIO 22 på ESP32  &lt;br /&gt;
&lt;br /&gt;
2. Strømforsyning:  &lt;br /&gt;
   Tilslut ESP32 og OLED til 5V strømforsyning via HLK-PMxx-modulet.&lt;br /&gt;
&lt;br /&gt;
3. Montering:  &lt;br /&gt;
   - Montér komponenterne på prototype-PCB.  &lt;br /&gt;
   - Placér hele opsætningen i Sonoff IP66 vandtæt etui for beskyttelse mod vejr og støv.&lt;br /&gt;
&lt;br /&gt;
== Kode til ESP32 ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* README plz...&lt;br /&gt;
   Benyt &amp;quot;uPesy ESP32 Wroom DevKit&amp;quot; som board model til kompilering.&lt;br /&gt;
   Jeg har oplevet, at andre har fejl der får LED til at blinke for hurtigt !?!&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;WiFi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;WebServer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;OneWire.h&amp;gt;&lt;br /&gt;
#include &amp;lt;DallasTemperature.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_GFX.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_SSD1306.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;&lt;br /&gt;
#include &amp;lt;EEPROM.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// === PIN-konfigurationer ===&lt;br /&gt;
#define ONE_WIRE_BUS   4  // Pin til DS18B20 temperatursensoren.&lt;br /&gt;
#define SCREEN_WIDTH 128  // Skærmens bredde (for OLED).&lt;br /&gt;
#define SCREEN_HEIGHT 64  // Skærmens højde (for OLED).&lt;br /&gt;
#define OLED_RESET    -1  // Reset pin til OLED (sættes til -1, da vi ikke bruger en fysisk reset pin).&lt;br /&gt;
#define EEPROM_SIZE 4096  // Størrelsen af EEPROM (kan gemme op til 4096 byte).&lt;br /&gt;
#define RST_PIN       13  // Pin for RST-knappen.&lt;br /&gt;
#define LED_BUILTIN    2  // Pin for LED.&lt;br /&gt;
&lt;br /&gt;
// === Globale objekter ===&lt;br /&gt;
OneWire oneWire(ONE_WIRE_BUS);        // Initialiserer OneWire-bus til DS18B20.&lt;br /&gt;
DallasTemperature sensors(&amp;amp;oneWire);  // Opretter et objekt til at håndtere temperaturmålinger.&lt;br /&gt;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &amp;amp;Wire, OLED_RESET);  // OLED-skærm til at vise informationer.&lt;br /&gt;
WebServer server(80);                 // Webserver på port 80 til at håndtere HTTP-anmodninger.&lt;br /&gt;
WiFiClient espClient;                 // WiFi klient til MQTT.&lt;br /&gt;
PubSubClient mqttClient(espClient);   // MQTT-klient, der bruger WiFi klienten.&lt;br /&gt;
&lt;br /&gt;
// === Globale variabler ===&lt;br /&gt;
unsigned long lastCommsCheck = 0;          // Tidspunkt for sidste Comms-tjek.&lt;br /&gt;
unsigned long CommsCheckInterval = 60000;  // Tidsinterval for Comms-tjek (60 sekunder) angivet i millisekunder.&lt;br /&gt;
unsigned long lastLEDCheck = 0;            // Tidspunkt for sidste LED-tjek.&lt;br /&gt;
unsigned long LEDCheckInterval = 2000;     // Tidsinterval for LED-tjek (2 sekunder) angivet i millisekunder.&lt;br /&gt;
unsigned long lastMQTTpublish = 0;         // Tidspunkt for sidste MQTT-publish.&lt;br /&gt;
unsigned long MQTTPublishInterval = 15000; // Tidsinterval for MQTT publish (15 sekunder) angivet i millisekunder.&lt;br /&gt;
bool debugEnabled = false;                 // Skal der udskriver til seriel port?&lt;br /&gt;
int updateInterval = 15000;                // Variabel til 15 sekunders opdateringsinterval angivet i millisekunder.&lt;br /&gt;
&lt;br /&gt;
// === Netværksindstillinger ===&lt;br /&gt;
char ssid[32] = &amp;quot;&amp;quot;;      // SSID til WiFi netværket.&lt;br /&gt;
char password[32] = &amp;quot;&amp;quot;;  // WiFi adgangskode.&lt;br /&gt;
&lt;br /&gt;
// === MQTT-indstillinger ===&lt;br /&gt;
char mqtt_server[128] = &amp;quot;&amp;quot;;    // MQTT-serverens IP eller domæne.&lt;br /&gt;
int mqtt_port = 1883;          // MQTT-port (standard 1883).&lt;br /&gt;
char mqtt_user[128] = &amp;quot;&amp;quot;;      // MQTT-brugernavn.&lt;br /&gt;
char mqtt_password[128] = &amp;quot;&amp;quot;;  // MQTT-adgangskode.&lt;br /&gt;
char mqtt_clientID[128] = &amp;quot;&amp;quot;;  // MQTT-clientID.&lt;br /&gt;
&lt;br /&gt;
void setup() { // Initiel programmatiske konfigurations kode. &lt;br /&gt;
  pinMode(RST_PIN, INPUT);        // Initialiser RST_PIN som et INPUT.&lt;br /&gt;
  pinMode(LED_BUILTIN, OUTPUT);   // Initialiser den blå LED.&lt;br /&gt;
  pinMode(RST_PIN, INPUT_PULLUP); // Aktiver intern pull-up modstand.&lt;br /&gt;
  digitalWrite(LED_BUILTIN, LOW); // Sluk den blå LED.&lt;br /&gt;
  Serial.begin(115200);           // Initialiserer seriel kommunikation til debugging.&lt;br /&gt;
  EEPROM.begin(EEPROM_SIZE);      // Initialiserer EEPROM for at gemme konfigurationer.&lt;br /&gt;
  sensors.begin();                // Starter temperatursensoren.&lt;br /&gt;
  lastCommsCheck = millis();      // Sætter første tidsværdi af variablen.&lt;br /&gt;
  lastLEDCheck = millis();        // Sætter første tidsværdi af variablen.&lt;br /&gt;
  lastMQTTpublish = millis();     // Sætter første tidsværdi af variablen.&lt;br /&gt;
  &lt;br /&gt;
  // OLED-initialisering&lt;br /&gt;
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {  // Tjekker om OLED-skærmen er korrekt tilsluttet&lt;br /&gt;
    Serial.println(F(&amp;quot;OLED fejl!&amp;quot;));&lt;br /&gt;
    for (;;);  // Stopper programmet, hvis skærmen ikke kan initialiseres&lt;br /&gt;
  }&lt;br /&gt;
  display.clearDisplay();  // Rydder skærmen&lt;br /&gt;
  display.setTextSize(1);  // Sætter tekststørrelse&lt;br /&gt;
  display.setTextColor(WHITE);  // Sætter tekstfarve til hvid&lt;br /&gt;
&lt;br /&gt;
  // Tjek om RST-knappen er trykket under opstart&lt;br /&gt;
  if (digitalRead(RST_PIN) == LOW) {  // Hvis pin er LOW&lt;br /&gt;
    Serial.println(&amp;quot;RST-knappen trykket. Nulstiller WiFi og MQTT konfiguration.&amp;quot;);&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    resetConfig();  // Nulstil konfiguration&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readConfig();                   // Læs gemte konfigurationer fra EEPROM&lt;br /&gt;
  if (strlen(ssid) == 0 || strlen(password) == 0) { // Hvis ingen konfiguration er gemt, start konfigurationsmode&lt;br /&gt;
    displayMessage(&amp;quot;WiFi konfig mangler.\n\nStarter konfig WiFi.&amp;quot;);&lt;br /&gt;
    setupConfigServer();          // Starter konfigurationsserveren, så brugeren kan konfigurere WiFi og MQTT&lt;br /&gt;
    } else {&lt;br /&gt;
    // WiFi-forbindelse&lt;br /&gt;
    connectWiFi();                // Forbinder til WiFi, hvis SSID og adgangskode er gemt&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Webserver-ruter&lt;br /&gt;
  server.on(&amp;quot;/&amp;quot;, handleRoot);                   // Root URL, der viser temperaturdata&lt;br /&gt;
  server.on(&amp;quot;/temperature&amp;quot;, handleTemperature); // URL til at sende temperaturdata til JavaScript opdatering af handleRoot&lt;br /&gt;
  server.on(&amp;quot;/config&amp;quot;, handleConfig);           // URL til at konfigurere WiFi og MQTT&lt;br /&gt;
  server.on(&amp;quot;/reset&amp;quot;, handleReset);             // URL til at nulstille WiFi&lt;br /&gt;
  server.begin();  // Starter webserveren&lt;br /&gt;
&lt;br /&gt;
  if (strlen(mqtt_server) &amp;gt; 0) { // start kun MQTT, hvis &amp;quot;mqtt_server&amp;quot; er konfigureret og gemt.&lt;br /&gt;
    // Hent MAC-adressen&lt;br /&gt;
    String mac = WiFi.macAddress();                         // Eksempel: &amp;quot;24:6F:28:1A:2B:3C&amp;quot;&lt;br /&gt;
    Serial.println(mac);&lt;br /&gt;
    mac.replace(&amp;quot;:&amp;quot;,&amp;quot;&amp;quot;);                                    // fjern kolon&#039;er&lt;br /&gt;
    String lastSixChars = mac.substring(mac.length() - 6);  // Gem de sidste 6 tegn - Resultat: &amp;quot;2B:3C&amp;quot;&lt;br /&gt;
    String mqtt_clientID = &amp;quot;hottub-&amp;quot; + lastSixChars;        // Generer klient-id baseret på de sidste 6 bogstaver og tal wifi mac adresse&lt;br /&gt;
    mqttClient.setServer(mqtt_server, mqtt_port);&lt;br /&gt;
    if (!mqttClient.connected()) {                          // HVis MQTT ikke er forbundet,&lt;br /&gt;
      reconnectMQTT();                                      // Forsøg at oprette forbindelse til MQTT.&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_server   = &amp;quot;) + mqtt_server);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_port     = &amp;quot;) + mqtt_port);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt-klientID = &amp;quot;) + mqtt_clientID);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_user     = &amp;quot;) + mqtt_user);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_password = &amp;quot;) + mqtt_password);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot; - Debug beskeder kan aktiveres på seriel port med &#039;debug on&#039; eller &#039;debug off&#039;(standard).&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot; - Systemet kan nulstilles ved at sende &#039;nullify system&#039; på seriel porten.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() { // Her placeres koden der skal eksekveres kontinuerligt&lt;br /&gt;
  server.handleClient();                                   // Lad webserveren behandle indkommende HTTP-anmodninger.&lt;br /&gt;
  sensors.requestTemperatures();                           // Anmoder om temperaturmåling.&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);                // Hente temperatur fra den første sensor (index 0).&lt;br /&gt;
  updateOledScreen(tempC);                                 // Opdaterer OLED-skærmen.&lt;br /&gt;
  &lt;br /&gt;
  if (millis() - lastMQTTpublish &amp;gt;= MQTTPublishInterval) { // Send MQTT publish hvis det er tid til det (hver 15. sekunder)&lt;br /&gt;
    if (strlen(mqtt_server) &amp;gt; 0) {                         // Send kun MQTT, hvis mqtt_server er angivet&lt;br /&gt;
      if (mqttClient.connected()) {                        // Send temperaturdata til MQTT, hvis forbindelsen er aktiv&lt;br /&gt;
        sendTemperatureToMQTT(tempC);&lt;br /&gt;
      }&lt;br /&gt;
      lastMQTTpublish = millis();                          // Opdater lastMQTTpublish variablen til nu.&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if (millis() - lastCommsCheck &amp;gt;= CommsCheckInterval) {   // Tjek forbindelser hvis det er tid til det (hver 60. sekunder)&lt;br /&gt;
    if (WiFi.status() != WL_CONNECTED) {                   // Hvis WiFi ikke er forbundet,&lt;br /&gt;
      connectWiFi();                                       // Forsøg at oprette forbindelse til WiFi.&lt;br /&gt;
    }&lt;br /&gt;
    if (strlen(mqtt_server) &amp;gt; 0) {                         // Tjek kun MQTT, hvis mqtt_server er angivet&lt;br /&gt;
      if (!mqttClient.connected()) {                       // HVis MQTT ikke er forbundet,&lt;br /&gt;
        reconnectMQTT();                                   // Forsøg at oprette forbindelse til MQTT.&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    lastCommsCheck = millis();                             // Opdater lastCommsCheck variablen til nu.&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (millis() - lastLEDCheck &amp;gt;= LEDCheckInterval) {      // Blinke funktion til indikation af at loop() køre&lt;br /&gt;
&lt;br /&gt;
    // Serial.print(millis());&lt;br /&gt;
    // Serial.print(&amp;quot; - &amp;quot;);&lt;br /&gt;
    // Serial.print(lastLEDCheck);&lt;br /&gt;
    // Serial.print(&amp;quot; = &amp;quot;);&lt;br /&gt;
    // Serial.println(millis() - lastLEDCheck);&lt;br /&gt;
    // Serial.println(&amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // Læs den nuværende tilstand af LED_BUILTIN og sæt den modsatte værdi.&lt;br /&gt;
    lastLEDCheck = millis();                              // Opdater tidspunktet for LED status ændring&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (Serial.available() &amp;gt; 0) {                           // Håndter seriel kommando linje&lt;br /&gt;
    handleSerialInput();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleSerialInput() { // Håndter seriel kommando linje&lt;br /&gt;
  String input = Serial.readStringUntil(&#039;\n&#039;);  // Læs indtil linjeskift&lt;br /&gt;
  input.trim();  // Fjern eventuelle mellemrum eller linjeskift&lt;br /&gt;
&lt;br /&gt;
  if (input == &amp;quot;debug on&amp;quot;) {&lt;br /&gt;
    debugEnabled = true;&lt;br /&gt;
    Serial.println(&amp;quot;Debug mode aktiveret.&amp;quot;);&lt;br /&gt;
  } &lt;br /&gt;
  else if (input == &amp;quot;debug off&amp;quot;) {&lt;br /&gt;
    debugEnabled = false;&lt;br /&gt;
    Serial.println(&amp;quot;Debug mode deaktiveret.&amp;quot;);&lt;br /&gt;
  } &lt;br /&gt;
  else if (input == &amp;quot;nullify system&amp;quot;) {&lt;br /&gt;
    Serial.println(&amp;quot;Systemet nulstilles om 42 sekunder!&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;SLUK HVIS DET VAR EN FEJL-KOMMANDO!&amp;quot;);&lt;br /&gt;
    int seconds =42;&lt;br /&gt;
    while (seconds &amp;gt; 0) {&lt;br /&gt;
      Serial.print(&amp;quot;Tid tilbage: &amp;quot;);&lt;br /&gt;
      Serial.print(seconds);&lt;br /&gt;
      Serial.println(&amp;quot; sekunder&amp;quot;);&lt;br /&gt;
      delay(1000);  // Vent 1 sekund&lt;br /&gt;
      seconds--;&lt;br /&gt;
    }&lt;br /&gt;
    resetConfig();&lt;br /&gt;
  } &lt;br /&gt;
  else {&lt;br /&gt;
    Serial.println(&amp;quot;--&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;Ugyldig kommando!&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;--&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;&#039;debug on&#039; eller &#039;debug off&#039; tænder / slukker for debug beskeder.&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;&#039;nullify system&#039; nulstiller til fabriksinstillinger.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupConfigServer() { // Starter soft-AP (WiFi access point) til konfiguration af systemet&lt;br /&gt;
  String apSSID = &amp;quot;Hottub_Config&amp;quot;;  // SSID for AP&lt;br /&gt;
  &lt;br /&gt;
  // Generer en tilfældig kode mellem 100000000 og 999999999&lt;br /&gt;
  long randomCode = random(100000000, 1000000000);&lt;br /&gt;
  String apPassword = String(randomCode);  // Konverter tallet til en streng&lt;br /&gt;
  &lt;br /&gt;
  // Start softAP&lt;br /&gt;
  WiFi.softAP(apSSID.c_str(), apPassword.c_str());&lt;br /&gt;
  &lt;br /&gt;
  // Vent et øjeblik for at sikre, at access pointet er startet&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  &lt;br /&gt;
  // Print IP-adresse for AP&lt;br /&gt;
  Serial.println(&amp;quot;Soft-AP startet.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  server.on(&amp;quot;/config&amp;quot;, handleConfig);  // Håndterer konfigurationsanmodninger&lt;br /&gt;
  server.begin();  // Starter webserveren&lt;br /&gt;
  Serial.println(&amp;quot;#############################&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;Konfigurationsserver startet.&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;SSID: &amp;quot; + apSSID);&lt;br /&gt;
  Serial.println(&amp;quot;Kode: &amp;quot; + apPassword);&lt;br /&gt;
  Serial.println(&amp;quot;Tilgå http://&amp;quot;+ WiFi.softAPIP().toString() + &amp;quot;/config for at konfigurere.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  // Vist information om konfigurationsserveren på OLED-skærmen&lt;br /&gt;
  display.clearDisplay();   // Rydder skærmen før opdatering&lt;br /&gt;
  display.setCursor(0, 0);  // Sætter cursoren i øverste venstre hjørne&lt;br /&gt;
  display.print(&amp;quot;- Config WiFi online!&amp;quot;);&lt;br /&gt;
  display.setCursor(0, 10);&lt;br /&gt;
  display.print(&amp;quot;SSID: &amp;quot; + apSSID);&lt;br /&gt;
  display.setCursor(0, 19);&lt;br /&gt;
  display.print(&amp;quot;Kodeord:&amp;quot;);&lt;br /&gt;
  display.setTextSize(2);    // Sætter skriftstørrelsen til 2 (standard er 1)&lt;br /&gt;
  display.setCursor(4, 28);&lt;br /&gt;
  display.print(apPassword); // Vis den tilfældige kode&lt;br /&gt;
  display.setTextSize(1);    // Sætter skriftstørrelsen til 1 (standard er 1)&lt;br /&gt;
  display.setCursor(0, 45);&lt;br /&gt;
  display.print(&amp;quot; IP:  &amp;quot; + WiFi.softAPIP().toString()); // Vist IP-adressen på soft-AP mode&lt;br /&gt;
  display.setCursor(0, 55);&lt;br /&gt;
  display.print(&amp;quot;http://&amp;lt;IP&amp;gt;/config&amp;quot;);&lt;br /&gt;
  display.display();        // Vis information på OLED.&lt;br /&gt;
&lt;br /&gt;
  // Vi stopper her og venter på, at brugeren gemmer en gyldig konfiguration&lt;br /&gt;
  while (strlen(ssid) == 0 || strlen(password) == 0) {&lt;br /&gt;
    delay(1000);  // Fortsæt med at vente, mens vi holder serveren aktiv&lt;br /&gt;
    server.handleClient();  // Håndter klientanmodninger under ventetid&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  // Når en gyldig konfiguration er gemt, afslut setup og gå videre til WiFi-forbindelse&lt;br /&gt;
  displayMessage(&amp;quot;Konfiguration gemt. Genstarter ESP32.&amp;quot;);&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  ESP.restart();  // Genstart for at anvende den gemte konfiguration&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void connectWiFi() { // Tilslutter til det konfigurerede Wireless netværk&lt;br /&gt;
  if (strlen(ssid) == 0 || strlen(password) == 0) {&lt;br /&gt;
    displayMessage(&amp;quot;Ugyldig WiFi config&amp;quot;);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  displayMessage(&amp;quot;Forbinder til WiFi...&amp;quot;);&lt;br /&gt;
  WiFi.begin(ssid, password);&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED &amp;amp;&amp;amp; attempts &amp;lt; 10) {&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    attempts++;&lt;br /&gt;
    displayMessage(&amp;quot;Forbinder: Forsøg &amp;quot; + String(attempts));&lt;br /&gt;
    Serial.println(&amp;quot;Forbinder til WiFi: Forsøg &amp;quot; + String(attempts));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (WiFi.status() != WL_CONNECTED) {&lt;br /&gt;
    display.clearDisplay();  // Rydder skærmen før visning&lt;br /&gt;
    display.setCursor(0, 0);&lt;br /&gt;
    display.println(&amp;quot;WiFi fejlede&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // Vis det aktuelle SSID på linje 2&lt;br /&gt;
    display.setCursor(0, 16);&lt;br /&gt;
    display.print(&amp;quot;SSID: &amp;quot;);&lt;br /&gt;
    display.print(ssid);  // Vist det aktuelle SSID&lt;br /&gt;
    &lt;br /&gt;
    display.display();  // Opdaterer skærmen&lt;br /&gt;
    Serial.println(&amp;quot;Kunne ikke forbinde til WiFi ved opstart.&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    displayMessage(&amp;quot;WiFi forbundet!&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void reconnectMQTT() { // Forsøger at oprette forbindelse til MQTT-serveren&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (!mqttClient.connected() &amp;amp;&amp;amp; attempts &amp;lt; 3) {&lt;br /&gt;
    if (mqttClient.connect(mqtt_clientID, mqtt_user, mqtt_password)) {&lt;br /&gt;
      mqttClient.publish(&amp;quot;hottubsensor/temperature&amp;quot;, &amp;quot;Forbundet til MQTT&amp;quot;);  // Send en testmeddelelse&lt;br /&gt;
    } else {&lt;br /&gt;
      Serial.print(&amp;quot;FEJL, RC=&amp;quot;);&lt;br /&gt;
      // RE 5 er f.eks. MQTT_CONNECT_UNAUTHORIZED - se https://pubsubclient.knolleary.net/api for flere fejl beskeder.&lt;br /&gt;
      Serial.print(mqttClient.state());&lt;br /&gt;
      Serial.println(&amp;quot; venter 1420 msek.&amp;quot;);&lt;br /&gt;
      delay(1420);  // Vent lidt før et nyt forsøg&lt;br /&gt;
      attempts++;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  // Hvis MQTT-forbindelsen mislykkes efter flere forsøg&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    Serial.println(&amp;quot;Kunne ikke forbinde til MQTT efter 3 forsøg.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void sendTemperatureToMQTT(float temp) { // Sender aktuel temperatur til MQTT server&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    return;  // Send kun, hvis MQTT er forbundet&lt;br /&gt;
  }&lt;br /&gt;
  char tempStr[10];                 // Midlertidig buffer&lt;br /&gt;
  dtostrf(temp, 6, 2, tempStr);     // Konverterer float til string med 2 decimaler&lt;br /&gt;
  mqttClient.publish(&amp;quot;hottubsensor/temperature&amp;quot;, tempStr);&lt;br /&gt;
  debugEnabled ? (void)Serial.println(&amp;quot;MQTT publish: &amp;quot; + String(tempStr)) : (void)0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void updateOledScreen(float temp) { // Opdatrerer OLED skærm med information under normal drift&lt;br /&gt;
  display.clearDisplay();   // Rydder skærmen før opdatering&lt;br /&gt;
  display.setCursor(0, 0);  // Sætter cursoren i øverste venstre hjørne&lt;br /&gt;
  &lt;br /&gt;
  // Vist temperatur på den første linje&lt;br /&gt;
  display.setTextSize(3);   // Sætter skriftstørrelsen til 3 (standard er 1)&lt;br /&gt;
  // display.print(&amp;quot; &amp;quot;);       // lav et mellemrum&lt;br /&gt;
  display.print(temp);      // Vis temperatur&lt;br /&gt;
  //display.print(&amp;quot; &amp;quot;);       // lav et mellemrum&lt;br /&gt;
  display.write(247);       // 247 er ASCII-koden for &#039;°&#039; i Adafruit GFX&lt;br /&gt;
  display.print(&amp;quot;C&amp;quot;);&lt;br /&gt;
  display.setTextSize(1);   // Gå tilbage til normal skriftstørrelse for de næste linjer&lt;br /&gt;
  if (debugEnabled) {&lt;br /&gt;
    char tempStr[10];                 // Midlertidig buffer&lt;br /&gt;
    dtostrf(temp, 6, 2, tempStr);     // Konverterer float til string med 2 decimaler&lt;br /&gt;
    Serial.println(&amp;quot;OLED update: &amp;quot; + String(tempStr));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Vist IP-adresse på den næste linje&lt;br /&gt;
  display.setCursor(0, 28);  // Sætter cursoren på næste linje&lt;br /&gt;
  display.print(&amp;quot;IP:   &amp;quot;);&lt;br /&gt;
  display.print(WiFi.localIP());  // Vist IP-adresse&lt;br /&gt;
  debugEnabled ? (void)Serial.println(String(&amp;quot;OLED update: IP = &amp;quot;) + WiFi.localIP().toString()) : (void)0;&lt;br /&gt;
  &lt;br /&gt;
  // Vist WiFi status på næste linje&lt;br /&gt;
  display.setCursor(0, 41);  // Sætter cursoren på den næste linje&lt;br /&gt;
  display.print(&amp;quot;WiFi: &amp;quot;);&lt;br /&gt;
  if (WiFi.status() == WL_CONNECTED) {&lt;br /&gt;
    display.print(WiFi.SSID()); // Vis SSID for aktuel WiFi&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: SSID: &amp;quot; + WiFi.SSID()) : (void)0;&lt;br /&gt;
  } else {&lt;br /&gt;
    display.print(&amp;quot;Ikke forbundet&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: WiFi: Ikke forbundet&amp;quot;) : (void)0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Vis MQTT informationer på næste linje&lt;br /&gt;
  display.setCursor(0, 53);  // Sætter cursoren på næste linje&lt;br /&gt;
  display.print(&amp;quot;MQTT: &amp;quot;);&lt;br /&gt;
  // Vis kun MQTT status , hvis mqtt_server er angivet&lt;br /&gt;
  if (strlen(mqtt_server) &amp;gt; 0) {&lt;br /&gt;
    // Hvis MQTT er aktiveret, vis status, ellers &amp;quot;Ikke forbundet&amp;quot;&lt;br /&gt;
    display.print(mqttClient.connected() ? &amp;quot;Forbundet&amp;quot; : &amp;quot;Ikke forbundet&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: MQTT: &amp;quot; + mqttClient.connected() ? &amp;quot;Forbundet&amp;quot; : &amp;quot;Ikke forbundet&amp;quot;) : (void)0;&lt;br /&gt;
  } else {&lt;br /&gt;
    display.print(&amp;quot;Ikke i brug.&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: MQTT: Ikke i brug.&amp;quot;) : (void)0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  display.display();  // Opdaterer skærmen med de nye data&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayMessage(String message) { // Skriver tekst ca. midt på OLED skærmen&lt;br /&gt;
  display.clearDisplay();             // Rydder skærmen&lt;br /&gt;
  display.setCursor(0, 24);           // Sætter cursoren ca. midt på skærmen&lt;br /&gt;
  display.println(message);           // Vist besked på skærmen&lt;br /&gt;
  display.display();                  // Opdaterer skærmen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleRoot() { // Root URL, der viser temperaturdata&lt;br /&gt;
  sensors.requestTemperatures();  // Anmoder om temperaturmåling&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);  // Henter temperatur fra den første sensor (index 0)&lt;br /&gt;
  String response;&lt;br /&gt;
&lt;br /&gt;
  if (tempC == -127.0) {&lt;br /&gt;
    response = &amp;quot;&amp;lt;p id=&#039;temperature&#039; class=&#039;error&#039;&amp;gt;Fejl: Ingen sensor fundet&amp;lt;/p&amp;gt;\n&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    response = &amp;quot;&amp;lt;div class=&#039;meter-container&#039;&amp;gt;&amp;lt;meter id=&#039;temperatur&#039; value=&#039;&amp;quot; + String(tempC) + &amp;quot;&#039; min=&#039;0&#039; max=&#039;45&#039; low=&#039;5&#039; high=&#039;37&#039; optimum=&#039;40&#039;&amp;gt;&amp;lt;/meter&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
    response += &amp;quot;&amp;lt;p id=&#039;temperature&#039; class=&#039;temperature&#039;&amp;gt;Aktuel temperatur: &amp;quot; + String(tempC) + &amp;quot; &amp;amp;deg;C&amp;lt;/p&amp;gt;\n&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String ipAddress = WiFi.localIP().toString();&lt;br /&gt;
&lt;br /&gt;
  response += &amp;quot;&amp;lt;div class=&#039;info&#039;&amp;gt;\nGå til &amp;lt;a href=&#039;http://&amp;quot; + ipAddress + &amp;quot;/reset&#039;&amp;gt;&amp;quot; + ipAddress + &amp;quot;/reset&amp;lt;/a&amp;gt; for at factory resette.&amp;lt;br /&amp;gt;\n&amp;quot;;&lt;br /&gt;
  response += &amp;quot;Gå til &amp;lt;a href=&#039;http://&amp;quot; + ipAddress + &amp;quot;/config&#039;&amp;gt;&amp;quot; + ipAddress + &amp;quot;/config&amp;lt;/a&amp;gt; for at ændre konfig.\n&amp;lt;/div&amp;gt;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  // Tilføjer HTML, CSS og JavaScript for dark mode og dynamisk temperatur-opdatering&lt;br /&gt;
  String htmlResponse = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n&amp;lt;html lang=&#039;da&#039;&amp;gt;\n&amp;lt;head&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;title&amp;gt;Hottub Temperatur&amp;lt;/title&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;style&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;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&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;h1 { color: green; font-size: 2rem; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.meter-container { margin-top: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;meter { width: 80%; max-width: 400px; height: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.temperature, .error { font-size: 1.2rem; margin-top: 10px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.error { color: red; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.info { color: silver; font-size: 1rem; margin-top: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.info.dark-mode { color: dimgray; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;a, a:visited { color: silver; text-decoration: underline; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;a.dark-mode, a.dark-mode:visited { color: dimgray; text-decoration: underline; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;button { padding: 10px 20px; margin-top: 20px; cursor: pointer; background-color: green; color: white; border: none; border-radius: 5px; font-size: 1rem; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;button:hover { background-color: darkgreen; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;body.dark-mode { background-color: black; color: green; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;body.dark-mode a { color: dimgray; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/style&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/head&amp;gt;\n&amp;lt;body&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;h1&amp;gt;Temperaturmåling&amp;lt;/h1&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += response;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;button id=&#039;themeToggle&#039; onclick=&#039;toggleDarkMode()&#039;&amp;gt;Dark Mode&amp;lt;/button&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;script&amp;gt;\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  // Dark mode javascript funktion.&lt;br /&gt;
  htmlResponse += &amp;quot;function toggleDarkMode() {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.body.classList.toggle(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  const isDarkMode = document.body.classList.contains(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  localStorage.setItem(&#039;darkMode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.getElementById(&#039;themeToggle&#039;).textContent = isDarkMode ? &#039;Light Mode&#039; : &#039;Dark Mode&#039;;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Toggler dark-mode for .info elementerne\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;.info&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.toggle(&#039;dark-mode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Toggler dark-mode for &amp;lt;a&amp;gt; elementerne\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;a&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.toggle(&#039;dark-mode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;if (localStorage.getItem(&#039;darkMode&#039;) === &#039;true&#039;) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.body.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.getElementById(&#039;themeToggle&#039;).textContent = &#039;Light Mode&#039;;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Tilføjer dark-mode til .info elementerne ved sideindlæsning, hvis dark mode er aktivt\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;.info&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Tilføjer dark-mode til &amp;lt;a&amp;gt; elementerne ved sideindlæsning, hvis dark mode er aktivt\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;a&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  // Javascript funktion til at opdatere temperatur dynamisk.&lt;br /&gt;
  htmlResponse += &amp;quot;function fetchTemperature() {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  fetch(&#039;/temperature&#039;).then(response =&amp;gt; response.text()).then(data =&amp;gt; {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    document.getElementById(&#039;temperature&#039;).innerHTML = data;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;setInterval(fetchTemperature, &amp;quot; + String(updateInterval) + &amp;quot;);\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/script&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/body&amp;gt;\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  server.send(200, &amp;quot;text/html&amp;quot;, htmlResponse);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleTemperature() { // URL til at sende temperaturdata til JavaScript opdatering af handleRoot&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  if (tempC == -127.0) {&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, &amp;quot;&amp;lt;p class=&#039;error&#039;&amp;gt;Fejl: Ingen sensor fundet&amp;lt;/p&amp;gt;&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, &amp;quot;Aktuel temperatur: &amp;quot; + String(tempC) + &amp;quot; &amp;amp;deg;C&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleReset() { // Håndterer web kald til /reset (websiden som kan bruges til at nulstille systemet)&lt;br /&gt;
  String input = server.arg(&amp;quot;reset_input&amp;quot;);  // Få input fra formularen&lt;br /&gt;
  &lt;br /&gt;
  // Tjek om input er &amp;quot;reset&amp;quot;, uanset store/små bogstaver&lt;br /&gt;
  if (input.equalsIgnoreCase(&amp;quot;reset&amp;quot;)) {&lt;br /&gt;
    server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;System nulstilles og genstartes.&amp;quot;);  // Vist besked om nulstilling&lt;br /&gt;
    resetConfig();  // Nulstil konfiguration.&lt;br /&gt;
    delay(3000);    // Vent tre sekunder.&lt;br /&gt;
    ESP.restart();  // Genstart ESP32.&lt;br /&gt;
  } else {&lt;br /&gt;
    // Hvis input ikke er korrekt, send en fejlbesked&lt;br /&gt;
    String htmlResponse = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n&amp;lt;html lang=&#039;da&#039;&amp;gt;\n&amp;lt;head&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta http-equiv=&#039;refresh&#039; content=&#039;15&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;title&amp;gt;Hottub Temperatur&amp;lt;/title&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/head&amp;gt;\n&amp;lt;body style=&#039;color: red; text-align: center;&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;h1&amp;gt;Skriv &#039;reset&#039; i tekstboksen for, at nulstille systemet.&amp;lt;/h1&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;form method=&#039;POST&#039; action=&#039;/reset&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;input type=&#039;text&#039; name=&#039;reset_input&#039; placeholder=&#039;Skriv her&#039; required&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;input type=&#039;submit&#039; value=&#039;Nulstil&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/form&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/body&amp;gt;\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
    server.send(400, &amp;quot;text/html&amp;quot;, htmlResponse);  // Send fejlmeddelelse hvis input er forkert&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleConfig() { // Håndterer web kald til /config (webside hvor systemet kan konfigureres)&lt;br /&gt;
  // Hvis det er en POST-anmodning (brugeren indsender data)&lt;br /&gt;
  if (server.method() == HTTP_POST) {&lt;br /&gt;
    // Læs konfigurationsdata fra webformularen og gem dem i variablerne&lt;br /&gt;
    strncpy(ssid, server.arg(&amp;quot;ssid&amp;quot;).c_str(), sizeof(ssid));&lt;br /&gt;
    strncpy(password, server.arg(&amp;quot;password&amp;quot;).c_str(), sizeof(password));&lt;br /&gt;
    strncpy(mqtt_server, server.arg(&amp;quot;mqtt_server&amp;quot;).c_str(), sizeof(mqtt_server));&lt;br /&gt;
    mqtt_port = server.arg(&amp;quot;mqtt_port&amp;quot;).toInt();  // Konverterer MQTT-porten fra tekst til integer&lt;br /&gt;
    if (mqtt_port &amp;lt;= 0 || mqtt_port &amp;gt; 65535) {  // Validering af portnummer&lt;br /&gt;
      mqtt_port = 1883;  // Standard MQTT-port, hvis den er ugyldig&lt;br /&gt;
    }&lt;br /&gt;
    strncpy(mqtt_user, server.arg(&amp;quot;mqtt_user&amp;quot;).c_str(), sizeof(mqtt_user));&lt;br /&gt;
    strncpy(mqtt_password, server.arg(&amp;quot;mqtt_password&amp;quot;).c_str(), sizeof(mqtt_password));&lt;br /&gt;
    &lt;br /&gt;
    // Gem konfigurationen i EEPROM&lt;br /&gt;
    saveConfig();&lt;br /&gt;
    &lt;br /&gt;
    // Bekræftelse og information&lt;br /&gt;
    server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Konfiguration gemt. Genstart ESP32.&amp;quot;);&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    ESP.restart();  // Genstart for at anvende den nye konfiguration&lt;br /&gt;
  } else {&lt;br /&gt;
    // Returner HTML-formular for konfiguration&lt;br /&gt;
    String html = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html lang=&#039;da&#039;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;&amp;lt;title&amp;gt;Konfiguration&amp;lt;/title&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;style&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f4f4f9; color: #333; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;h1 { text-align: center; color: #4CAF50; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;.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); }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;text&#039;], input[type=&#039;password&#039;], input[type=&#039;number&#039;] { width: 100%; padding: 12px; margin: 8px 0; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;submit&#039;] { background-color: #4CAF50; color: white; padding: 14px 20px; border: none; border-radius: 4px; cursor: pointer; width: 100%; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;submit&#039;]:hover { background-color: #45a049; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;label { font-weight: bold; margin-top: 10px; display: block; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;.info { text-align: center; font-size: 14px; color: #777; margin-top: 20px; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;div class=&#039;container&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;h1&amp;gt;Konfiguration&amp;lt;/h1&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;form method=&#039;POST&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;ssid&#039;&amp;gt;SSID:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;ssid&#039; name=&#039;ssid&#039; type=&#039;text&#039; required&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;password&#039;&amp;gt;Password:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;password&#039; name=&#039;password&#039; type=&#039;password&#039; required&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_server&#039;&amp;gt;MQTT Server:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_server&#039; name=&#039;mqtt_server&#039; type=&#039;text&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_port&#039;&amp;gt;MQTT Port:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_port&#039; name=&#039;mqtt_port&#039; type=&#039;number&#039; value=&#039;1883&#039; min=&#039;1&#039; max=&#039;65535&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_user&#039;&amp;gt;MQTT User:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_user&#039; name=&#039;mqtt_user&#039; type=&#039;text&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_password&#039;&amp;gt;MQTT Password:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_password&#039; name=&#039;mqtt_password&#039; type=&#039;password&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;input type=&#039;submit&#039; value=&#039;Gem Konfiguration&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/form&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;div class=&#039;info&#039;&amp;gt;Undlad MQTT-information, hvis den ikke skal bruges.&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, html);  // Sender formularen som HTML&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void resetConfig() { // Nulstiller al konfigurations data&lt;br /&gt;
  memset(ssid, 0, sizeof(ssid));&lt;br /&gt;
  memset(password, 0, sizeof(password));&lt;br /&gt;
  memset(mqtt_server, 0, sizeof(mqtt_server));&lt;br /&gt;
  memset(mqtt_user, 0, sizeof(mqtt_user));&lt;br /&gt;
  memset(mqtt_password, 0, sizeof(mqtt_password));&lt;br /&gt;
  mqtt_port = 1883;  // Default port&lt;br /&gt;
&lt;br /&gt;
  // Initialiser EEPROM med den ønskede størrelse&lt;br /&gt;
  if (!EEPROM.begin(EEPROM_SIZE)) {&lt;br /&gt;
    Serial.println(&amp;quot;EEPROM initialization failed.&amp;quot;);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Rydder EEPROM ved at skrive 0xFF til hver byte (fjerner gamle data)&lt;br /&gt;
  for (int i = 0; i &amp;lt; EEPROM_SIZE; i++) {&lt;br /&gt;
    EEPROM.write(i, 0xFF);  // Write 0xFF to each byte&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  Serial.println(&amp;quot;Skriver til alle adresser.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  saveConfig();  // Funktion til at gemme den opdaterede (tomme) konfiguration til EEPROM&lt;br /&gt;
&lt;br /&gt;
  display.clearDisplay();  // Rydder skærmen&lt;br /&gt;
  display.setTextSize(2);   // Sætter skriftstørrelsen til 2&lt;br /&gt;
  display.println(&amp;quot;WiFi og MQTT&amp;quot;);&lt;br /&gt;
  display.println(&amp;quot; nulstillet.&amp;quot;);&lt;br /&gt;
  display.println(&amp;quot;Genstarter!&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;WiFi og MQTT nulstillet. Genstarter ESP32.&amp;quot;);&lt;br /&gt;
  delay(3000);&lt;br /&gt;
  ESP.restart();  // Restart the ESP32&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void saveConfig() { // Gemmer konfigurationen i EEPROM&lt;br /&gt;
  EEPROM.put(0, ssid);&lt;br /&gt;
  EEPROM.put(32, password);&lt;br /&gt;
  EEPROM.put(64, mqtt_server);&lt;br /&gt;
  EEPROM.put(128, mqtt_port);&lt;br /&gt;
  EEPROM.put(132, mqtt_user);&lt;br /&gt;
  EEPROM.put(164, mqtt_password);&lt;br /&gt;
  EEPROM.commit();  // Bekræft gemt data&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void readConfig() { // Læs konfigurationen fra EEPROM&lt;br /&gt;
  EEPROM.get(0, ssid);&lt;br /&gt;
  EEPROM.get(32, password);&lt;br /&gt;
  EEPROM.get(64, mqtt_server);&lt;br /&gt;
  EEPROM.get(128, mqtt_port);&lt;br /&gt;
  EEPROM.get(132, mqtt_user);&lt;br /&gt;
  EEPROM.get(164, mqtt_password);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Diverse links ==&lt;br /&gt;
* [https://samueladesola.medium.com/how-to-set-up-esp32-wroom-32-b2100060470c How to set up ESP32-WROOM-329] in Arduino IDE.&lt;br /&gt;
&lt;br /&gt;
== Afslutning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Arduino]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Skift_IP_til_Flemmings_WireGuard_server.&amp;diff=2079</id>
		<title>Skift IP til Flemmings WireGuard server.</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Skift_IP_til_Flemmings_WireGuard_server.&amp;diff=2079"/>
		<updated>2025-11-22T12:44:17Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Skift IP til Flemmings WireGuard VPN Server ==&lt;br /&gt;
&lt;br /&gt;
* Start WireGuard app.  &lt;br /&gt;
* Tryk på det lille “større end” tegn ud for den server der skal rettes.  &lt;br /&gt;
[[File:Press_greater-than.png|frameless|555px|Press greater than]]&lt;br /&gt;
* Tryk på “Edit”.  &lt;br /&gt;
[[File:Press_edit.png|frameless|555px|Press edit]]&lt;br /&gt;
* Scroll ned til “Peer” sektionen.  &lt;br /&gt;
* Her skal Endpoint rettes til:  &lt;br /&gt;
* 188.228.&#039;&#039;&#039;89.227&#039;&#039;&#039;:xxxxxx  &lt;br /&gt;
* Hvor xxxxx skal være uændret hos dig!  &lt;br /&gt;
* Tryk “Save”  &lt;br /&gt;
[[File:Press_save.png|frameless|555px|Press save]]&lt;br /&gt;
* Tryk “WireGuard”  &lt;br /&gt;
[[File:Press_wireguard.png|frameless|555px|Press wireguard]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Test om det virker…&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Skift_IP_til_Flemmings_WireGuard_server.&amp;diff=2078</id>
		<title>Skift IP til Flemmings WireGuard server.</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Skift_IP_til_Flemmings_WireGuard_server.&amp;diff=2078"/>
		<updated>2025-11-22T12:43:24Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Skift IP til Flemmings WireGuard VPN Server ==&lt;br /&gt;
&lt;br /&gt;
* Start WireGuard app.  &lt;br /&gt;
* Tryk på det lille “større end” tegn ud for den server der skal rettes.  &lt;br /&gt;
[[File:Press_greater-than.png|frameless|555px|Press greater than]]&lt;br /&gt;
* Tryk på “Edit”.  &lt;br /&gt;
[[File:Press_edit.png|frameless|555px|Press edit]]&lt;br /&gt;
* Scroll ned til “Peer” sektionen.  &lt;br /&gt;
* Her skal Endpoint rettes til:  &lt;br /&gt;
* 188.228.&#039;&#039;&#039;89.227&#039;&#039;&#039;:xxxxxx  &lt;br /&gt;
* Hvor xxxxx skal være uændret.  &lt;br /&gt;
* Tryk “Save”  &lt;br /&gt;
[[File:Press_save.png|frameless|555px|Press save]]&lt;br /&gt;
* Tryk “WireGuard”  &lt;br /&gt;
[[File:Press_wireguard.png|frameless|555px|Press wireguard]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Test om det virker…&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Skift_IP_til_Flemmings_WireGuard_server.&amp;diff=2077</id>
		<title>Skift IP til Flemmings WireGuard server.</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Skift_IP_til_Flemmings_WireGuard_server.&amp;diff=2077"/>
		<updated>2025-11-22T12:42:12Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Skift IP til Flemmings WireGuard VPN Server ==&lt;br /&gt;
&lt;br /&gt;
* Start WireGuard app.  &lt;br /&gt;
* Tryk på det lille “større end” tegn ud for den server der skal rettes.  &lt;br /&gt;
[[File:Press_greater-than.png|frameless|333px|Press greater than]]&lt;br /&gt;
* Tryk på “Edit”.  &lt;br /&gt;
[[File:Press_edit.png|frameless|333px|Press edit]]&lt;br /&gt;
* Scroll ned til “Peer” sektionen.  &lt;br /&gt;
* Her skal Endpoint rettes til:  &lt;br /&gt;
* 188.228.&#039;&#039;&#039;89.227&#039;&#039;&#039;:xxxxxx  &lt;br /&gt;
* Hvor xxxxx skal være uændret.  &lt;br /&gt;
* Tryk “Save”  &lt;br /&gt;
[[File:Press_save.png|frameless|333px|Press save]]&lt;br /&gt;
* Tryk “WireGuard”  &lt;br /&gt;
[[File:Press_wireguard.png|frameless|333px|Press wireguard]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Test om det virker…&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Skift_IP_til_Flemmings_WireGuard_server.&amp;diff=2076</id>
		<title>Skift IP til Flemmings WireGuard server.</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Skift_IP_til_Flemmings_WireGuard_server.&amp;diff=2076"/>
		<updated>2025-11-22T12:40:21Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Skift IP til Flemmings WireGuard VPN Server ==&lt;br /&gt;
&lt;br /&gt;
* Start WireGuard app.  &lt;br /&gt;
* Tryk på det lille “større end” tegn ud for den server der skal rettes.  &lt;br /&gt;
[[File:Press_greater-than.png|frameless|200px|Press greater than]]&lt;br /&gt;
* Tryk på “Edit”.  &lt;br /&gt;
[[File:Press_edit.png|frameless|200px|Press edit]]&lt;br /&gt;
* Scroll ned til “Peer” sektionen.  &lt;br /&gt;
* Her skal Endpoint rettes til:  &lt;br /&gt;
* 188.228.&#039;&#039;&#039;89.227&#039;&#039;&#039;:xxxxxx  &lt;br /&gt;
* Hvor xxxxx skal være uændret.  &lt;br /&gt;
* Tryk “Save”  &lt;br /&gt;
[[File:Press_save.png|frameless|200px|Press save]]&lt;br /&gt;
* Tryk “WireGuard”  &lt;br /&gt;
[[File:Press_wireguard.png|frameless|200px|Press wireguard]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Test om det virker…&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=File:Press_wireguard.png&amp;diff=2075</id>
		<title>File:Press wireguard.png</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=File:Press_wireguard.png&amp;diff=2075"/>
		<updated>2025-11-22T12:18:47Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=File:Press_save.png&amp;diff=2074</id>
		<title>File:Press save.png</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=File:Press_save.png&amp;diff=2074"/>
		<updated>2025-11-22T12:18:26Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=File:Press_edit.png&amp;diff=2073</id>
		<title>File:Press edit.png</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=File:Press_edit.png&amp;diff=2073"/>
		<updated>2025-11-22T12:17:55Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=File:Press_greater-than.png&amp;diff=2072</id>
		<title>File:Press greater-than.png</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=File:Press_greater-than.png&amp;diff=2072"/>
		<updated>2025-11-22T12:16:43Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Skift_IP_til_Flemmings_WireGuard_server.&amp;diff=2071</id>
		<title>Skift IP til Flemmings WireGuard server.</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Skift_IP_til_Flemmings_WireGuard_server.&amp;diff=2071"/>
		<updated>2025-11-22T11:51:30Z</updated>

		<summary type="html">&lt;p&gt;Admin: Created page with &amp;quot;.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Arc_Ariders_Server_Slam_weekend&amp;diff=2070</id>
		<title>Arc Ariders Server Slam weekend</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Arc_Ariders_Server_Slam_weekend&amp;diff=2070"/>
		<updated>2025-10-23T12:07:01Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Gaming]][[Category:Arc Raiders]]&lt;br /&gt;
&lt;br /&gt;
=== &#039;&#039;&#039;Statistics and estimated data&#039;&#039;&#039; ===&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
! Category !! Data / Estimate !! Comment&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Time window&#039;&#039;&#039; || Friday 15:00 – Monday 09:00 (≈ 66 hours) || Worldwide open beta &amp;quot;Server Slam&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Peak concurrent players (Steam)&#039;&#039;&#039; || 189,668 || According to SteamDB (one-week view)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated average concurrent players (Steam)&#039;&#039;&#039; || ≈ 95,000 || Assumed roughly half of peak&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Total rounds played&#039;&#039;&#039; || 30,000,000 || Official event statistic&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated total player-participations&#039;&#039;&#039; || 300–600 million participations || 10–20 players × 30M rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Average players per hour (aggregate participations)&#039;&#039;&#039; || ≈ 4.5 – 9.0 million players/hour || 300–600M participations ÷ 66 hours&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Total ARC units destroyed&#039;&#039;&#039; || 58,000,000 || Official statistic&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Average ARC destroyed per round&#039;&#039;&#039; || ≈ 1.93 || 58M ÷ 30M rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Deadliest ARC&#039;&#039;&#039; || The Rocketeer || 2,356,295 knockdowns&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Knockdowns caused by The Queen&#039;&#039;&#039; || 108,000 || Boss encounters&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Knockdowns caused by Lightning&#039;&#039;&#039; || 26,676 || Environmental hazard&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Knockdowns while playing Recorder&#039;&#039;&#039; || 2,669 || Humorous easter egg&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Total recorded knockdowns (approx.)&#039;&#039;&#039; || ≈ 2,493,640 || Summed from the listed knockdown figures&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Average deaths per round&#039;&#039;&#039; || ≈ 0.083 || About 1 death every 12 rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Most crafted weapon&#039;&#039;&#039; || Ferro || Official stat&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Number of Recorders found&#039;&#039;&#039; || Over 96,000 || Collectible items found&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Rubber ducks collected&#039;&#039;&#039; || 816,162 || ≈ 1 duck per 37 rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated rounds per hour&#039;&#039;&#039; || ≈ 454,500 rounds/hour || 30M ÷ 66 hours&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated concurrent players per hour (all platforms)&#039;&#039;&#039; || ≈ 4.5 – 9.0 million || 454,500 rounds/hour × 10–20 players/round&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Primary activity window&#039;&#039;&#039; || Saturday (majority of play) || SteamDB shows highest sustained peaks on Saturday&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Share of rounds played on Saturday (estimate)&#039;&#039;&#039; || 50–60 % || ≈ 15–17 million rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Fun facts&#039;&#039;&#039; || &amp;gt;2,600 players knocked down while playing the Recorder || Community had fun with easter eggs&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;ARC per player per round (estimate)&#039;&#039;&#039; || ≈ 0.1 – 0.2 ARC/player/round || 58M ARC ÷ (30M rounds × 10–20 players)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====== Sources: ======&lt;br /&gt;
* Embark Studios – [https://arcraiders.com/en/news/server-slam-thank-you/ ARC Raiders Server Slam figures]&lt;br /&gt;
* SteamDB.info ([https://steamdb.info/app/2427520/ concurrent-player graph])&lt;br /&gt;
* Own estimates based on the event duration and the guestimated 10–20 players per round.&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Arc_Ariders_Server_Slam_weekend&amp;diff=2069</id>
		<title>Arc Ariders Server Slam weekend</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Arc_Ariders_Server_Slam_weekend&amp;diff=2069"/>
		<updated>2025-10-23T12:06:17Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Gaming]][[Category:Arc Raiders]]== &#039;&#039;&#039;Statistics and estimated data&#039;&#039;&#039; ==&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
! Category !! Data / Estimate !! Comment&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Time window&#039;&#039;&#039; || Friday 15:00 – Monday 09:00 (≈ 66 hours) || Worldwide open beta &amp;quot;Server Slam&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Peak concurrent players (Steam)&#039;&#039;&#039; || 189,668 || According to SteamDB (one-week view)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated average concurrent players (Steam)&#039;&#039;&#039; || ≈ 95,000 || Assumed roughly half of peak&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Total rounds played&#039;&#039;&#039; || 30,000,000 || Official event statistic&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated total player-participations&#039;&#039;&#039; || 300–600 million participations || 10–20 players × 30M rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Average players per hour (aggregate participations)&#039;&#039;&#039; || ≈ 4.5 – 9.0 million players/hour || 300–600M participations ÷ 66 hours&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Total ARC units destroyed&#039;&#039;&#039; || 58,000,000 || Official statistic&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Average ARC destroyed per round&#039;&#039;&#039; || ≈ 1.93 || 58M ÷ 30M rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Deadliest ARC&#039;&#039;&#039; || The Rocketeer || 2,356,295 knockdowns&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Knockdowns caused by The Queen&#039;&#039;&#039; || 108,000 || Boss encounters&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Knockdowns caused by Lightning&#039;&#039;&#039; || 26,676 || Environmental hazard&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Knockdowns while playing Recorder&#039;&#039;&#039; || 2,669 || Humorous easter egg&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Total recorded knockdowns (approx.)&#039;&#039;&#039; || ≈ 2,493,640 || Summed from the listed knockdown figures&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Average deaths per round&#039;&#039;&#039; || ≈ 0.083 || About 1 death every 12 rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Most crafted weapon&#039;&#039;&#039; || Ferro || Official stat&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Number of Recorders found&#039;&#039;&#039; || Over 96,000 || Collectible items found&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Rubber ducks collected&#039;&#039;&#039; || 816,162 || ≈ 1 duck per 37 rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated rounds per hour&#039;&#039;&#039; || ≈ 454,500 rounds/hour || 30M ÷ 66 hours&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated concurrent players per hour (all platforms)&#039;&#039;&#039; || ≈ 4.5 – 9.0 million || 454,500 rounds/hour × 10–20 players/round&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Primary activity window&#039;&#039;&#039; || Saturday (majority of play) || SteamDB shows highest sustained peaks on Saturday&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Share of rounds played on Saturday (estimate)&#039;&#039;&#039; || 50–60 % || ≈ 15–17 million rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Fun facts&#039;&#039;&#039; || &amp;gt;2,600 players knocked down while playing the Recorder || Community had fun with easter eggs&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;ARC per player per round (estimate)&#039;&#039;&#039; || ≈ 0.1 – 0.2 ARC/player/round || 58M ARC ÷ (30M rounds × 10–20 players)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====== Sources: ======&lt;br /&gt;
* Embark Studios – [https://arcraiders.com/en/news/server-slam-thank-you/ ARC Raiders Server Slam figures]&lt;br /&gt;
* SteamDB.info ([https://steamdb.info/app/2427520/ concurrent-player graph])&lt;br /&gt;
* Own estimates based on the event duration and the guestimated 10–20 players per round.&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Category:Arc_Raiders&amp;diff=2068</id>
		<title>Category:Arc Raiders</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Category:Arc_Raiders&amp;diff=2068"/>
		<updated>2025-10-23T11:56:30Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Stuph &#039;bout &#039;&#039;[https://arcraiders.com Arc Raiders]&#039;&#039;...&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Category:Arc_Raiders&amp;diff=2067</id>
		<title>Category:Arc Raiders</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Category:Arc_Raiders&amp;diff=2067"/>
		<updated>2025-10-23T11:54:48Z</updated>

		<summary type="html">&lt;p&gt;Admin: Created page with &amp;quot;Stuph &amp;#039;bout &amp;#039;&amp;#039;Arc Raiders&amp;#039;&amp;#039;...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Stuph &#039;bout &#039;&#039;Arc Raiders&#039;&#039;...&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Arc_Ariders_Server_Slam_weekend&amp;diff=2066</id>
		<title>Arc Ariders Server Slam weekend</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Arc_Ariders_Server_Slam_weekend&amp;diff=2066"/>
		<updated>2025-10-23T11:53:37Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Gaming]][[Category:Arc Raiders]]== &#039;&#039;&#039;Statistics and estimated data&#039;&#039;&#039; ==&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
! Category !! Data / Estimate !! Comment&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Time window&#039;&#039;&#039; || Friday 15:00 – Monday 09:00 (≈ 66 hours) || Worldwide open beta &amp;quot;Server Slam&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Peak concurrent players (Steam)&#039;&#039;&#039; || 189,668 || According to SteamDB (one-week view)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated average concurrent players (Steam)&#039;&#039;&#039; || ≈ 95,000 || Assumed roughly half of peak&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Total rounds played&#039;&#039;&#039; || 30,000,000 || Official event statistic&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated total player-participations&#039;&#039;&#039; || 300–600 million participations || 10–20 players × 30M rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Average players per hour (aggregate participations)&#039;&#039;&#039; || ≈ 4.5 – 9.0 million players/hour || 300–600M participations ÷ 66 hours&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Total ARC units destroyed&#039;&#039;&#039; || 58,000,000 || Official statistic&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Average ARC destroyed per round&#039;&#039;&#039; || ≈ 1.93 || 58M ÷ 30M rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Deadliest ARC&#039;&#039;&#039; || The Rocketeer || 2,356,295 knockdowns&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Knockdowns caused by The Queen&#039;&#039;&#039; || 108,000 || Boss encounters&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Knockdowns caused by Lightning&#039;&#039;&#039; || 26,676 || Environmental hazard&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Knockdowns while playing Recorder&#039;&#039;&#039; || 2,669 || Humorous easter egg&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Total recorded knockdowns (approx.)&#039;&#039;&#039; || ≈ 2,493,640 || Summed from the listed knockdown figures&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Average deaths per round&#039;&#039;&#039; || ≈ 0.083 || About 1 death every 12 rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Most crafted weapon&#039;&#039;&#039; || Ferro || Official stat&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Number of Recorders found&#039;&#039;&#039; || Over 96,000 || Collectible items found&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Rubber ducks collected&#039;&#039;&#039; || 816,162 || ≈ 1 duck per 37 rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated rounds per hour&#039;&#039;&#039; || ≈ 454,500 rounds/hour || 30M ÷ 66 hours&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated concurrent players per hour (all platforms)&#039;&#039;&#039; || ≈ 4.5 – 9.0 million || 454,500 rounds/hour × 10–20 players/round&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Primary activity window&#039;&#039;&#039; || Saturday (majority of play) || SteamDB shows highest sustained peaks on Saturday&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Share of rounds played on Saturday (estimate)&#039;&#039;&#039; || 50–60 % || ≈ 15–17 million rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Fun facts&#039;&#039;&#039; || &amp;gt;2,600 players knocked down while playing the Recorder || Community had fun with easter eggs&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;ARC per player per round (estimate)&#039;&#039;&#039; || ≈ 0.1 – 0.2 ARC/player/round || 58M ARC ÷ (30M rounds × 10–20 players)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====== Sources: ======&lt;br /&gt;
* Embark Studios – [https://cdn.discordapp.com/attachments/1107613897357275188/1430579292877885593/ArcRaiders_KeyArt_SlamInfographic_16-9_Final.jpg?ex=68faf30a&amp;amp;is=68f9a18a&amp;amp;hm=c66a9a3baad3a205f82537857f2f9ecf1e4325b7080a57e14a7aec2ae4a50bb2 ARC Raiders Server Slam figures]&lt;br /&gt;
* SteamDB.info ([https://steamdb.info/app/2427520/ concurrent-player graph])&lt;br /&gt;
* Own estimates based on the event duration and the guestimated 10–20 players per round.&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Arc_Ariders_Server_Slam_weekend&amp;diff=2065</id>
		<title>Arc Ariders Server Slam weekend</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Arc_Ariders_Server_Slam_weekend&amp;diff=2065"/>
		<updated>2025-10-23T11:52:15Z</updated>

		<summary type="html">&lt;p&gt;Admin: links to datasources&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== &#039;&#039;&#039;Statistics and estimated data&#039;&#039;&#039; ==&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
! Category !! Data / Estimate !! Comment&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Time window&#039;&#039;&#039; || Friday 15:00 – Monday 09:00 (≈ 66 hours) || Worldwide open beta &amp;quot;Server Slam&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Peak concurrent players (Steam)&#039;&#039;&#039; || 189,668 || According to SteamDB (one-week view)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated average concurrent players (Steam)&#039;&#039;&#039; || ≈ 95,000 || Assumed roughly half of peak&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Total rounds played&#039;&#039;&#039; || 30,000,000 || Official event statistic&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated total player-participations&#039;&#039;&#039; || 300–600 million participations || 10–20 players × 30M rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Average players per hour (aggregate participations)&#039;&#039;&#039; || ≈ 4.5 – 9.0 million players/hour || 300–600M participations ÷ 66 hours&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Total ARC units destroyed&#039;&#039;&#039; || 58,000,000 || Official statistic&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Average ARC destroyed per round&#039;&#039;&#039; || ≈ 1.93 || 58M ÷ 30M rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Deadliest ARC&#039;&#039;&#039; || The Rocketeer || 2,356,295 knockdowns&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Knockdowns caused by The Queen&#039;&#039;&#039; || 108,000 || Boss encounters&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Knockdowns caused by Lightning&#039;&#039;&#039; || 26,676 || Environmental hazard&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Knockdowns while playing Recorder&#039;&#039;&#039; || 2,669 || Humorous easter egg&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Total recorded knockdowns (approx.)&#039;&#039;&#039; || ≈ 2,493,640 || Summed from the listed knockdown figures&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Average deaths per round&#039;&#039;&#039; || ≈ 0.083 || About 1 death every 12 rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Most crafted weapon&#039;&#039;&#039; || Ferro || Official stat&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Number of Recorders found&#039;&#039;&#039; || Over 96,000 || Collectible items found&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Rubber ducks collected&#039;&#039;&#039; || 816,162 || ≈ 1 duck per 37 rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated rounds per hour&#039;&#039;&#039; || ≈ 454,500 rounds/hour || 30M ÷ 66 hours&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated concurrent players per hour (all platforms)&#039;&#039;&#039; || ≈ 4.5 – 9.0 million || 454,500 rounds/hour × 10–20 players/round&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Primary activity window&#039;&#039;&#039; || Saturday (majority of play) || SteamDB shows highest sustained peaks on Saturday&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Share of rounds played on Saturday (estimate)&#039;&#039;&#039; || 50–60 % || ≈ 15–17 million rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Fun facts&#039;&#039;&#039; || &amp;gt;2,600 players knocked down while playing the Recorder || Community had fun with easter eggs&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;ARC per player per round (estimate)&#039;&#039;&#039; || ≈ 0.1 – 0.2 ARC/player/round || 58M ARC ÷ (30M rounds × 10–20 players)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====== Sources: ======&lt;br /&gt;
* Embark Studios – [https://cdn.discordapp.com/attachments/1107613897357275188/1430579292877885593/ArcRaiders_KeyArt_SlamInfographic_16-9_Final.jpg?ex=68faf30a&amp;amp;is=68f9a18a&amp;amp;hm=c66a9a3baad3a205f82537857f2f9ecf1e4325b7080a57e14a7aec2ae4a50bb2 ARC Raiders Server Slam figures]&lt;br /&gt;
* SteamDB.info ([https://steamdb.info/app/2427520/ concurrent-player graph])&lt;br /&gt;
* Own estimates based on the event duration and the guestimated 10–20 players per round.&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Arc_Ariders_Server_Slam_weekend&amp;diff=2064</id>
		<title>Arc Ariders Server Slam weekend</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Arc_Ariders_Server_Slam_weekend&amp;diff=2064"/>
		<updated>2025-10-23T11:45:02Z</updated>

		<summary type="html">&lt;p&gt;Admin: Created page with &amp;quot;{| class=&amp;quot;wikitable sortable&amp;quot; |+ &amp;#039;&amp;#039;&amp;#039;ARC Raiders – Server Slam statistics and estimated data&amp;#039;&amp;#039;&amp;#039; ! Category !! Data / Estimate !! Comment |- | &amp;#039;&amp;#039;&amp;#039;Time window&amp;#039;&amp;#039;&amp;#039; || Friday 15:00 – Monday 09:00 (≈ 66 hours) || Worldwide open beta &amp;quot;Server Slam&amp;quot; |- | &amp;#039;&amp;#039;&amp;#039;Peak concurrent players (Steam)&amp;#039;&amp;#039;&amp;#039; || 189,668 || According to SteamDB (one-week view) |- | &amp;#039;&amp;#039;&amp;#039;Estimated average concurrent players (Steam)&amp;#039;&amp;#039;&amp;#039; || ≈ 95,000 || Assumed roughly half of peak |- | &amp;#039;&amp;#039;&amp;#039;Total rounds played&amp;#039;&amp;#039;&amp;#039; ||...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;ARC Raiders – Server Slam statistics and estimated data&#039;&#039;&#039;&lt;br /&gt;
! Category !! Data / Estimate !! Comment&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Time window&#039;&#039;&#039; || Friday 15:00 – Monday 09:00 (≈ 66 hours) || Worldwide open beta &amp;quot;Server Slam&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Peak concurrent players (Steam)&#039;&#039;&#039; || 189,668 || According to SteamDB (one-week view)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated average concurrent players (Steam)&#039;&#039;&#039; || ≈ 95,000 || Assumed roughly half of peak&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Total rounds played&#039;&#039;&#039; || 30,000,000 || Official event statistic&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated total player-participations&#039;&#039;&#039; || 300–600 million participations || 10–20 players × 30M rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Average players per hour (aggregate participations)&#039;&#039;&#039; || ≈ 4.5 – 9.0 million players/hour || 300–600M participations ÷ 66 hours&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Total ARC units destroyed&#039;&#039;&#039; || 58,000,000 || Official statistic&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Average ARC destroyed per round&#039;&#039;&#039; || ≈ 1.93 || 58M ÷ 30M rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Deadliest ARC&#039;&#039;&#039; || The Rocketeer || 2,356,295 knockdowns&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Knockdowns caused by The Queen&#039;&#039;&#039; || 108,000 || Boss encounters&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Knockdowns caused by Lightning&#039;&#039;&#039; || 26,676 || Environmental hazard&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Knockdowns while playing Recorder&#039;&#039;&#039; || 2,669 || Humorous easter egg&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Total recorded knockdowns (approx.)&#039;&#039;&#039; || ≈ 2,493,640 || Summed from the listed knockdown figures&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Average deaths per round&#039;&#039;&#039; || ≈ 0.083 || About 1 death every 12 rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Most crafted weapon&#039;&#039;&#039; || Ferro || Official stat&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Number of Recorders found&#039;&#039;&#039; || Over 96,000 || Collectible items found&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Rubber ducks collected&#039;&#039;&#039; || 816,162 || ≈ 1 duck per 37 rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated rounds per hour&#039;&#039;&#039; || ≈ 454,500 rounds/hour || 30M ÷ 66 hours&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Estimated concurrent players per hour (all platforms)&#039;&#039;&#039; || ≈ 4.5 – 9.0 million || 454,500 rounds/hour × 10–20 players/round&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Primary activity window&#039;&#039;&#039; || Saturday (majority of play) || SteamDB shows highest sustained peaks on Saturday&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Share of rounds played on Saturday (estimate)&#039;&#039;&#039; || 50–60 % || ≈ 15–17 million rounds&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Fun facts&#039;&#039;&#039; || &amp;gt;2,600 players knocked down while playing the Recorder || Community had fun with easter eggs&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;ARC per player per round (estimate)&#039;&#039;&#039; || ≈ 0.1 – 0.2 ARC/player/round || 58M ARC ÷ (30M rounds × 10–20 players)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Sources:&lt;br /&gt;
* Embark Studios – ARC Raiders (Server Slam figures)&lt;br /&gt;
* SteamDB.info (concurrent-player graph)&lt;br /&gt;
* Own estimates based on the event duration and the guestimated 10–20 players per round.&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Main_Page&amp;diff=2063</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Main_Page&amp;diff=2063"/>
		<updated>2025-10-20T06:48:02Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&amp;lt;!-- START TABLE --&amp;gt;&lt;br /&gt;
{| style=&amp;quot;width: 100%; margin:4px 0 0 0; background:none; border-spacing: 0px;&amp;quot;&lt;br /&gt;
| class=&amp;quot;MainPageBG&amp;quot; style=&amp;quot;width:40%; border:1px solid #cef2e0; background:#f5fffa; vertical-align:top; color:#000;&amp;quot; |&lt;br /&gt;
{| style=&amp;quot;vertical-align:top; background:#f5fffa; width:100%&amp;quot;&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center; white-space:nowrap; color:#000;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-size:162%; border:none; margin:0; padding:.1em; color:#000;&amp;quot;&amp;gt;Welcome to&amp;lt;/div&amp;gt;&amp;lt;span style=&amp;quot;font-size:142%; border:none; margin:0; padding:.1em; color:#000;&amp;quot;&amp;gt;munkjensen.net/wiki&amp;lt;/span&amp;gt;&lt;br /&gt;
[[File:TiredGuardianAngel.png|frameless|Tired guardian angel.]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;top:+0.2em; font-size:95%;&amp;quot;&amp;gt;the encyclopedia that only &#039;&#039;&#039;I&#039;&#039;&#039; can edit.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;!-- Highlights --&amp;gt;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;padding:2px;&amp;quot; | &amp;lt;h2 style=&amp;quot;margin:3px; background:#cef2e0; font-size:120%; font-weight:bold; border:1px solid #a3bfb1; text-align:left; color:#000; padding:0.2em 0.4em;&amp;quot;&amp;gt;Highlights&amp;lt;/h2&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;color:#000; padding:2px 5px 5px; width:50%;&amp;quot; |&lt;br /&gt;
* [[RainViewer]]&lt;br /&gt;
* [[DMI Vejr Ikast|DMI Weather Ikast]]&lt;br /&gt;
* [[Ender 5 Plus + Klipper]].&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- CATEGORIES --&amp;gt;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;padding:2px;&amp;quot; |&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2 style=&amp;quot;margin:3px; background:#cedff2; font-size:120%; font-weight:bold; border:1px solid #a3bfb1; text-align:left; color:#000; padding:0.2em 0.4em;&amp;quot;&amp;gt;[[:Special:Categories|Categories]]&amp;lt;/h2&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;color:#000; padding:2px 5px 5px; width:50%;&amp;quot; style=&amp;quot;color:#000; padding:2px 5px 5px;&amp;quot;| &lt;br /&gt;
* [[:Category:Apple|Apple]].&lt;br /&gt;
* [[:Category:Pi-Hole|Pi-Hole]].&lt;br /&gt;
* [[:Category:Windows|Windows]].&lt;br /&gt;
* [[: category:Elgato_Stream_Deck| Elgato Stream Deck]].&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Main_Page&amp;diff=2062</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Main_Page&amp;diff=2062"/>
		<updated>2025-10-20T06:42:46Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&amp;lt;!-- START TABLE --&amp;gt;&lt;br /&gt;
{| style=&amp;quot;width: 100%; margin:4px 0 0 0; background:none; border-spacing: 0px;&amp;quot;&lt;br /&gt;
| class=&amp;quot;MainPageBG&amp;quot; style=&amp;quot;width:40%; border:1px solid #cef2e0; background:#f5fffa; vertical-align:top; color:#000;&amp;quot; |&lt;br /&gt;
{| style=&amp;quot;vertical-align:top; background:#f5fffa; width:100%&amp;quot;&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center; white-space:nowrap; color:#000;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-size:162%; border:none; margin:0; padding:.1em; color:#000;&amp;quot;&amp;gt;Welcome to&amp;lt;/div&amp;gt;&amp;lt;span style=&amp;quot;font-size:142%; border:none; margin:0; padding:.1em; color:#000;&amp;quot;&amp;gt;munkjensen.net/wiki&amp;lt;/span&amp;gt;&lt;br /&gt;
[[File:TiredGuardianAngel.png|frameless|Tired guardian angel.]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;top:+0.2em; font-size:95%;&amp;quot;&amp;gt;the encyclopedia that only &#039;&#039;&#039;I&#039;&#039;&#039; can edit.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;!-- Highlights --&amp;gt;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;padding:2px;&amp;quot; | &amp;lt;h2 style=&amp;quot;margin:3px; background:#cef2e0; font-size:120%; font-weight:bold; border:1px solid #a3bfb1; text-align:left; color:#000; padding:0.2em 0.4em;&amp;quot;&amp;gt;Highlights&amp;lt;/h2&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;color:#000; padding:2px 5px 5px; width:50%;&amp;quot; |  &lt;br /&gt;
* [[El spotpris]]&lt;br /&gt;
* [[RainViewer]]&lt;br /&gt;
* [[DMI Vejr Ikast|DMI Weather Ikast]]&lt;br /&gt;
* [[Ender 5 Plus + Klipper]].&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- CATEGORIES --&amp;gt;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;padding:2px;&amp;quot; |&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2 style=&amp;quot;margin:3px; background:#cedff2; font-size:120%; font-weight:bold; border:1px solid #a3bfb1; text-align:left; color:#000; padding:0.2em 0.4em;&amp;quot;&amp;gt;[[:Special:Categories|Categories]]&amp;lt;/h2&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;color:#000; padding:2px 5px 5px; width:50%;&amp;quot; style=&amp;quot;color:#000; padding:2px 5px 5px;&amp;quot;| &lt;br /&gt;
* [[:Category:Apple|Apple]].&lt;br /&gt;
* [[:Category:Pi-Hole|Pi-Hole]].&lt;br /&gt;
* [[:Category:Windows|Windows]].&lt;br /&gt;
* [[: category:Elgato_Stream_Deck| Elgato Stream Deck]].&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Enabling_Redis_for_PHP_on_Synology_NAS_(Manual_Method)&amp;diff=2061</id>
		<title>Enabling Redis for PHP on Synology NAS (Manual Method)</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Enabling_Redis_for_PHP_on_Synology_NAS_(Manual_Method)&amp;diff=2061"/>
		<updated>2025-05-11T14:59:20Z</updated>

		<summary type="html">&lt;p&gt;Admin: Created page with &amp;quot;= Enabling Redis for PHP on Synology NAS (Manual Method) =  This page describes how to manually enable Redis support for PHP (v8.2) on a Synology NAS using the built-in PHP package and Docker-based Redis server.  == Background == Synology does not officially include the `redis` PHP extension in its default PHP packages. Attempting to use Redis from PHP results in the following fatal error: &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt; Fatal error: Uncaught Error: Class &amp;quot;Redis&amp;quot; not found...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Enabling Redis for PHP on Synology NAS (Manual Method) =&lt;br /&gt;
&lt;br /&gt;
This page describes how to manually enable Redis support for PHP (v8.2) on a Synology NAS using the built-in PHP package and Docker-based Redis server.&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
Synology does not officially include the `redis` PHP extension in its default PHP packages. Attempting to use Redis from PHP results in the following fatal error:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
Fatal error: Uncaught Error: Class &amp;quot;Redis&amp;quot; not found&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
* PHP 8.2 installed from Synology Package Center.&lt;br /&gt;
* A running Redis server (e.g., using Docker or a separate host).&lt;br /&gt;
* SSH access to your Synology NAS.&lt;br /&gt;
* Redis PHP extension installed manually or available (e.g., copied from PECL build).&lt;br /&gt;
&lt;br /&gt;
== Solution ==&lt;br /&gt;
To make Synology&#039;s PHP interpreter recognize the Redis extension, you need to manually add it to the `extension_list.json` file used by Synology’s PHP infrastructure.&lt;br /&gt;
&lt;br /&gt;
=== Steps ===&lt;br /&gt;
# SSH into your NAS.&lt;br /&gt;
# Open the PHP 8.2 extension configuration file:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo vi /volume1/@appstore/PHP8.2/misc/extension_list.json&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Add the following block at the end of the JSON list (inside the outermost `{}`):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
    ,&lt;br /&gt;
    &amp;quot;redis&amp;quot;: {&lt;br /&gt;
        &amp;quot;enable_default&amp;quot;: false,&lt;br /&gt;
        &amp;quot;desc&amp;quot;: &amp;quot;This module is not officially supported by Synology and may be removed by updates&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Save the file and exit the editor.&lt;br /&gt;
&lt;br /&gt;
== Important Notes ==&lt;br /&gt;
* The Redis extension must already be available in the appropriate PHP `ext` directory (e.g., `/var/packages/PHP8.2/target/usr/local/lib/php82/modules/redis.so`).&lt;br /&gt;
* After modifying `extension_list.json`, go to **Web Station** or **PHP Settings** and enable the `redis` extension.&lt;br /&gt;
* This configuration may be **overwritten by DSM updates**, so it should be re-checked after system upgrades.&lt;br /&gt;
&lt;br /&gt;
== Source ==&lt;br /&gt;
Synology Community Forum thread: https://community.synology.com/enu/forum/69/post/161908&lt;br /&gt;
&lt;br /&gt;
== Status ==&lt;br /&gt;
Tested working as of May 2025 on DSM 7.x and PHP 8.2.&lt;br /&gt;
&lt;br /&gt;
[[Category:DS918+]]&lt;br /&gt;
[[Category:Synology]]&lt;br /&gt;
[[Category:NextCloud]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=My_bitcoin_wallet&amp;diff=2060</id>
		<title>My bitcoin wallet</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=My_bitcoin_wallet&amp;diff=2060"/>
		<updated>2024-12-24T12:11:41Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Donations */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Donations ===&lt;br /&gt;
&lt;br /&gt;
You are most welcome to donate [https://en.wikipedia.org/wiki/Bitcoin bitcoins] to me, if you found anything on this wiki so helpfull that you want to express your appreciation financially.&lt;br /&gt;
&lt;br /&gt;
My wallet key is: [bitcoin:1F3DoQC6tDVYKphTq1XLMGWYs5msTBQTZC bitcoin:1F3DoQC6tDVYKphTq1XLMGWYs5msTBQTZC]&lt;br /&gt;
&lt;br /&gt;
If bitcoins are not your thing then you can [https://www.buymeacoffee.com/zuQ6aQ0ZES buy me a coffee] &amp;lt;3&lt;br /&gt;
&lt;br /&gt;
=== General disclaimer ===&lt;br /&gt;
&lt;br /&gt;
All information found on the domain http://munkjensen.net is provided without any warranty of any kind... everything here is written by me for my own use. Feel free to try out anything you find here at your own risk ;-)&lt;br /&gt;
=== Contact information ===&lt;br /&gt;
&lt;br /&gt;
* Reddit: [https://www.reddit.com/user/DaneMonnik/ u/DaneMonnik]&lt;br /&gt;
* Secure mail: Use [[My PGP Public Key]].&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2059</id>
		<title>Temperaturføler med ESP32, DS18B20 og OLED-skærm</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2059"/>
		<updated>2024-12-18T16:18:17Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Indledning */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Indledning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Projektet inkluderer nem konfiguration af opkobling til Wi-Fi og der er mulighed for, at sende data til en MQTT-server.&lt;br /&gt;
&lt;br /&gt;
== Indkøbsliste ==&lt;br /&gt;
* [https://arduinotech.dk/shop/esp32-usb-c-iot-mainboard-devkitc-cp2102-38pin/ ESP32 USB-C IoT bundkort DevKitC CP2102 38PIN]&lt;br /&gt;
* [https://arduinotech.dk/shop/0-96%e2%80%b3-i2c-iic-spi-serial-128x64-oled-new-model/ 0,96&amp;quot; I2C 128x64 OLED-display]&lt;br /&gt;
* [https://arduinotech.dk/shop/ds18b20-waterproof-digital-thermal-probe-with-3-5mm-mini-audio-jack-1-meter-cable/ DS18B20 vandtæt digital termisk sonde (1 meter kabel)]&lt;br /&gt;
* [https://arduinotech.dk/shop/tsp-xx-replace-hlk-pmxx-ac-dc-220v-to-12v-buck-step-down-power-supply-module-converter/ HLK-PMxx 220VAC til 5VDC Step Down Converter]&lt;br /&gt;
* [https://arduinotech.dk/shop/sonoff-ip66-waterproof-case/ Sonoff IP66 vandtæt etui]&lt;br /&gt;
* [https://arduinotech.dk/shop/prototype-pcb-print/ Prototype PCB print - Type 1]&lt;br /&gt;
&lt;br /&gt;
== Byggevejledning ==&lt;br /&gt;
&lt;br /&gt;
1. Tilslutninger:  &lt;br /&gt;
   - DS18B20:  &lt;br /&gt;
     - VCC (rød)  → 3.3V på ESP32  &lt;br /&gt;
     - GND (sort) → GND på ESP32  &lt;br /&gt;
     - Data (gul) → GPIO 4 på ESP32  &lt;br /&gt;
     - Tilføj en 4,7kΩ pull-up modstand mellem Data og VCC.&lt;br /&gt;
&lt;br /&gt;
   - OLED-skærm (I2C):  &lt;br /&gt;
     - VCC → 3.3V på ESP32  &lt;br /&gt;
     - GND → GND  på ESP32  &lt;br /&gt;
     - SDA → GPIO 21 på ESP32  &lt;br /&gt;
     - SCL → GPIO 22 på ESP32  &lt;br /&gt;
&lt;br /&gt;
2. Strømforsyning:  &lt;br /&gt;
   Tilslut ESP32 og OLED til 5V strømforsyning via HLK-PMxx-modulet.&lt;br /&gt;
&lt;br /&gt;
3. Montering:  &lt;br /&gt;
   - Montér komponenterne på prototype-PCB.  &lt;br /&gt;
   - Placér hele opsætningen i Sonoff IP66 vandtæt etui for beskyttelse mod vejr og støv.&lt;br /&gt;
&lt;br /&gt;
== Kode til ESP32 ==&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed wikitable&amp;quot;&lt;br /&gt;
! Kopier denne kode til Arduino IDE --&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
/* README plz...&lt;br /&gt;
   Benyt &amp;quot;uPesy ESP32 Wroom DevKit&amp;quot; som board model til kompilering.&lt;br /&gt;
   Jeg har oplevet, at andre har fejl der får LED til at blinke for hurtigt !?!&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;WiFi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;WebServer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;OneWire.h&amp;gt;&lt;br /&gt;
#include &amp;lt;DallasTemperature.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_GFX.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_SSD1306.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;&lt;br /&gt;
#include &amp;lt;EEPROM.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// === PIN-konfigurationer ===&lt;br /&gt;
#define ONE_WIRE_BUS   4  // Pin til DS18B20 temperatursensoren.&lt;br /&gt;
#define SCREEN_WIDTH 128  // Skærmens bredde (for OLED).&lt;br /&gt;
#define SCREEN_HEIGHT 64  // Skærmens højde (for OLED).&lt;br /&gt;
#define OLED_RESET    -1  // Reset pin til OLED (sættes til -1, da vi ikke bruger en fysisk reset pin).&lt;br /&gt;
#define EEPROM_SIZE 4096  // Størrelsen af EEPROM (kan gemme op til 4096 byte).&lt;br /&gt;
#define RST_PIN       13  // Pin for RST-knappen.&lt;br /&gt;
#define LED_BUILTIN    2  // Pin for LED.&lt;br /&gt;
&lt;br /&gt;
// === Globale objekter ===&lt;br /&gt;
OneWire oneWire(ONE_WIRE_BUS);        // Initialiserer OneWire-bus til DS18B20.&lt;br /&gt;
DallasTemperature sensors(&amp;amp;oneWire);  // Opretter et objekt til at håndtere temperaturmålinger.&lt;br /&gt;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &amp;amp;Wire, OLED_RESET);  // OLED-skærm til at vise informationer.&lt;br /&gt;
WebServer server(80);                 // Webserver på port 80 til at håndtere HTTP-anmodninger.&lt;br /&gt;
WiFiClient espClient;                 // WiFi klient til MQTT.&lt;br /&gt;
PubSubClient mqttClient(espClient);   // MQTT-klient, der bruger WiFi klienten.&lt;br /&gt;
&lt;br /&gt;
// === Globale variabler ===&lt;br /&gt;
unsigned long lastCommsCheck = 0;          // Tidspunkt for sidste Comms-tjek.&lt;br /&gt;
unsigned long CommsCheckInterval = 60000;  // Tidsinterval for Comms-tjek (60 sekunder) angivet i millisekunder.&lt;br /&gt;
unsigned long lastLEDCheck = 0;            // Tidspunkt for sidste LED-tjek.&lt;br /&gt;
unsigned long LEDCheckInterval = 2000;     // Tidsinterval for LED-tjek (2 sekunder) angivet i millisekunder.&lt;br /&gt;
unsigned long lastMQTTpublish = 0;         // Tidspunkt for sidste MQTT-publish.&lt;br /&gt;
unsigned long MQTTPublishInterval = 15000; // Tidsinterval for MQTT publish (15 sekunder) angivet i millisekunder.&lt;br /&gt;
bool debugEnabled = false;                 // Skal der udskriver til seriel port?&lt;br /&gt;
int updateInterval = 15000;                // Variabel til 15 sekunders opdateringsinterval angivet i millisekunder.&lt;br /&gt;
&lt;br /&gt;
// === Netværksindstillinger ===&lt;br /&gt;
char ssid[32] = &amp;quot;&amp;quot;;      // SSID til WiFi netværket.&lt;br /&gt;
char password[32] = &amp;quot;&amp;quot;;  // WiFi adgangskode.&lt;br /&gt;
&lt;br /&gt;
// === MQTT-indstillinger ===&lt;br /&gt;
char mqtt_server[128] = &amp;quot;&amp;quot;;    // MQTT-serverens IP eller domæne.&lt;br /&gt;
int mqtt_port = 1883;          // MQTT-port (standard 1883).&lt;br /&gt;
char mqtt_user[128] = &amp;quot;&amp;quot;;      // MQTT-brugernavn.&lt;br /&gt;
char mqtt_password[128] = &amp;quot;&amp;quot;;  // MQTT-adgangskode.&lt;br /&gt;
char mqtt_clientID[128] = &amp;quot;&amp;quot;;  // MQTT-clientID.&lt;br /&gt;
&lt;br /&gt;
void setup() { // Initiel programmatiske konfigurations kode. &lt;br /&gt;
  pinMode(RST_PIN, INPUT);        // Initialiser RST_PIN som et INPUT.&lt;br /&gt;
  pinMode(LED_BUILTIN, OUTPUT);   // Initialiser den blå LED.&lt;br /&gt;
  pinMode(RST_PIN, INPUT_PULLUP); // Aktiver intern pull-up modstand.&lt;br /&gt;
  digitalWrite(LED_BUILTIN, LOW); // Sluk den blå LED.&lt;br /&gt;
  Serial.begin(115200);           // Initialiserer seriel kommunikation til debugging.&lt;br /&gt;
  EEPROM.begin(EEPROM_SIZE);      // Initialiserer EEPROM for at gemme konfigurationer.&lt;br /&gt;
  sensors.begin();                // Starter temperatursensoren.&lt;br /&gt;
  lastCommsCheck = millis();      // Sætter første tidsværdi af variablen.&lt;br /&gt;
  lastLEDCheck = millis();        // Sætter første tidsværdi af variablen.&lt;br /&gt;
  lastMQTTpublish = millis();     // Sætter første tidsværdi af variablen.&lt;br /&gt;
  &lt;br /&gt;
  // OLED-initialisering&lt;br /&gt;
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {  // Tjekker om OLED-skærmen er korrekt tilsluttet&lt;br /&gt;
    Serial.println(F(&amp;quot;OLED fejl!&amp;quot;));&lt;br /&gt;
    for (;;);  // Stopper programmet, hvis skærmen ikke kan initialiseres&lt;br /&gt;
  }&lt;br /&gt;
  display.clearDisplay();  // Rydder skærmen&lt;br /&gt;
  display.setTextSize(1);  // Sætter tekststørrelse&lt;br /&gt;
  display.setTextColor(WHITE);  // Sætter tekstfarve til hvid&lt;br /&gt;
&lt;br /&gt;
  // Tjek om RST-knappen er trykket under opstart&lt;br /&gt;
  if (digitalRead(RST_PIN) == LOW) {  // Hvis pin er LOW&lt;br /&gt;
    Serial.println(&amp;quot;RST-knappen trykket. Nulstiller WiFi og MQTT konfiguration.&amp;quot;);&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    resetConfig();  // Nulstil konfiguration&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readConfig();                   // Læs gemte konfigurationer fra EEPROM&lt;br /&gt;
  if (strlen(ssid) == 0 || strlen(password) == 0) { // Hvis ingen konfiguration er gemt, start konfigurationsmode&lt;br /&gt;
    displayMessage(&amp;quot;WiFi konfig mangler.\n\nStarter konfig WiFi.&amp;quot;);&lt;br /&gt;
    setupConfigServer();          // Starter konfigurationsserveren, så brugeren kan konfigurere WiFi og MQTT&lt;br /&gt;
    } else {&lt;br /&gt;
    // WiFi-forbindelse&lt;br /&gt;
    connectWiFi();                // Forbinder til WiFi, hvis SSID og adgangskode er gemt&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Webserver-ruter&lt;br /&gt;
  server.on(&amp;quot;/&amp;quot;, handleRoot);                   // Root URL, der viser temperaturdata&lt;br /&gt;
  server.on(&amp;quot;/temperature&amp;quot;, handleTemperature); // URL til at sende temperaturdata til JavaScript opdatering af handleRoot&lt;br /&gt;
  server.on(&amp;quot;/config&amp;quot;, handleConfig);           // URL til at konfigurere WiFi og MQTT&lt;br /&gt;
  server.on(&amp;quot;/reset&amp;quot;, handleReset);             // URL til at nulstille WiFi&lt;br /&gt;
  server.begin();  // Starter webserveren&lt;br /&gt;
&lt;br /&gt;
  if (strlen(mqtt_server) &amp;gt; 0) { // start kun MQTT, hvis &amp;quot;mqtt_server&amp;quot; er konfigureret og gemt.&lt;br /&gt;
    // Hent MAC-adressen&lt;br /&gt;
    String mac = WiFi.macAddress();                         // Eksempel: &amp;quot;24:6F:28:1A:2B:3C&amp;quot;&lt;br /&gt;
    Serial.println(mac);&lt;br /&gt;
    mac.replace(&amp;quot;:&amp;quot;,&amp;quot;&amp;quot;);                                    // fjern kolon&#039;er&lt;br /&gt;
    String lastSixChars = mac.substring(mac.length() - 6);  // Gem de sidste 6 tegn - Resultat: &amp;quot;2B:3C&amp;quot;&lt;br /&gt;
    String mqtt_clientID = &amp;quot;hottub-&amp;quot; + lastSixChars;        // Generer klient-id baseret på de sidste 6 bogstaver og tal wifi mac adresse&lt;br /&gt;
    mqttClient.setServer(mqtt_server, mqtt_port);&lt;br /&gt;
    if (!mqttClient.connected()) {                          // HVis MQTT ikke er forbundet,&lt;br /&gt;
      reconnectMQTT();                                      // Forsøg at oprette forbindelse til MQTT.&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_server   = &amp;quot;) + mqtt_server);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_port     = &amp;quot;) + mqtt_port);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt-klientID = &amp;quot;) + mqtt_clientID);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_user     = &amp;quot;) + mqtt_user);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_password = &amp;quot;) + mqtt_password);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot; - Debug beskeder kan aktiveres på seriel port med &#039;debug on&#039; eller &#039;debug off&#039;(standard).&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot; - Systemet kan nulstilles ved at sende &#039;nullify system&#039; på seriel porten.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() { // Her placeres koden der skal eksekveres kontinuerligt&lt;br /&gt;
  server.handleClient();                                   // Lad webserveren behandle indkommende HTTP-anmodninger.&lt;br /&gt;
  sensors.requestTemperatures();                           // Anmoder om temperaturmåling.&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);                // Hente temperatur fra den første sensor (index 0).&lt;br /&gt;
  updateOledScreen(tempC);                                 // Opdaterer OLED-skærmen.&lt;br /&gt;
  &lt;br /&gt;
  if (millis() - lastMQTTpublish &amp;gt;= MQTTPublishInterval) { // Send MQTT publish hvis det er tid til det (hver 15. sekunder)&lt;br /&gt;
    if (strlen(mqtt_server) &amp;gt; 0) {                         // Send kun MQTT, hvis mqtt_server er angivet&lt;br /&gt;
      if (mqttClient.connected()) {                        // Send temperaturdata til MQTT, hvis forbindelsen er aktiv&lt;br /&gt;
        sendTemperatureToMQTT(tempC);&lt;br /&gt;
      }&lt;br /&gt;
      lastMQTTpublish = millis();                          // Opdater lastMQTTpublish variablen til nu.&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if (millis() - lastCommsCheck &amp;gt;= CommsCheckInterval) {   // Tjek forbindelser hvis det er tid til det (hver 60. sekunder)&lt;br /&gt;
    if (WiFi.status() != WL_CONNECTED) {                   // Hvis WiFi ikke er forbundet,&lt;br /&gt;
      connectWiFi();                                       // Forsøg at oprette forbindelse til WiFi.&lt;br /&gt;
    }&lt;br /&gt;
    if (strlen(mqtt_server) &amp;gt; 0) {                         // Tjek kun MQTT, hvis mqtt_server er angivet&lt;br /&gt;
      if (!mqttClient.connected()) {                       // HVis MQTT ikke er forbundet,&lt;br /&gt;
        reconnectMQTT();                                   // Forsøg at oprette forbindelse til MQTT.&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    lastCommsCheck = millis();                             // Opdater lastCommsCheck variablen til nu.&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (millis() - lastLEDCheck &amp;gt;= LEDCheckInterval) {      // Blinke funktion til indikation af at loop() køre&lt;br /&gt;
&lt;br /&gt;
    // Serial.print(millis());&lt;br /&gt;
    // Serial.print(&amp;quot; - &amp;quot;);&lt;br /&gt;
    // Serial.print(lastLEDCheck);&lt;br /&gt;
    // Serial.print(&amp;quot; = &amp;quot;);&lt;br /&gt;
    // Serial.println(millis() - lastLEDCheck);&lt;br /&gt;
    // Serial.println(&amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // Læs den nuværende tilstand af LED_BUILTIN og sæt den modsatte værdi.&lt;br /&gt;
    lastLEDCheck = millis();                              // Opdater tidspunktet for LED status ændring&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (Serial.available() &amp;gt; 0) {                           // Håndter seriel kommando linje&lt;br /&gt;
    handleSerialInput();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleSerialInput() { // Håndter seriel kommando linje&lt;br /&gt;
  String input = Serial.readStringUntil(&#039;\n&#039;);  // Læs indtil linjeskift&lt;br /&gt;
  input.trim();  // Fjern eventuelle mellemrum eller linjeskift&lt;br /&gt;
&lt;br /&gt;
  if (input == &amp;quot;debug on&amp;quot;) {&lt;br /&gt;
    debugEnabled = true;&lt;br /&gt;
    Serial.println(&amp;quot;Debug mode aktiveret.&amp;quot;);&lt;br /&gt;
  } &lt;br /&gt;
  else if (input == &amp;quot;debug off&amp;quot;) {&lt;br /&gt;
    debugEnabled = false;&lt;br /&gt;
    Serial.println(&amp;quot;Debug mode deaktiveret.&amp;quot;);&lt;br /&gt;
  } &lt;br /&gt;
  else if (input == &amp;quot;nullify system&amp;quot;) {&lt;br /&gt;
    Serial.println(&amp;quot;Systemet nulstilles om 42 sekunder!&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;SLUK HVIS DET VAR EN FEJL-KOMMANDO!&amp;quot;);&lt;br /&gt;
    int seconds =42;&lt;br /&gt;
    while (seconds &amp;gt; 0) {&lt;br /&gt;
      Serial.print(&amp;quot;Tid tilbage: &amp;quot;);&lt;br /&gt;
      Serial.print(seconds);&lt;br /&gt;
      Serial.println(&amp;quot; sekunder&amp;quot;);&lt;br /&gt;
      delay(1000);  // Vent 1 sekund&lt;br /&gt;
      seconds--;&lt;br /&gt;
    }&lt;br /&gt;
    resetConfig();&lt;br /&gt;
  } &lt;br /&gt;
  else {&lt;br /&gt;
    Serial.println(&amp;quot;--&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;Ugyldig kommando!&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;--&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;&#039;debug on&#039; eller &#039;debug off&#039; tænder / slukker for debug beskeder.&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;&#039;nullify system&#039; nulstiller til fabriksinstillinger.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupConfigServer() { // Starter soft-AP (WiFi access point) til konfiguration af systemet&lt;br /&gt;
  String apSSID = &amp;quot;Hottub_Config&amp;quot;;  // SSID for AP&lt;br /&gt;
  &lt;br /&gt;
  // Generer en tilfældig kode mellem 100000000 og 999999999&lt;br /&gt;
  long randomCode = random(100000000, 1000000000);&lt;br /&gt;
  String apPassword = String(randomCode);  // Konverter tallet til en streng&lt;br /&gt;
  &lt;br /&gt;
  // Start softAP&lt;br /&gt;
  WiFi.softAP(apSSID.c_str(), apPassword.c_str());&lt;br /&gt;
  &lt;br /&gt;
  // Vent et øjeblik for at sikre, at access pointet er startet&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  &lt;br /&gt;
  // Print IP-adresse for AP&lt;br /&gt;
  Serial.println(&amp;quot;Soft-AP startet.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  server.on(&amp;quot;/config&amp;quot;, handleConfig);  // Håndterer konfigurationsanmodninger&lt;br /&gt;
  server.begin();  // Starter webserveren&lt;br /&gt;
  Serial.println(&amp;quot;#############################&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;Konfigurationsserver startet.&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;SSID: &amp;quot; + apSSID);&lt;br /&gt;
  Serial.println(&amp;quot;Kode: &amp;quot; + apPassword);&lt;br /&gt;
  Serial.println(&amp;quot;Tilgå http://&amp;quot;+ WiFi.softAPIP().toString() + &amp;quot;/config for at konfigurere.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  // Vist information om konfigurationsserveren på OLED-skærmen&lt;br /&gt;
  display.clearDisplay();   // Rydder skærmen før opdatering&lt;br /&gt;
  display.setCursor(0, 0);  // Sætter cursoren i øverste venstre hjørne&lt;br /&gt;
  display.print(&amp;quot;- Config WiFi online!&amp;quot;);&lt;br /&gt;
  display.setCursor(0, 10);&lt;br /&gt;
  display.print(&amp;quot;SSID: &amp;quot; + apSSID);&lt;br /&gt;
  display.setCursor(0, 19);&lt;br /&gt;
  display.print(&amp;quot;Kodeord:&amp;quot;);&lt;br /&gt;
  display.setTextSize(2);    // Sætter skriftstørrelsen til 2 (standard er 1)&lt;br /&gt;
  display.setCursor(4, 28);&lt;br /&gt;
  display.print(apPassword); // Vis den tilfældige kode&lt;br /&gt;
  display.setTextSize(1);    // Sætter skriftstørrelsen til 1 (standard er 1)&lt;br /&gt;
  display.setCursor(0, 45);&lt;br /&gt;
  display.print(&amp;quot; IP:  &amp;quot; + WiFi.softAPIP().toString()); // Vist IP-adressen på soft-AP mode&lt;br /&gt;
  display.setCursor(0, 55);&lt;br /&gt;
  display.print(&amp;quot;http://&amp;lt;IP&amp;gt;/config&amp;quot;);&lt;br /&gt;
  display.display();        // Vis information på OLED.&lt;br /&gt;
&lt;br /&gt;
  // Vi stopper her og venter på, at brugeren gemmer en gyldig konfiguration&lt;br /&gt;
  while (strlen(ssid) == 0 || strlen(password) == 0) {&lt;br /&gt;
    delay(1000);  // Fortsæt med at vente, mens vi holder serveren aktiv&lt;br /&gt;
    server.handleClient();  // Håndter klientanmodninger under ventetid&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  // Når en gyldig konfiguration er gemt, afslut setup og gå videre til WiFi-forbindelse&lt;br /&gt;
  displayMessage(&amp;quot;Konfiguration gemt. Genstarter ESP32.&amp;quot;);&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  ESP.restart();  // Genstart for at anvende den gemte konfiguration&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void connectWiFi() { // Tilslutter til det konfigurerede Wireless netværk&lt;br /&gt;
  if (strlen(ssid) == 0 || strlen(password) == 0) {&lt;br /&gt;
    displayMessage(&amp;quot;Ugyldig WiFi config&amp;quot;);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  displayMessage(&amp;quot;Forbinder til WiFi...&amp;quot;);&lt;br /&gt;
  WiFi.begin(ssid, password);&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED &amp;amp;&amp;amp; attempts &amp;lt; 10) {&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    attempts++;&lt;br /&gt;
    displayMessage(&amp;quot;Forbinder: Forsøg &amp;quot; + String(attempts));&lt;br /&gt;
    Serial.println(&amp;quot;Forbinder til WiFi: Forsøg &amp;quot; + String(attempts));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (WiFi.status() != WL_CONNECTED) {&lt;br /&gt;
    display.clearDisplay();  // Rydder skærmen før visning&lt;br /&gt;
    display.setCursor(0, 0);&lt;br /&gt;
    display.println(&amp;quot;WiFi fejlede&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // Vis det aktuelle SSID på linje 2&lt;br /&gt;
    display.setCursor(0, 16);&lt;br /&gt;
    display.print(&amp;quot;SSID: &amp;quot;);&lt;br /&gt;
    display.print(ssid);  // Vist det aktuelle SSID&lt;br /&gt;
    &lt;br /&gt;
    display.display();  // Opdaterer skærmen&lt;br /&gt;
    Serial.println(&amp;quot;Kunne ikke forbinde til WiFi ved opstart.&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    displayMessage(&amp;quot;WiFi forbundet!&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void reconnectMQTT() { // Forsøger at oprette forbindelse til MQTT-serveren&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (!mqttClient.connected() &amp;amp;&amp;amp; attempts &amp;lt; 3) {&lt;br /&gt;
    if (mqttClient.connect(mqtt_clientID, mqtt_user, mqtt_password)) {&lt;br /&gt;
      mqttClient.publish(&amp;quot;hottubsensor/temperature&amp;quot;, &amp;quot;Forbundet til MQTT&amp;quot;);  // Send en testmeddelelse&lt;br /&gt;
    } else {&lt;br /&gt;
      Serial.print(&amp;quot;FEJL, RC=&amp;quot;);&lt;br /&gt;
      // RE 5 er f.eks. MQTT_CONNECT_UNAUTHORIZED - se https://pubsubclient.knolleary.net/api for flere fejl beskeder.&lt;br /&gt;
      Serial.print(mqttClient.state());&lt;br /&gt;
      Serial.println(&amp;quot; venter 1420 msek.&amp;quot;);&lt;br /&gt;
      delay(1420);  // Vent lidt før et nyt forsøg&lt;br /&gt;
      attempts++;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  // Hvis MQTT-forbindelsen mislykkes efter flere forsøg&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    Serial.println(&amp;quot;Kunne ikke forbinde til MQTT efter 3 forsøg.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void sendTemperatureToMQTT(float temp) { // Sender aktuel temperatur til MQTT server&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    return;  // Send kun, hvis MQTT er forbundet&lt;br /&gt;
  }&lt;br /&gt;
  char tempStr[10];                 // Midlertidig buffer&lt;br /&gt;
  dtostrf(temp, 6, 2, tempStr);     // Konverterer float til string med 2 decimaler&lt;br /&gt;
  mqttClient.publish(&amp;quot;hottubsensor/temperature&amp;quot;, tempStr);&lt;br /&gt;
  debugEnabled ? (void)Serial.println(&amp;quot;MQTT publish: &amp;quot; + String(tempStr)) : (void)0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void updateOledScreen(float temp) { // Opdatrerer OLED skærm med information under normal drift&lt;br /&gt;
  display.clearDisplay();   // Rydder skærmen før opdatering&lt;br /&gt;
  display.setCursor(0, 0);  // Sætter cursoren i øverste venstre hjørne&lt;br /&gt;
  &lt;br /&gt;
  // Vist temperatur på den første linje&lt;br /&gt;
  display.setTextSize(3);   // Sætter skriftstørrelsen til 3 (standard er 1)&lt;br /&gt;
  // display.print(&amp;quot; &amp;quot;);       // lav et mellemrum&lt;br /&gt;
  display.print(temp);      // Vis temperatur&lt;br /&gt;
  //display.print(&amp;quot; &amp;quot;);       // lav et mellemrum&lt;br /&gt;
  display.write(247);       // 247 er ASCII-koden for &#039;°&#039; i Adafruit GFX&lt;br /&gt;
  display.print(&amp;quot;C&amp;quot;);&lt;br /&gt;
  display.setTextSize(1);   // Gå tilbage til normal skriftstørrelse for de næste linjer&lt;br /&gt;
  if (debugEnabled) {&lt;br /&gt;
    char tempStr[10];                 // Midlertidig buffer&lt;br /&gt;
    dtostrf(temp, 6, 2, tempStr);     // Konverterer float til string med 2 decimaler&lt;br /&gt;
    Serial.println(&amp;quot;OLED update: &amp;quot; + String(tempStr));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Vist IP-adresse på den næste linje&lt;br /&gt;
  display.setCursor(0, 28);  // Sætter cursoren på næste linje&lt;br /&gt;
  display.print(&amp;quot;IP:   &amp;quot;);&lt;br /&gt;
  display.print(WiFi.localIP());  // Vist IP-adresse&lt;br /&gt;
  debugEnabled ? (void)Serial.println(String(&amp;quot;OLED update: IP = &amp;quot;) + WiFi.localIP().toString()) : (void)0;&lt;br /&gt;
  &lt;br /&gt;
  // Vist WiFi status på næste linje&lt;br /&gt;
  display.setCursor(0, 41);  // Sætter cursoren på den næste linje&lt;br /&gt;
  display.print(&amp;quot;WiFi: &amp;quot;);&lt;br /&gt;
  if (WiFi.status() == WL_CONNECTED) {&lt;br /&gt;
    display.print(WiFi.SSID()); // Vis SSID for aktuel WiFi&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: SSID: &amp;quot; + WiFi.SSID()) : (void)0;&lt;br /&gt;
  } else {&lt;br /&gt;
    display.print(&amp;quot;Ikke forbundet&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: WiFi: Ikke forbundet&amp;quot;) : (void)0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Vis MQTT informationer på næste linje&lt;br /&gt;
  display.setCursor(0, 53);  // Sætter cursoren på næste linje&lt;br /&gt;
  display.print(&amp;quot;MQTT: &amp;quot;);&lt;br /&gt;
  // Vis kun MQTT status , hvis mqtt_server er angivet&lt;br /&gt;
  if (strlen(mqtt_server) &amp;gt; 0) {&lt;br /&gt;
    // Hvis MQTT er aktiveret, vis status, ellers &amp;quot;Ikke forbundet&amp;quot;&lt;br /&gt;
    display.print(mqttClient.connected() ? &amp;quot;Forbundet&amp;quot; : &amp;quot;Ikke forbundet&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: MQTT: &amp;quot; + mqttClient.connected() ? &amp;quot;Forbundet&amp;quot; : &amp;quot;Ikke forbundet&amp;quot;) : (void)0;&lt;br /&gt;
  } else {&lt;br /&gt;
    display.print(&amp;quot;Ikke i brug.&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: MQTT: Ikke i brug.&amp;quot;) : (void)0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  display.display();  // Opdaterer skærmen med de nye data&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayMessage(String message) { // Skriver tekst ca. midt på OLED skærmen&lt;br /&gt;
  display.clearDisplay();             // Rydder skærmen&lt;br /&gt;
  display.setCursor(0, 24);           // Sætter cursoren ca. midt på skærmen&lt;br /&gt;
  display.println(message);           // Vist besked på skærmen&lt;br /&gt;
  display.display();                  // Opdaterer skærmen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleRoot() { // Root URL, der viser temperaturdata&lt;br /&gt;
  sensors.requestTemperatures();  // Anmoder om temperaturmåling&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);  // Henter temperatur fra den første sensor (index 0)&lt;br /&gt;
  String response;&lt;br /&gt;
&lt;br /&gt;
  if (tempC == -127.0) {&lt;br /&gt;
    response = &amp;quot;&amp;lt;p id=&#039;temperature&#039; class=&#039;error&#039;&amp;gt;Fejl: Ingen sensor fundet&amp;lt;/p&amp;gt;\n&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    response = &amp;quot;&amp;lt;div class=&#039;meter-container&#039;&amp;gt;&amp;lt;meter id=&#039;temperatur&#039; value=&#039;&amp;quot; + String(tempC) + &amp;quot;&#039; min=&#039;0&#039; max=&#039;45&#039; low=&#039;5&#039; high=&#039;37&#039; optimum=&#039;40&#039;&amp;gt;&amp;lt;/meter&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
    response += &amp;quot;&amp;lt;p id=&#039;temperature&#039; class=&#039;temperature&#039;&amp;gt;Aktuel temperatur: &amp;quot; + String(tempC) + &amp;quot; &amp;amp;deg;C&amp;lt;/p&amp;gt;\n&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String ipAddress = WiFi.localIP().toString();&lt;br /&gt;
&lt;br /&gt;
  response += &amp;quot;&amp;lt;div class=&#039;info&#039;&amp;gt;\nGå til &amp;lt;a href=&#039;http://&amp;quot; + ipAddress + &amp;quot;/reset&#039;&amp;gt;&amp;quot; + ipAddress + &amp;quot;/reset&amp;lt;/a&amp;gt; for at factory resette.&amp;lt;br /&amp;gt;\n&amp;quot;;&lt;br /&gt;
  response += &amp;quot;Gå til &amp;lt;a href=&#039;http://&amp;quot; + ipAddress + &amp;quot;/config&#039;&amp;gt;&amp;quot; + ipAddress + &amp;quot;/config&amp;lt;/a&amp;gt; for at ændre konfig.\n&amp;lt;/div&amp;gt;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  // Tilføjer HTML, CSS og JavaScript for dark mode og dynamisk temperatur-opdatering&lt;br /&gt;
  String htmlResponse = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n&amp;lt;html lang=&#039;da&#039;&amp;gt;\n&amp;lt;head&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;title&amp;gt;Hottub Temperatur&amp;lt;/title&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;style&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;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&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;h1 { color: green; font-size: 2rem; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.meter-container { margin-top: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;meter { width: 80%; max-width: 400px; height: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.temperature, .error { font-size: 1.2rem; margin-top: 10px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.error { color: red; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.info { color: silver; font-size: 1rem; margin-top: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.info.dark-mode { color: dimgray; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;a, a:visited { color: silver; text-decoration: underline; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;a.dark-mode, a.dark-mode:visited { color: dimgray; text-decoration: underline; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;button { padding: 10px 20px; margin-top: 20px; cursor: pointer; background-color: green; color: white; border: none; border-radius: 5px; font-size: 1rem; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;button:hover { background-color: darkgreen; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;body.dark-mode { background-color: black; color: green; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;body.dark-mode a { color: dimgray; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/style&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/head&amp;gt;\n&amp;lt;body&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;h1&amp;gt;Temperaturmåling&amp;lt;/h1&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += response;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;button id=&#039;themeToggle&#039; onclick=&#039;toggleDarkMode()&#039;&amp;gt;Dark Mode&amp;lt;/button&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;script&amp;gt;\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  // Dark mode javascript funktion.&lt;br /&gt;
  htmlResponse += &amp;quot;function toggleDarkMode() {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.body.classList.toggle(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  const isDarkMode = document.body.classList.contains(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  localStorage.setItem(&#039;darkMode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.getElementById(&#039;themeToggle&#039;).textContent = isDarkMode ? &#039;Light Mode&#039; : &#039;Dark Mode&#039;;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Toggler dark-mode for .info elementerne\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;.info&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.toggle(&#039;dark-mode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Toggler dark-mode for &amp;lt;a&amp;gt; elementerne\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;a&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.toggle(&#039;dark-mode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;if (localStorage.getItem(&#039;darkMode&#039;) === &#039;true&#039;) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.body.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.getElementById(&#039;themeToggle&#039;).textContent = &#039;Light Mode&#039;;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Tilføjer dark-mode til .info elementerne ved sideindlæsning, hvis dark mode er aktivt\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;.info&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Tilføjer dark-mode til &amp;lt;a&amp;gt; elementerne ved sideindlæsning, hvis dark mode er aktivt\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;a&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  // Javascript funktion til at opdatere temperatur dynamisk.&lt;br /&gt;
  htmlResponse += &amp;quot;function fetchTemperature() {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  fetch(&#039;/temperature&#039;).then(response =&amp;gt; response.text()).then(data =&amp;gt; {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    document.getElementById(&#039;temperature&#039;).innerHTML = data;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;setInterval(fetchTemperature, &amp;quot; + String(updateInterval) + &amp;quot;);\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/script&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/body&amp;gt;\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  server.send(200, &amp;quot;text/html&amp;quot;, htmlResponse);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleTemperature() { // URL til at sende temperaturdata til JavaScript opdatering af handleRoot&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  if (tempC == -127.0) {&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, &amp;quot;&amp;lt;p class=&#039;error&#039;&amp;gt;Fejl: Ingen sensor fundet&amp;lt;/p&amp;gt;&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, &amp;quot;Aktuel temperatur: &amp;quot; + String(tempC) + &amp;quot; &amp;amp;deg;C&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleReset() { // Håndterer web kald til /reset (websiden som kan bruges til at nulstille systemet)&lt;br /&gt;
  String input = server.arg(&amp;quot;reset_input&amp;quot;);  // Få input fra formularen&lt;br /&gt;
  &lt;br /&gt;
  // Tjek om input er &amp;quot;reset&amp;quot;, uanset store/små bogstaver&lt;br /&gt;
  if (input.equalsIgnoreCase(&amp;quot;reset&amp;quot;)) {&lt;br /&gt;
    server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;System nulstilles og genstartes.&amp;quot;);  // Vist besked om nulstilling&lt;br /&gt;
    resetConfig();  // Nulstil konfiguration.&lt;br /&gt;
    delay(3000);    // Vent tre sekunder.&lt;br /&gt;
    ESP.restart();  // Genstart ESP32.&lt;br /&gt;
  } else {&lt;br /&gt;
    // Hvis input ikke er korrekt, send en fejlbesked&lt;br /&gt;
    String htmlResponse = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n&amp;lt;html lang=&#039;da&#039;&amp;gt;\n&amp;lt;head&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta http-equiv=&#039;refresh&#039; content=&#039;15&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;title&amp;gt;Hottub Temperatur&amp;lt;/title&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/head&amp;gt;\n&amp;lt;body style=&#039;color: red; text-align: center;&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;h1&amp;gt;Skriv &#039;reset&#039; i tekstboksen for, at nulstille systemet.&amp;lt;/h1&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;form method=&#039;POST&#039; action=&#039;/reset&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;input type=&#039;text&#039; name=&#039;reset_input&#039; placeholder=&#039;Skriv her&#039; required&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;input type=&#039;submit&#039; value=&#039;Nulstil&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/form&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/body&amp;gt;\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
    server.send(400, &amp;quot;text/html&amp;quot;, htmlResponse);  // Send fejlmeddelelse hvis input er forkert&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleConfig() { // Håndterer web kald til /config (webside hvor systemet kan konfigureres)&lt;br /&gt;
  // Hvis det er en POST-anmodning (brugeren indsender data)&lt;br /&gt;
  if (server.method() == HTTP_POST) {&lt;br /&gt;
    // Læs konfigurationsdata fra webformularen og gem dem i variablerne&lt;br /&gt;
    strncpy(ssid, server.arg(&amp;quot;ssid&amp;quot;).c_str(), sizeof(ssid));&lt;br /&gt;
    strncpy(password, server.arg(&amp;quot;password&amp;quot;).c_str(), sizeof(password));&lt;br /&gt;
    strncpy(mqtt_server, server.arg(&amp;quot;mqtt_server&amp;quot;).c_str(), sizeof(mqtt_server));&lt;br /&gt;
    mqtt_port = server.arg(&amp;quot;mqtt_port&amp;quot;).toInt();  // Konverterer MQTT-porten fra tekst til integer&lt;br /&gt;
    if (mqtt_port &amp;lt;= 0 || mqtt_port &amp;gt; 65535) {  // Validering af portnummer&lt;br /&gt;
      mqtt_port = 1883;  // Standard MQTT-port, hvis den er ugyldig&lt;br /&gt;
    }&lt;br /&gt;
    strncpy(mqtt_user, server.arg(&amp;quot;mqtt_user&amp;quot;).c_str(), sizeof(mqtt_user));&lt;br /&gt;
    strncpy(mqtt_password, server.arg(&amp;quot;mqtt_password&amp;quot;).c_str(), sizeof(mqtt_password));&lt;br /&gt;
    &lt;br /&gt;
    // Gem konfigurationen i EEPROM&lt;br /&gt;
    saveConfig();&lt;br /&gt;
    &lt;br /&gt;
    // Bekræftelse og information&lt;br /&gt;
    server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Konfiguration gemt. Genstart ESP32.&amp;quot;);&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    ESP.restart();  // Genstart for at anvende den nye konfiguration&lt;br /&gt;
  } else {&lt;br /&gt;
    // Returner HTML-formular for konfiguration&lt;br /&gt;
    String html = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html lang=&#039;da&#039;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;&amp;lt;title&amp;gt;Konfiguration&amp;lt;/title&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;style&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f4f4f9; color: #333; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;h1 { text-align: center; color: #4CAF50; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;.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); }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;text&#039;], input[type=&#039;password&#039;], input[type=&#039;number&#039;] { width: 100%; padding: 12px; margin: 8px 0; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;submit&#039;] { background-color: #4CAF50; color: white; padding: 14px 20px; border: none; border-radius: 4px; cursor: pointer; width: 100%; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;submit&#039;]:hover { background-color: #45a049; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;label { font-weight: bold; margin-top: 10px; display: block; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;.info { text-align: center; font-size: 14px; color: #777; margin-top: 20px; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;div class=&#039;container&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;h1&amp;gt;Konfiguration&amp;lt;/h1&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;form method=&#039;POST&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;ssid&#039;&amp;gt;SSID:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;ssid&#039; name=&#039;ssid&#039; type=&#039;text&#039; required&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;password&#039;&amp;gt;Password:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;password&#039; name=&#039;password&#039; type=&#039;password&#039; required&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_server&#039;&amp;gt;MQTT Server:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_server&#039; name=&#039;mqtt_server&#039; type=&#039;text&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_port&#039;&amp;gt;MQTT Port:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_port&#039; name=&#039;mqtt_port&#039; type=&#039;number&#039; value=&#039;1883&#039; min=&#039;1&#039; max=&#039;65535&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_user&#039;&amp;gt;MQTT User:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_user&#039; name=&#039;mqtt_user&#039; type=&#039;text&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_password&#039;&amp;gt;MQTT Password:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_password&#039; name=&#039;mqtt_password&#039; type=&#039;password&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;input type=&#039;submit&#039; value=&#039;Gem Konfiguration&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/form&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;div class=&#039;info&#039;&amp;gt;Undlad MQTT-information, hvis den ikke skal bruges.&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, html);  // Sender formularen som HTML&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void resetConfig() { // Nulstiller al konfigurations data&lt;br /&gt;
  memset(ssid, 0, sizeof(ssid));&lt;br /&gt;
  memset(password, 0, sizeof(password));&lt;br /&gt;
  memset(mqtt_server, 0, sizeof(mqtt_server));&lt;br /&gt;
  memset(mqtt_user, 0, sizeof(mqtt_user));&lt;br /&gt;
  memset(mqtt_password, 0, sizeof(mqtt_password));&lt;br /&gt;
  mqtt_port = 1883;  // Default port&lt;br /&gt;
&lt;br /&gt;
  // Initialiser EEPROM med den ønskede størrelse&lt;br /&gt;
  if (!EEPROM.begin(EEPROM_SIZE)) {&lt;br /&gt;
    Serial.println(&amp;quot;EEPROM initialization failed.&amp;quot;);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Rydder EEPROM ved at skrive 0xFF til hver byte (fjerner gamle data)&lt;br /&gt;
  for (int i = 0; i &amp;lt; EEPROM_SIZE; i++) {&lt;br /&gt;
    EEPROM.write(i, 0xFF);  // Write 0xFF to each byte&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  Serial.println(&amp;quot;Skriver til alle adresser.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  saveConfig();  // Funktion til at gemme den opdaterede (tomme) konfiguration til EEPROM&lt;br /&gt;
&lt;br /&gt;
  display.clearDisplay();  // Rydder skærmen&lt;br /&gt;
  display.setTextSize(2);   // Sætter skriftstørrelsen til 2&lt;br /&gt;
  display.println(&amp;quot;WiFi og MQTT&amp;quot;);&lt;br /&gt;
  display.println(&amp;quot; nulstillet.&amp;quot;);&lt;br /&gt;
  display.println(&amp;quot;Genstarter!&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;WiFi og MQTT nulstillet. Genstarter ESP32.&amp;quot;);&lt;br /&gt;
  delay(3000);&lt;br /&gt;
  ESP.restart();  // Restart the ESP32&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void saveConfig() { // Gemmer konfigurationen i EEPROM&lt;br /&gt;
  EEPROM.put(0, ssid);&lt;br /&gt;
  EEPROM.put(32, password);&lt;br /&gt;
  EEPROM.put(64, mqtt_server);&lt;br /&gt;
  EEPROM.put(128, mqtt_port);&lt;br /&gt;
  EEPROM.put(132, mqtt_user);&lt;br /&gt;
  EEPROM.put(164, mqtt_password);&lt;br /&gt;
  EEPROM.commit();  // Bekræft gemt data&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void readConfig() { // Læs konfigurationen fra EEPROM&lt;br /&gt;
  EEPROM.get(0, ssid);&lt;br /&gt;
  EEPROM.get(32, password);&lt;br /&gt;
  EEPROM.get(64, mqtt_server);&lt;br /&gt;
  EEPROM.get(128, mqtt_port);&lt;br /&gt;
  EEPROM.get(132, mqtt_user);&lt;br /&gt;
  EEPROM.get(164, mqtt_password);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diverse links ==&lt;br /&gt;
* [https://samueladesola.medium.com/how-to-set-up-esp32-wroom-32-b2100060470c How to set up ESP32-WROOM-329] in Arduino IDE.&lt;br /&gt;
&lt;br /&gt;
== Afslutning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Arduino]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2058</id>
		<title>Temperaturføler med ESP32, DS18B20 og OLED-skærm</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2058"/>
		<updated>2024-12-18T16:13:52Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Byggevejledning */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Indledning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== Indkøbsliste ==&lt;br /&gt;
* [https://arduinotech.dk/shop/esp32-usb-c-iot-mainboard-devkitc-cp2102-38pin/ ESP32 USB-C IoT bundkort DevKitC CP2102 38PIN]&lt;br /&gt;
* [https://arduinotech.dk/shop/0-96%e2%80%b3-i2c-iic-spi-serial-128x64-oled-new-model/ 0,96&amp;quot; I2C 128x64 OLED-display]&lt;br /&gt;
* [https://arduinotech.dk/shop/ds18b20-waterproof-digital-thermal-probe-with-3-5mm-mini-audio-jack-1-meter-cable/ DS18B20 vandtæt digital termisk sonde (1 meter kabel)]&lt;br /&gt;
* [https://arduinotech.dk/shop/tsp-xx-replace-hlk-pmxx-ac-dc-220v-to-12v-buck-step-down-power-supply-module-converter/ HLK-PMxx 220VAC til 5VDC Step Down Converter]&lt;br /&gt;
* [https://arduinotech.dk/shop/sonoff-ip66-waterproof-case/ Sonoff IP66 vandtæt etui]&lt;br /&gt;
* [https://arduinotech.dk/shop/prototype-pcb-print/ Prototype PCB print - Type 1]&lt;br /&gt;
&lt;br /&gt;
== Byggevejledning ==&lt;br /&gt;
&lt;br /&gt;
1. Tilslutninger:  &lt;br /&gt;
   - DS18B20:  &lt;br /&gt;
     - VCC (rød)  → 3.3V på ESP32  &lt;br /&gt;
     - GND (sort) → GND på ESP32  &lt;br /&gt;
     - Data (gul) → GPIO 4 på ESP32  &lt;br /&gt;
     - Tilføj en 4,7kΩ pull-up modstand mellem Data og VCC.&lt;br /&gt;
&lt;br /&gt;
   - OLED-skærm (I2C):  &lt;br /&gt;
     - VCC → 3.3V på ESP32  &lt;br /&gt;
     - GND → GND  på ESP32  &lt;br /&gt;
     - SDA → GPIO 21 på ESP32  &lt;br /&gt;
     - SCL → GPIO 22 på ESP32  &lt;br /&gt;
&lt;br /&gt;
2. Strømforsyning:  &lt;br /&gt;
   Tilslut ESP32 og OLED til 5V strømforsyning via HLK-PMxx-modulet.&lt;br /&gt;
&lt;br /&gt;
3. Montering:  &lt;br /&gt;
   - Montér komponenterne på prototype-PCB.  &lt;br /&gt;
   - Placér hele opsætningen i Sonoff IP66 vandtæt etui for beskyttelse mod vejr og støv.&lt;br /&gt;
&lt;br /&gt;
== Kode til ESP32 ==&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed wikitable&amp;quot;&lt;br /&gt;
! Kopier denne kode til Arduino IDE --&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
/* README plz...&lt;br /&gt;
   Benyt &amp;quot;uPesy ESP32 Wroom DevKit&amp;quot; som board model til kompilering.&lt;br /&gt;
   Jeg har oplevet, at andre har fejl der får LED til at blinke for hurtigt !?!&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;WiFi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;WebServer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;OneWire.h&amp;gt;&lt;br /&gt;
#include &amp;lt;DallasTemperature.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_GFX.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_SSD1306.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;&lt;br /&gt;
#include &amp;lt;EEPROM.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// === PIN-konfigurationer ===&lt;br /&gt;
#define ONE_WIRE_BUS   4  // Pin til DS18B20 temperatursensoren.&lt;br /&gt;
#define SCREEN_WIDTH 128  // Skærmens bredde (for OLED).&lt;br /&gt;
#define SCREEN_HEIGHT 64  // Skærmens højde (for OLED).&lt;br /&gt;
#define OLED_RESET    -1  // Reset pin til OLED (sættes til -1, da vi ikke bruger en fysisk reset pin).&lt;br /&gt;
#define EEPROM_SIZE 4096  // Størrelsen af EEPROM (kan gemme op til 4096 byte).&lt;br /&gt;
#define RST_PIN       13  // Pin for RST-knappen.&lt;br /&gt;
#define LED_BUILTIN    2  // Pin for LED.&lt;br /&gt;
&lt;br /&gt;
// === Globale objekter ===&lt;br /&gt;
OneWire oneWire(ONE_WIRE_BUS);        // Initialiserer OneWire-bus til DS18B20.&lt;br /&gt;
DallasTemperature sensors(&amp;amp;oneWire);  // Opretter et objekt til at håndtere temperaturmålinger.&lt;br /&gt;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &amp;amp;Wire, OLED_RESET);  // OLED-skærm til at vise informationer.&lt;br /&gt;
WebServer server(80);                 // Webserver på port 80 til at håndtere HTTP-anmodninger.&lt;br /&gt;
WiFiClient espClient;                 // WiFi klient til MQTT.&lt;br /&gt;
PubSubClient mqttClient(espClient);   // MQTT-klient, der bruger WiFi klienten.&lt;br /&gt;
&lt;br /&gt;
// === Globale variabler ===&lt;br /&gt;
unsigned long lastCommsCheck = 0;          // Tidspunkt for sidste Comms-tjek.&lt;br /&gt;
unsigned long CommsCheckInterval = 60000;  // Tidsinterval for Comms-tjek (60 sekunder) angivet i millisekunder.&lt;br /&gt;
unsigned long lastLEDCheck = 0;            // Tidspunkt for sidste LED-tjek.&lt;br /&gt;
unsigned long LEDCheckInterval = 2000;     // Tidsinterval for LED-tjek (2 sekunder) angivet i millisekunder.&lt;br /&gt;
unsigned long lastMQTTpublish = 0;         // Tidspunkt for sidste MQTT-publish.&lt;br /&gt;
unsigned long MQTTPublishInterval = 15000; // Tidsinterval for MQTT publish (15 sekunder) angivet i millisekunder.&lt;br /&gt;
bool debugEnabled = false;                 // Skal der udskriver til seriel port?&lt;br /&gt;
int updateInterval = 15000;                // Variabel til 15 sekunders opdateringsinterval angivet i millisekunder.&lt;br /&gt;
&lt;br /&gt;
// === Netværksindstillinger ===&lt;br /&gt;
char ssid[32] = &amp;quot;&amp;quot;;      // SSID til WiFi netværket.&lt;br /&gt;
char password[32] = &amp;quot;&amp;quot;;  // WiFi adgangskode.&lt;br /&gt;
&lt;br /&gt;
// === MQTT-indstillinger ===&lt;br /&gt;
char mqtt_server[128] = &amp;quot;&amp;quot;;    // MQTT-serverens IP eller domæne.&lt;br /&gt;
int mqtt_port = 1883;          // MQTT-port (standard 1883).&lt;br /&gt;
char mqtt_user[128] = &amp;quot;&amp;quot;;      // MQTT-brugernavn.&lt;br /&gt;
char mqtt_password[128] = &amp;quot;&amp;quot;;  // MQTT-adgangskode.&lt;br /&gt;
char mqtt_clientID[128] = &amp;quot;&amp;quot;;  // MQTT-clientID.&lt;br /&gt;
&lt;br /&gt;
void setup() { // Initiel programmatiske konfigurations kode. &lt;br /&gt;
  pinMode(RST_PIN, INPUT);        // Initialiser RST_PIN som et INPUT.&lt;br /&gt;
  pinMode(LED_BUILTIN, OUTPUT);   // Initialiser den blå LED.&lt;br /&gt;
  pinMode(RST_PIN, INPUT_PULLUP); // Aktiver intern pull-up modstand.&lt;br /&gt;
  digitalWrite(LED_BUILTIN, LOW); // Sluk den blå LED.&lt;br /&gt;
  Serial.begin(115200);           // Initialiserer seriel kommunikation til debugging.&lt;br /&gt;
  EEPROM.begin(EEPROM_SIZE);      // Initialiserer EEPROM for at gemme konfigurationer.&lt;br /&gt;
  sensors.begin();                // Starter temperatursensoren.&lt;br /&gt;
  lastCommsCheck = millis();      // Sætter første tidsværdi af variablen.&lt;br /&gt;
  lastLEDCheck = millis();        // Sætter første tidsværdi af variablen.&lt;br /&gt;
  lastMQTTpublish = millis();     // Sætter første tidsværdi af variablen.&lt;br /&gt;
  &lt;br /&gt;
  // OLED-initialisering&lt;br /&gt;
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {  // Tjekker om OLED-skærmen er korrekt tilsluttet&lt;br /&gt;
    Serial.println(F(&amp;quot;OLED fejl!&amp;quot;));&lt;br /&gt;
    for (;;);  // Stopper programmet, hvis skærmen ikke kan initialiseres&lt;br /&gt;
  }&lt;br /&gt;
  display.clearDisplay();  // Rydder skærmen&lt;br /&gt;
  display.setTextSize(1);  // Sætter tekststørrelse&lt;br /&gt;
  display.setTextColor(WHITE);  // Sætter tekstfarve til hvid&lt;br /&gt;
&lt;br /&gt;
  // Tjek om RST-knappen er trykket under opstart&lt;br /&gt;
  if (digitalRead(RST_PIN) == LOW) {  // Hvis pin er LOW&lt;br /&gt;
    Serial.println(&amp;quot;RST-knappen trykket. Nulstiller WiFi og MQTT konfiguration.&amp;quot;);&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    resetConfig();  // Nulstil konfiguration&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readConfig();                   // Læs gemte konfigurationer fra EEPROM&lt;br /&gt;
  if (strlen(ssid) == 0 || strlen(password) == 0) { // Hvis ingen konfiguration er gemt, start konfigurationsmode&lt;br /&gt;
    displayMessage(&amp;quot;WiFi konfig mangler.\n\nStarter konfig WiFi.&amp;quot;);&lt;br /&gt;
    setupConfigServer();          // Starter konfigurationsserveren, så brugeren kan konfigurere WiFi og MQTT&lt;br /&gt;
    } else {&lt;br /&gt;
    // WiFi-forbindelse&lt;br /&gt;
    connectWiFi();                // Forbinder til WiFi, hvis SSID og adgangskode er gemt&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Webserver-ruter&lt;br /&gt;
  server.on(&amp;quot;/&amp;quot;, handleRoot);                   // Root URL, der viser temperaturdata&lt;br /&gt;
  server.on(&amp;quot;/temperature&amp;quot;, handleTemperature); // URL til at sende temperaturdata til JavaScript opdatering af handleRoot&lt;br /&gt;
  server.on(&amp;quot;/config&amp;quot;, handleConfig);           // URL til at konfigurere WiFi og MQTT&lt;br /&gt;
  server.on(&amp;quot;/reset&amp;quot;, handleReset);             // URL til at nulstille WiFi&lt;br /&gt;
  server.begin();  // Starter webserveren&lt;br /&gt;
&lt;br /&gt;
  if (strlen(mqtt_server) &amp;gt; 0) { // start kun MQTT, hvis &amp;quot;mqtt_server&amp;quot; er konfigureret og gemt.&lt;br /&gt;
    // Hent MAC-adressen&lt;br /&gt;
    String mac = WiFi.macAddress();                         // Eksempel: &amp;quot;24:6F:28:1A:2B:3C&amp;quot;&lt;br /&gt;
    Serial.println(mac);&lt;br /&gt;
    mac.replace(&amp;quot;:&amp;quot;,&amp;quot;&amp;quot;);                                    // fjern kolon&#039;er&lt;br /&gt;
    String lastSixChars = mac.substring(mac.length() - 6);  // Gem de sidste 6 tegn - Resultat: &amp;quot;2B:3C&amp;quot;&lt;br /&gt;
    String mqtt_clientID = &amp;quot;hottub-&amp;quot; + lastSixChars;        // Generer klient-id baseret på de sidste 6 bogstaver og tal wifi mac adresse&lt;br /&gt;
    mqttClient.setServer(mqtt_server, mqtt_port);&lt;br /&gt;
    if (!mqttClient.connected()) {                          // HVis MQTT ikke er forbundet,&lt;br /&gt;
      reconnectMQTT();                                      // Forsøg at oprette forbindelse til MQTT.&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_server   = &amp;quot;) + mqtt_server);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_port     = &amp;quot;) + mqtt_port);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt-klientID = &amp;quot;) + mqtt_clientID);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_user     = &amp;quot;) + mqtt_user);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_password = &amp;quot;) + mqtt_password);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot; - Debug beskeder kan aktiveres på seriel port med &#039;debug on&#039; eller &#039;debug off&#039;(standard).&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot; - Systemet kan nulstilles ved at sende &#039;nullify system&#039; på seriel porten.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() { // Her placeres koden der skal eksekveres kontinuerligt&lt;br /&gt;
  server.handleClient();                                   // Lad webserveren behandle indkommende HTTP-anmodninger.&lt;br /&gt;
  sensors.requestTemperatures();                           // Anmoder om temperaturmåling.&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);                // Hente temperatur fra den første sensor (index 0).&lt;br /&gt;
  updateOledScreen(tempC);                                 // Opdaterer OLED-skærmen.&lt;br /&gt;
  &lt;br /&gt;
  if (millis() - lastMQTTpublish &amp;gt;= MQTTPublishInterval) { // Send MQTT publish hvis det er tid til det (hver 15. sekunder)&lt;br /&gt;
    if (strlen(mqtt_server) &amp;gt; 0) {                         // Send kun MQTT, hvis mqtt_server er angivet&lt;br /&gt;
      if (mqttClient.connected()) {                        // Send temperaturdata til MQTT, hvis forbindelsen er aktiv&lt;br /&gt;
        sendTemperatureToMQTT(tempC);&lt;br /&gt;
      }&lt;br /&gt;
      lastMQTTpublish = millis();                          // Opdater lastMQTTpublish variablen til nu.&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if (millis() - lastCommsCheck &amp;gt;= CommsCheckInterval) {   // Tjek forbindelser hvis det er tid til det (hver 60. sekunder)&lt;br /&gt;
    if (WiFi.status() != WL_CONNECTED) {                   // Hvis WiFi ikke er forbundet,&lt;br /&gt;
      connectWiFi();                                       // Forsøg at oprette forbindelse til WiFi.&lt;br /&gt;
    }&lt;br /&gt;
    if (strlen(mqtt_server) &amp;gt; 0) {                         // Tjek kun MQTT, hvis mqtt_server er angivet&lt;br /&gt;
      if (!mqttClient.connected()) {                       // HVis MQTT ikke er forbundet,&lt;br /&gt;
        reconnectMQTT();                                   // Forsøg at oprette forbindelse til MQTT.&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    lastCommsCheck = millis();                             // Opdater lastCommsCheck variablen til nu.&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (millis() - lastLEDCheck &amp;gt;= LEDCheckInterval) {      // Blinke funktion til indikation af at loop() køre&lt;br /&gt;
&lt;br /&gt;
    // Serial.print(millis());&lt;br /&gt;
    // Serial.print(&amp;quot; - &amp;quot;);&lt;br /&gt;
    // Serial.print(lastLEDCheck);&lt;br /&gt;
    // Serial.print(&amp;quot; = &amp;quot;);&lt;br /&gt;
    // Serial.println(millis() - lastLEDCheck);&lt;br /&gt;
    // Serial.println(&amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // Læs den nuværende tilstand af LED_BUILTIN og sæt den modsatte værdi.&lt;br /&gt;
    lastLEDCheck = millis();                              // Opdater tidspunktet for LED status ændring&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (Serial.available() &amp;gt; 0) {                           // Håndter seriel kommando linje&lt;br /&gt;
    handleSerialInput();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleSerialInput() { // Håndter seriel kommando linje&lt;br /&gt;
  String input = Serial.readStringUntil(&#039;\n&#039;);  // Læs indtil linjeskift&lt;br /&gt;
  input.trim();  // Fjern eventuelle mellemrum eller linjeskift&lt;br /&gt;
&lt;br /&gt;
  if (input == &amp;quot;debug on&amp;quot;) {&lt;br /&gt;
    debugEnabled = true;&lt;br /&gt;
    Serial.println(&amp;quot;Debug mode aktiveret.&amp;quot;);&lt;br /&gt;
  } &lt;br /&gt;
  else if (input == &amp;quot;debug off&amp;quot;) {&lt;br /&gt;
    debugEnabled = false;&lt;br /&gt;
    Serial.println(&amp;quot;Debug mode deaktiveret.&amp;quot;);&lt;br /&gt;
  } &lt;br /&gt;
  else if (input == &amp;quot;nullify system&amp;quot;) {&lt;br /&gt;
    Serial.println(&amp;quot;Systemet nulstilles om 42 sekunder!&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;SLUK HVIS DET VAR EN FEJL-KOMMANDO!&amp;quot;);&lt;br /&gt;
    int seconds =42;&lt;br /&gt;
    while (seconds &amp;gt; 0) {&lt;br /&gt;
      Serial.print(&amp;quot;Tid tilbage: &amp;quot;);&lt;br /&gt;
      Serial.print(seconds);&lt;br /&gt;
      Serial.println(&amp;quot; sekunder&amp;quot;);&lt;br /&gt;
      delay(1000);  // Vent 1 sekund&lt;br /&gt;
      seconds--;&lt;br /&gt;
    }&lt;br /&gt;
    resetConfig();&lt;br /&gt;
  } &lt;br /&gt;
  else {&lt;br /&gt;
    Serial.println(&amp;quot;--&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;Ugyldig kommando!&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;--&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;&#039;debug on&#039; eller &#039;debug off&#039; tænder / slukker for debug beskeder.&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;&#039;nullify system&#039; nulstiller til fabriksinstillinger.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupConfigServer() { // Starter soft-AP (WiFi access point) til konfiguration af systemet&lt;br /&gt;
  String apSSID = &amp;quot;Hottub_Config&amp;quot;;  // SSID for AP&lt;br /&gt;
  &lt;br /&gt;
  // Generer en tilfældig kode mellem 100000000 og 999999999&lt;br /&gt;
  long randomCode = random(100000000, 1000000000);&lt;br /&gt;
  String apPassword = String(randomCode);  // Konverter tallet til en streng&lt;br /&gt;
  &lt;br /&gt;
  // Start softAP&lt;br /&gt;
  WiFi.softAP(apSSID.c_str(), apPassword.c_str());&lt;br /&gt;
  &lt;br /&gt;
  // Vent et øjeblik for at sikre, at access pointet er startet&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  &lt;br /&gt;
  // Print IP-adresse for AP&lt;br /&gt;
  Serial.println(&amp;quot;Soft-AP startet.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  server.on(&amp;quot;/config&amp;quot;, handleConfig);  // Håndterer konfigurationsanmodninger&lt;br /&gt;
  server.begin();  // Starter webserveren&lt;br /&gt;
  Serial.println(&amp;quot;#############################&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;Konfigurationsserver startet.&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;SSID: &amp;quot; + apSSID);&lt;br /&gt;
  Serial.println(&amp;quot;Kode: &amp;quot; + apPassword);&lt;br /&gt;
  Serial.println(&amp;quot;Tilgå http://&amp;quot;+ WiFi.softAPIP().toString() + &amp;quot;/config for at konfigurere.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  // Vist information om konfigurationsserveren på OLED-skærmen&lt;br /&gt;
  display.clearDisplay();   // Rydder skærmen før opdatering&lt;br /&gt;
  display.setCursor(0, 0);  // Sætter cursoren i øverste venstre hjørne&lt;br /&gt;
  display.print(&amp;quot;- Config WiFi online!&amp;quot;);&lt;br /&gt;
  display.setCursor(0, 10);&lt;br /&gt;
  display.print(&amp;quot;SSID: &amp;quot; + apSSID);&lt;br /&gt;
  display.setCursor(0, 19);&lt;br /&gt;
  display.print(&amp;quot;Kodeord:&amp;quot;);&lt;br /&gt;
  display.setTextSize(2);    // Sætter skriftstørrelsen til 2 (standard er 1)&lt;br /&gt;
  display.setCursor(4, 28);&lt;br /&gt;
  display.print(apPassword); // Vis den tilfældige kode&lt;br /&gt;
  display.setTextSize(1);    // Sætter skriftstørrelsen til 1 (standard er 1)&lt;br /&gt;
  display.setCursor(0, 45);&lt;br /&gt;
  display.print(&amp;quot; IP:  &amp;quot; + WiFi.softAPIP().toString()); // Vist IP-adressen på soft-AP mode&lt;br /&gt;
  display.setCursor(0, 55);&lt;br /&gt;
  display.print(&amp;quot;http://&amp;lt;IP&amp;gt;/config&amp;quot;);&lt;br /&gt;
  display.display();        // Vis information på OLED.&lt;br /&gt;
&lt;br /&gt;
  // Vi stopper her og venter på, at brugeren gemmer en gyldig konfiguration&lt;br /&gt;
  while (strlen(ssid) == 0 || strlen(password) == 0) {&lt;br /&gt;
    delay(1000);  // Fortsæt med at vente, mens vi holder serveren aktiv&lt;br /&gt;
    server.handleClient();  // Håndter klientanmodninger under ventetid&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  // Når en gyldig konfiguration er gemt, afslut setup og gå videre til WiFi-forbindelse&lt;br /&gt;
  displayMessage(&amp;quot;Konfiguration gemt. Genstarter ESP32.&amp;quot;);&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  ESP.restart();  // Genstart for at anvende den gemte konfiguration&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void connectWiFi() { // Tilslutter til det konfigurerede Wireless netværk&lt;br /&gt;
  if (strlen(ssid) == 0 || strlen(password) == 0) {&lt;br /&gt;
    displayMessage(&amp;quot;Ugyldig WiFi config&amp;quot;);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  displayMessage(&amp;quot;Forbinder til WiFi...&amp;quot;);&lt;br /&gt;
  WiFi.begin(ssid, password);&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED &amp;amp;&amp;amp; attempts &amp;lt; 10) {&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    attempts++;&lt;br /&gt;
    displayMessage(&amp;quot;Forbinder: Forsøg &amp;quot; + String(attempts));&lt;br /&gt;
    Serial.println(&amp;quot;Forbinder til WiFi: Forsøg &amp;quot; + String(attempts));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (WiFi.status() != WL_CONNECTED) {&lt;br /&gt;
    display.clearDisplay();  // Rydder skærmen før visning&lt;br /&gt;
    display.setCursor(0, 0);&lt;br /&gt;
    display.println(&amp;quot;WiFi fejlede&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // Vis det aktuelle SSID på linje 2&lt;br /&gt;
    display.setCursor(0, 16);&lt;br /&gt;
    display.print(&amp;quot;SSID: &amp;quot;);&lt;br /&gt;
    display.print(ssid);  // Vist det aktuelle SSID&lt;br /&gt;
    &lt;br /&gt;
    display.display();  // Opdaterer skærmen&lt;br /&gt;
    Serial.println(&amp;quot;Kunne ikke forbinde til WiFi ved opstart.&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    displayMessage(&amp;quot;WiFi forbundet!&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void reconnectMQTT() { // Forsøger at oprette forbindelse til MQTT-serveren&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (!mqttClient.connected() &amp;amp;&amp;amp; attempts &amp;lt; 3) {&lt;br /&gt;
    if (mqttClient.connect(mqtt_clientID, mqtt_user, mqtt_password)) {&lt;br /&gt;
      mqttClient.publish(&amp;quot;hottubsensor/temperature&amp;quot;, &amp;quot;Forbundet til MQTT&amp;quot;);  // Send en testmeddelelse&lt;br /&gt;
    } else {&lt;br /&gt;
      Serial.print(&amp;quot;FEJL, RC=&amp;quot;);&lt;br /&gt;
      // RE 5 er f.eks. MQTT_CONNECT_UNAUTHORIZED - se https://pubsubclient.knolleary.net/api for flere fejl beskeder.&lt;br /&gt;
      Serial.print(mqttClient.state());&lt;br /&gt;
      Serial.println(&amp;quot; venter 1420 msek.&amp;quot;);&lt;br /&gt;
      delay(1420);  // Vent lidt før et nyt forsøg&lt;br /&gt;
      attempts++;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  // Hvis MQTT-forbindelsen mislykkes efter flere forsøg&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    Serial.println(&amp;quot;Kunne ikke forbinde til MQTT efter 3 forsøg.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void sendTemperatureToMQTT(float temp) { // Sender aktuel temperatur til MQTT server&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    return;  // Send kun, hvis MQTT er forbundet&lt;br /&gt;
  }&lt;br /&gt;
  char tempStr[10];                 // Midlertidig buffer&lt;br /&gt;
  dtostrf(temp, 6, 2, tempStr);     // Konverterer float til string med 2 decimaler&lt;br /&gt;
  mqttClient.publish(&amp;quot;hottubsensor/temperature&amp;quot;, tempStr);&lt;br /&gt;
  debugEnabled ? (void)Serial.println(&amp;quot;MQTT publish: &amp;quot; + String(tempStr)) : (void)0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void updateOledScreen(float temp) { // Opdatrerer OLED skærm med information under normal drift&lt;br /&gt;
  display.clearDisplay();   // Rydder skærmen før opdatering&lt;br /&gt;
  display.setCursor(0, 0);  // Sætter cursoren i øverste venstre hjørne&lt;br /&gt;
  &lt;br /&gt;
  // Vist temperatur på den første linje&lt;br /&gt;
  display.setTextSize(3);   // Sætter skriftstørrelsen til 3 (standard er 1)&lt;br /&gt;
  // display.print(&amp;quot; &amp;quot;);       // lav et mellemrum&lt;br /&gt;
  display.print(temp);      // Vis temperatur&lt;br /&gt;
  //display.print(&amp;quot; &amp;quot;);       // lav et mellemrum&lt;br /&gt;
  display.write(247);       // 247 er ASCII-koden for &#039;°&#039; i Adafruit GFX&lt;br /&gt;
  display.print(&amp;quot;C&amp;quot;);&lt;br /&gt;
  display.setTextSize(1);   // Gå tilbage til normal skriftstørrelse for de næste linjer&lt;br /&gt;
  if (debugEnabled) {&lt;br /&gt;
    char tempStr[10];                 // Midlertidig buffer&lt;br /&gt;
    dtostrf(temp, 6, 2, tempStr);     // Konverterer float til string med 2 decimaler&lt;br /&gt;
    Serial.println(&amp;quot;OLED update: &amp;quot; + String(tempStr));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Vist IP-adresse på den næste linje&lt;br /&gt;
  display.setCursor(0, 28);  // Sætter cursoren på næste linje&lt;br /&gt;
  display.print(&amp;quot;IP:   &amp;quot;);&lt;br /&gt;
  display.print(WiFi.localIP());  // Vist IP-adresse&lt;br /&gt;
  debugEnabled ? (void)Serial.println(String(&amp;quot;OLED update: IP = &amp;quot;) + WiFi.localIP().toString()) : (void)0;&lt;br /&gt;
  &lt;br /&gt;
  // Vist WiFi status på næste linje&lt;br /&gt;
  display.setCursor(0, 41);  // Sætter cursoren på den næste linje&lt;br /&gt;
  display.print(&amp;quot;WiFi: &amp;quot;);&lt;br /&gt;
  if (WiFi.status() == WL_CONNECTED) {&lt;br /&gt;
    display.print(WiFi.SSID()); // Vis SSID for aktuel WiFi&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: SSID: &amp;quot; + WiFi.SSID()) : (void)0;&lt;br /&gt;
  } else {&lt;br /&gt;
    display.print(&amp;quot;Ikke forbundet&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: WiFi: Ikke forbundet&amp;quot;) : (void)0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Vis MQTT informationer på næste linje&lt;br /&gt;
  display.setCursor(0, 53);  // Sætter cursoren på næste linje&lt;br /&gt;
  display.print(&amp;quot;MQTT: &amp;quot;);&lt;br /&gt;
  // Vis kun MQTT status , hvis mqtt_server er angivet&lt;br /&gt;
  if (strlen(mqtt_server) &amp;gt; 0) {&lt;br /&gt;
    // Hvis MQTT er aktiveret, vis status, ellers &amp;quot;Ikke forbundet&amp;quot;&lt;br /&gt;
    display.print(mqttClient.connected() ? &amp;quot;Forbundet&amp;quot; : &amp;quot;Ikke forbundet&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: MQTT: &amp;quot; + mqttClient.connected() ? &amp;quot;Forbundet&amp;quot; : &amp;quot;Ikke forbundet&amp;quot;) : (void)0;&lt;br /&gt;
  } else {&lt;br /&gt;
    display.print(&amp;quot;Ikke i brug.&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: MQTT: Ikke i brug.&amp;quot;) : (void)0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  display.display();  // Opdaterer skærmen med de nye data&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayMessage(String message) { // Skriver tekst ca. midt på OLED skærmen&lt;br /&gt;
  display.clearDisplay();             // Rydder skærmen&lt;br /&gt;
  display.setCursor(0, 24);           // Sætter cursoren ca. midt på skærmen&lt;br /&gt;
  display.println(message);           // Vist besked på skærmen&lt;br /&gt;
  display.display();                  // Opdaterer skærmen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleRoot() { // Root URL, der viser temperaturdata&lt;br /&gt;
  sensors.requestTemperatures();  // Anmoder om temperaturmåling&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);  // Henter temperatur fra den første sensor (index 0)&lt;br /&gt;
  String response;&lt;br /&gt;
&lt;br /&gt;
  if (tempC == -127.0) {&lt;br /&gt;
    response = &amp;quot;&amp;lt;p id=&#039;temperature&#039; class=&#039;error&#039;&amp;gt;Fejl: Ingen sensor fundet&amp;lt;/p&amp;gt;\n&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    response = &amp;quot;&amp;lt;div class=&#039;meter-container&#039;&amp;gt;&amp;lt;meter id=&#039;temperatur&#039; value=&#039;&amp;quot; + String(tempC) + &amp;quot;&#039; min=&#039;0&#039; max=&#039;45&#039; low=&#039;5&#039; high=&#039;37&#039; optimum=&#039;40&#039;&amp;gt;&amp;lt;/meter&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
    response += &amp;quot;&amp;lt;p id=&#039;temperature&#039; class=&#039;temperature&#039;&amp;gt;Aktuel temperatur: &amp;quot; + String(tempC) + &amp;quot; &amp;amp;deg;C&amp;lt;/p&amp;gt;\n&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String ipAddress = WiFi.localIP().toString();&lt;br /&gt;
&lt;br /&gt;
  response += &amp;quot;&amp;lt;div class=&#039;info&#039;&amp;gt;\nGå til &amp;lt;a href=&#039;http://&amp;quot; + ipAddress + &amp;quot;/reset&#039;&amp;gt;&amp;quot; + ipAddress + &amp;quot;/reset&amp;lt;/a&amp;gt; for at factory resette.&amp;lt;br /&amp;gt;\n&amp;quot;;&lt;br /&gt;
  response += &amp;quot;Gå til &amp;lt;a href=&#039;http://&amp;quot; + ipAddress + &amp;quot;/config&#039;&amp;gt;&amp;quot; + ipAddress + &amp;quot;/config&amp;lt;/a&amp;gt; for at ændre konfig.\n&amp;lt;/div&amp;gt;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  // Tilføjer HTML, CSS og JavaScript for dark mode og dynamisk temperatur-opdatering&lt;br /&gt;
  String htmlResponse = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n&amp;lt;html lang=&#039;da&#039;&amp;gt;\n&amp;lt;head&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;title&amp;gt;Hottub Temperatur&amp;lt;/title&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;style&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;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&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;h1 { color: green; font-size: 2rem; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.meter-container { margin-top: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;meter { width: 80%; max-width: 400px; height: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.temperature, .error { font-size: 1.2rem; margin-top: 10px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.error { color: red; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.info { color: silver; font-size: 1rem; margin-top: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.info.dark-mode { color: dimgray; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;a, a:visited { color: silver; text-decoration: underline; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;a.dark-mode, a.dark-mode:visited { color: dimgray; text-decoration: underline; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;button { padding: 10px 20px; margin-top: 20px; cursor: pointer; background-color: green; color: white; border: none; border-radius: 5px; font-size: 1rem; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;button:hover { background-color: darkgreen; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;body.dark-mode { background-color: black; color: green; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;body.dark-mode a { color: dimgray; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/style&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/head&amp;gt;\n&amp;lt;body&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;h1&amp;gt;Temperaturmåling&amp;lt;/h1&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += response;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;button id=&#039;themeToggle&#039; onclick=&#039;toggleDarkMode()&#039;&amp;gt;Dark Mode&amp;lt;/button&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;script&amp;gt;\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  // Dark mode javascript funktion.&lt;br /&gt;
  htmlResponse += &amp;quot;function toggleDarkMode() {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.body.classList.toggle(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  const isDarkMode = document.body.classList.contains(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  localStorage.setItem(&#039;darkMode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.getElementById(&#039;themeToggle&#039;).textContent = isDarkMode ? &#039;Light Mode&#039; : &#039;Dark Mode&#039;;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Toggler dark-mode for .info elementerne\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;.info&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.toggle(&#039;dark-mode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Toggler dark-mode for &amp;lt;a&amp;gt; elementerne\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;a&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.toggle(&#039;dark-mode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;if (localStorage.getItem(&#039;darkMode&#039;) === &#039;true&#039;) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.body.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.getElementById(&#039;themeToggle&#039;).textContent = &#039;Light Mode&#039;;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Tilføjer dark-mode til .info elementerne ved sideindlæsning, hvis dark mode er aktivt\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;.info&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Tilføjer dark-mode til &amp;lt;a&amp;gt; elementerne ved sideindlæsning, hvis dark mode er aktivt\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;a&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  // Javascript funktion til at opdatere temperatur dynamisk.&lt;br /&gt;
  htmlResponse += &amp;quot;function fetchTemperature() {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  fetch(&#039;/temperature&#039;).then(response =&amp;gt; response.text()).then(data =&amp;gt; {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    document.getElementById(&#039;temperature&#039;).innerHTML = data;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;setInterval(fetchTemperature, &amp;quot; + String(updateInterval) + &amp;quot;);\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/script&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/body&amp;gt;\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  server.send(200, &amp;quot;text/html&amp;quot;, htmlResponse);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleTemperature() { // URL til at sende temperaturdata til JavaScript opdatering af handleRoot&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  if (tempC == -127.0) {&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, &amp;quot;&amp;lt;p class=&#039;error&#039;&amp;gt;Fejl: Ingen sensor fundet&amp;lt;/p&amp;gt;&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, &amp;quot;Aktuel temperatur: &amp;quot; + String(tempC) + &amp;quot; &amp;amp;deg;C&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleReset() { // Håndterer web kald til /reset (websiden som kan bruges til at nulstille systemet)&lt;br /&gt;
  String input = server.arg(&amp;quot;reset_input&amp;quot;);  // Få input fra formularen&lt;br /&gt;
  &lt;br /&gt;
  // Tjek om input er &amp;quot;reset&amp;quot;, uanset store/små bogstaver&lt;br /&gt;
  if (input.equalsIgnoreCase(&amp;quot;reset&amp;quot;)) {&lt;br /&gt;
    server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;System nulstilles og genstartes.&amp;quot;);  // Vist besked om nulstilling&lt;br /&gt;
    resetConfig();  // Nulstil konfiguration.&lt;br /&gt;
    delay(3000);    // Vent tre sekunder.&lt;br /&gt;
    ESP.restart();  // Genstart ESP32.&lt;br /&gt;
  } else {&lt;br /&gt;
    // Hvis input ikke er korrekt, send en fejlbesked&lt;br /&gt;
    String htmlResponse = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n&amp;lt;html lang=&#039;da&#039;&amp;gt;\n&amp;lt;head&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta http-equiv=&#039;refresh&#039; content=&#039;15&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;title&amp;gt;Hottub Temperatur&amp;lt;/title&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/head&amp;gt;\n&amp;lt;body style=&#039;color: red; text-align: center;&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;h1&amp;gt;Skriv &#039;reset&#039; i tekstboksen for, at nulstille systemet.&amp;lt;/h1&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;form method=&#039;POST&#039; action=&#039;/reset&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;input type=&#039;text&#039; name=&#039;reset_input&#039; placeholder=&#039;Skriv her&#039; required&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;input type=&#039;submit&#039; value=&#039;Nulstil&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/form&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/body&amp;gt;\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
    server.send(400, &amp;quot;text/html&amp;quot;, htmlResponse);  // Send fejlmeddelelse hvis input er forkert&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleConfig() { // Håndterer web kald til /config (webside hvor systemet kan konfigureres)&lt;br /&gt;
  // Hvis det er en POST-anmodning (brugeren indsender data)&lt;br /&gt;
  if (server.method() == HTTP_POST) {&lt;br /&gt;
    // Læs konfigurationsdata fra webformularen og gem dem i variablerne&lt;br /&gt;
    strncpy(ssid, server.arg(&amp;quot;ssid&amp;quot;).c_str(), sizeof(ssid));&lt;br /&gt;
    strncpy(password, server.arg(&amp;quot;password&amp;quot;).c_str(), sizeof(password));&lt;br /&gt;
    strncpy(mqtt_server, server.arg(&amp;quot;mqtt_server&amp;quot;).c_str(), sizeof(mqtt_server));&lt;br /&gt;
    mqtt_port = server.arg(&amp;quot;mqtt_port&amp;quot;).toInt();  // Konverterer MQTT-porten fra tekst til integer&lt;br /&gt;
    if (mqtt_port &amp;lt;= 0 || mqtt_port &amp;gt; 65535) {  // Validering af portnummer&lt;br /&gt;
      mqtt_port = 1883;  // Standard MQTT-port, hvis den er ugyldig&lt;br /&gt;
    }&lt;br /&gt;
    strncpy(mqtt_user, server.arg(&amp;quot;mqtt_user&amp;quot;).c_str(), sizeof(mqtt_user));&lt;br /&gt;
    strncpy(mqtt_password, server.arg(&amp;quot;mqtt_password&amp;quot;).c_str(), sizeof(mqtt_password));&lt;br /&gt;
    &lt;br /&gt;
    // Gem konfigurationen i EEPROM&lt;br /&gt;
    saveConfig();&lt;br /&gt;
    &lt;br /&gt;
    // Bekræftelse og information&lt;br /&gt;
    server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Konfiguration gemt. Genstart ESP32.&amp;quot;);&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    ESP.restart();  // Genstart for at anvende den nye konfiguration&lt;br /&gt;
  } else {&lt;br /&gt;
    // Returner HTML-formular for konfiguration&lt;br /&gt;
    String html = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html lang=&#039;da&#039;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;&amp;lt;title&amp;gt;Konfiguration&amp;lt;/title&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;style&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f4f4f9; color: #333; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;h1 { text-align: center; color: #4CAF50; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;.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); }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;text&#039;], input[type=&#039;password&#039;], input[type=&#039;number&#039;] { width: 100%; padding: 12px; margin: 8px 0; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;submit&#039;] { background-color: #4CAF50; color: white; padding: 14px 20px; border: none; border-radius: 4px; cursor: pointer; width: 100%; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;submit&#039;]:hover { background-color: #45a049; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;label { font-weight: bold; margin-top: 10px; display: block; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;.info { text-align: center; font-size: 14px; color: #777; margin-top: 20px; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;div class=&#039;container&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;h1&amp;gt;Konfiguration&amp;lt;/h1&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;form method=&#039;POST&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;ssid&#039;&amp;gt;SSID:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;ssid&#039; name=&#039;ssid&#039; type=&#039;text&#039; required&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;password&#039;&amp;gt;Password:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;password&#039; name=&#039;password&#039; type=&#039;password&#039; required&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_server&#039;&amp;gt;MQTT Server:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_server&#039; name=&#039;mqtt_server&#039; type=&#039;text&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_port&#039;&amp;gt;MQTT Port:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_port&#039; name=&#039;mqtt_port&#039; type=&#039;number&#039; value=&#039;1883&#039; min=&#039;1&#039; max=&#039;65535&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_user&#039;&amp;gt;MQTT User:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_user&#039; name=&#039;mqtt_user&#039; type=&#039;text&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_password&#039;&amp;gt;MQTT Password:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_password&#039; name=&#039;mqtt_password&#039; type=&#039;password&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;input type=&#039;submit&#039; value=&#039;Gem Konfiguration&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/form&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;div class=&#039;info&#039;&amp;gt;Undlad MQTT-information, hvis den ikke skal bruges.&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, html);  // Sender formularen som HTML&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void resetConfig() { // Nulstiller al konfigurations data&lt;br /&gt;
  memset(ssid, 0, sizeof(ssid));&lt;br /&gt;
  memset(password, 0, sizeof(password));&lt;br /&gt;
  memset(mqtt_server, 0, sizeof(mqtt_server));&lt;br /&gt;
  memset(mqtt_user, 0, sizeof(mqtt_user));&lt;br /&gt;
  memset(mqtt_password, 0, sizeof(mqtt_password));&lt;br /&gt;
  mqtt_port = 1883;  // Default port&lt;br /&gt;
&lt;br /&gt;
  // Initialiser EEPROM med den ønskede størrelse&lt;br /&gt;
  if (!EEPROM.begin(EEPROM_SIZE)) {&lt;br /&gt;
    Serial.println(&amp;quot;EEPROM initialization failed.&amp;quot;);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Rydder EEPROM ved at skrive 0xFF til hver byte (fjerner gamle data)&lt;br /&gt;
  for (int i = 0; i &amp;lt; EEPROM_SIZE; i++) {&lt;br /&gt;
    EEPROM.write(i, 0xFF);  // Write 0xFF to each byte&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  Serial.println(&amp;quot;Skriver til alle adresser.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  saveConfig();  // Funktion til at gemme den opdaterede (tomme) konfiguration til EEPROM&lt;br /&gt;
&lt;br /&gt;
  display.clearDisplay();  // Rydder skærmen&lt;br /&gt;
  display.setTextSize(2);   // Sætter skriftstørrelsen til 2&lt;br /&gt;
  display.println(&amp;quot;WiFi og MQTT&amp;quot;);&lt;br /&gt;
  display.println(&amp;quot; nulstillet.&amp;quot;);&lt;br /&gt;
  display.println(&amp;quot;Genstarter!&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;WiFi og MQTT nulstillet. Genstarter ESP32.&amp;quot;);&lt;br /&gt;
  delay(3000);&lt;br /&gt;
  ESP.restart();  // Restart the ESP32&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void saveConfig() { // Gemmer konfigurationen i EEPROM&lt;br /&gt;
  EEPROM.put(0, ssid);&lt;br /&gt;
  EEPROM.put(32, password);&lt;br /&gt;
  EEPROM.put(64, mqtt_server);&lt;br /&gt;
  EEPROM.put(128, mqtt_port);&lt;br /&gt;
  EEPROM.put(132, mqtt_user);&lt;br /&gt;
  EEPROM.put(164, mqtt_password);&lt;br /&gt;
  EEPROM.commit();  // Bekræft gemt data&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void readConfig() { // Læs konfigurationen fra EEPROM&lt;br /&gt;
  EEPROM.get(0, ssid);&lt;br /&gt;
  EEPROM.get(32, password);&lt;br /&gt;
  EEPROM.get(64, mqtt_server);&lt;br /&gt;
  EEPROM.get(128, mqtt_port);&lt;br /&gt;
  EEPROM.get(132, mqtt_user);&lt;br /&gt;
  EEPROM.get(164, mqtt_password);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diverse links ==&lt;br /&gt;
* [https://samueladesola.medium.com/how-to-set-up-esp32-wroom-32-b2100060470c How to set up ESP32-WROOM-329] in Arduino IDE.&lt;br /&gt;
&lt;br /&gt;
== Afslutning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Arduino]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2057</id>
		<title>Temperaturføler med ESP32, DS18B20 og OLED-skærm</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2057"/>
		<updated>2024-12-18T16:12:43Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Kode til ESP32 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Indledning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== Indkøbsliste ==&lt;br /&gt;
* [https://arduinotech.dk/shop/esp32-usb-c-iot-mainboard-devkitc-cp2102-38pin/ ESP32 USB-C IoT bundkort DevKitC CP2102 38PIN]&lt;br /&gt;
* [https://arduinotech.dk/shop/0-96%e2%80%b3-i2c-iic-spi-serial-128x64-oled-new-model/ 0,96&amp;quot; I2C 128x64 OLED-display]&lt;br /&gt;
* [https://arduinotech.dk/shop/ds18b20-waterproof-digital-thermal-probe-with-3-5mm-mini-audio-jack-1-meter-cable/ DS18B20 vandtæt digital termisk sonde (1 meter kabel)]&lt;br /&gt;
* [https://arduinotech.dk/shop/tsp-xx-replace-hlk-pmxx-ac-dc-220v-to-12v-buck-step-down-power-supply-module-converter/ HLK-PMxx 220VAC til 5VDC Step Down Converter]&lt;br /&gt;
* [https://arduinotech.dk/shop/sonoff-ip66-waterproof-case/ Sonoff IP66 vandtæt etui]&lt;br /&gt;
* [https://arduinotech.dk/shop/prototype-pcb-print/ Prototype PCB print - Type 1]&lt;br /&gt;
&lt;br /&gt;
== Byggevejledning ==&lt;br /&gt;
&lt;br /&gt;
1. Tilslutninger:  &lt;br /&gt;
   - DS18B20:  &lt;br /&gt;
     - VCC (rød) → 3.3V på ESP32  &lt;br /&gt;
     - GND (sort) → GND på ESP32  &lt;br /&gt;
     - Data (gul) → GPIO 4 på ESP32  &lt;br /&gt;
     - Tilføj en 4,7kΩ pull-up modstand mellem Data og VCC.&lt;br /&gt;
&lt;br /&gt;
   - OLED-skærm (I2C):  &lt;br /&gt;
     - VCC → 3.3V på ESP32  &lt;br /&gt;
     - GND → GND på ESP32  &lt;br /&gt;
     - SDA → GPIO 21 på ESP32  &lt;br /&gt;
     - SCL → GPIO 22 på ESP32  &lt;br /&gt;
&lt;br /&gt;
2. Strømforsyning:  &lt;br /&gt;
   Tilslut ESP32 og OLED til 5V strømforsyning via HLK-PMxx-modulet.&lt;br /&gt;
&lt;br /&gt;
3. Montering:  &lt;br /&gt;
   - Montér komponenterne på prototype-PCB.  &lt;br /&gt;
   - Placér hele opsætningen i Sonoff IP66 vandtæt etui for beskyttelse mod vejr og støv.&lt;br /&gt;
&lt;br /&gt;
== Kode til ESP32 ==&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed wikitable&amp;quot;&lt;br /&gt;
! Kopier denne kode til Arduino IDE --&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
/* README plz...&lt;br /&gt;
   Benyt &amp;quot;uPesy ESP32 Wroom DevKit&amp;quot; som board model til kompilering.&lt;br /&gt;
   Jeg har oplevet, at andre har fejl der får LED til at blinke for hurtigt !?!&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;WiFi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;WebServer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;OneWire.h&amp;gt;&lt;br /&gt;
#include &amp;lt;DallasTemperature.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_GFX.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_SSD1306.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;&lt;br /&gt;
#include &amp;lt;EEPROM.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// === PIN-konfigurationer ===&lt;br /&gt;
#define ONE_WIRE_BUS   4  // Pin til DS18B20 temperatursensoren.&lt;br /&gt;
#define SCREEN_WIDTH 128  // Skærmens bredde (for OLED).&lt;br /&gt;
#define SCREEN_HEIGHT 64  // Skærmens højde (for OLED).&lt;br /&gt;
#define OLED_RESET    -1  // Reset pin til OLED (sættes til -1, da vi ikke bruger en fysisk reset pin).&lt;br /&gt;
#define EEPROM_SIZE 4096  // Størrelsen af EEPROM (kan gemme op til 4096 byte).&lt;br /&gt;
#define RST_PIN       13  // Pin for RST-knappen.&lt;br /&gt;
#define LED_BUILTIN    2  // Pin for LED.&lt;br /&gt;
&lt;br /&gt;
// === Globale objekter ===&lt;br /&gt;
OneWire oneWire(ONE_WIRE_BUS);        // Initialiserer OneWire-bus til DS18B20.&lt;br /&gt;
DallasTemperature sensors(&amp;amp;oneWire);  // Opretter et objekt til at håndtere temperaturmålinger.&lt;br /&gt;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &amp;amp;Wire, OLED_RESET);  // OLED-skærm til at vise informationer.&lt;br /&gt;
WebServer server(80);                 // Webserver på port 80 til at håndtere HTTP-anmodninger.&lt;br /&gt;
WiFiClient espClient;                 // WiFi klient til MQTT.&lt;br /&gt;
PubSubClient mqttClient(espClient);   // MQTT-klient, der bruger WiFi klienten.&lt;br /&gt;
&lt;br /&gt;
// === Globale variabler ===&lt;br /&gt;
unsigned long lastCommsCheck = 0;          // Tidspunkt for sidste Comms-tjek.&lt;br /&gt;
unsigned long CommsCheckInterval = 60000;  // Tidsinterval for Comms-tjek (60 sekunder) angivet i millisekunder.&lt;br /&gt;
unsigned long lastLEDCheck = 0;            // Tidspunkt for sidste LED-tjek.&lt;br /&gt;
unsigned long LEDCheckInterval = 2000;     // Tidsinterval for LED-tjek (2 sekunder) angivet i millisekunder.&lt;br /&gt;
unsigned long lastMQTTpublish = 0;         // Tidspunkt for sidste MQTT-publish.&lt;br /&gt;
unsigned long MQTTPublishInterval = 15000; // Tidsinterval for MQTT publish (15 sekunder) angivet i millisekunder.&lt;br /&gt;
bool debugEnabled = false;                 // Skal der udskriver til seriel port?&lt;br /&gt;
int updateInterval = 15000;                // Variabel til 15 sekunders opdateringsinterval angivet i millisekunder.&lt;br /&gt;
&lt;br /&gt;
// === Netværksindstillinger ===&lt;br /&gt;
char ssid[32] = &amp;quot;&amp;quot;;      // SSID til WiFi netværket.&lt;br /&gt;
char password[32] = &amp;quot;&amp;quot;;  // WiFi adgangskode.&lt;br /&gt;
&lt;br /&gt;
// === MQTT-indstillinger ===&lt;br /&gt;
char mqtt_server[128] = &amp;quot;&amp;quot;;    // MQTT-serverens IP eller domæne.&lt;br /&gt;
int mqtt_port = 1883;          // MQTT-port (standard 1883).&lt;br /&gt;
char mqtt_user[128] = &amp;quot;&amp;quot;;      // MQTT-brugernavn.&lt;br /&gt;
char mqtt_password[128] = &amp;quot;&amp;quot;;  // MQTT-adgangskode.&lt;br /&gt;
char mqtt_clientID[128] = &amp;quot;&amp;quot;;  // MQTT-clientID.&lt;br /&gt;
&lt;br /&gt;
void setup() { // Initiel programmatiske konfigurations kode. &lt;br /&gt;
  pinMode(RST_PIN, INPUT);        // Initialiser RST_PIN som et INPUT.&lt;br /&gt;
  pinMode(LED_BUILTIN, OUTPUT);   // Initialiser den blå LED.&lt;br /&gt;
  pinMode(RST_PIN, INPUT_PULLUP); // Aktiver intern pull-up modstand.&lt;br /&gt;
  digitalWrite(LED_BUILTIN, LOW); // Sluk den blå LED.&lt;br /&gt;
  Serial.begin(115200);           // Initialiserer seriel kommunikation til debugging.&lt;br /&gt;
  EEPROM.begin(EEPROM_SIZE);      // Initialiserer EEPROM for at gemme konfigurationer.&lt;br /&gt;
  sensors.begin();                // Starter temperatursensoren.&lt;br /&gt;
  lastCommsCheck = millis();      // Sætter første tidsværdi af variablen.&lt;br /&gt;
  lastLEDCheck = millis();        // Sætter første tidsværdi af variablen.&lt;br /&gt;
  lastMQTTpublish = millis();     // Sætter første tidsværdi af variablen.&lt;br /&gt;
  &lt;br /&gt;
  // OLED-initialisering&lt;br /&gt;
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {  // Tjekker om OLED-skærmen er korrekt tilsluttet&lt;br /&gt;
    Serial.println(F(&amp;quot;OLED fejl!&amp;quot;));&lt;br /&gt;
    for (;;);  // Stopper programmet, hvis skærmen ikke kan initialiseres&lt;br /&gt;
  }&lt;br /&gt;
  display.clearDisplay();  // Rydder skærmen&lt;br /&gt;
  display.setTextSize(1);  // Sætter tekststørrelse&lt;br /&gt;
  display.setTextColor(WHITE);  // Sætter tekstfarve til hvid&lt;br /&gt;
&lt;br /&gt;
  // Tjek om RST-knappen er trykket under opstart&lt;br /&gt;
  if (digitalRead(RST_PIN) == LOW) {  // Hvis pin er LOW&lt;br /&gt;
    Serial.println(&amp;quot;RST-knappen trykket. Nulstiller WiFi og MQTT konfiguration.&amp;quot;);&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    resetConfig();  // Nulstil konfiguration&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readConfig();                   // Læs gemte konfigurationer fra EEPROM&lt;br /&gt;
  if (strlen(ssid) == 0 || strlen(password) == 0) { // Hvis ingen konfiguration er gemt, start konfigurationsmode&lt;br /&gt;
    displayMessage(&amp;quot;WiFi konfig mangler.\n\nStarter konfig WiFi.&amp;quot;);&lt;br /&gt;
    setupConfigServer();          // Starter konfigurationsserveren, så brugeren kan konfigurere WiFi og MQTT&lt;br /&gt;
    } else {&lt;br /&gt;
    // WiFi-forbindelse&lt;br /&gt;
    connectWiFi();                // Forbinder til WiFi, hvis SSID og adgangskode er gemt&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Webserver-ruter&lt;br /&gt;
  server.on(&amp;quot;/&amp;quot;, handleRoot);                   // Root URL, der viser temperaturdata&lt;br /&gt;
  server.on(&amp;quot;/temperature&amp;quot;, handleTemperature); // URL til at sende temperaturdata til JavaScript opdatering af handleRoot&lt;br /&gt;
  server.on(&amp;quot;/config&amp;quot;, handleConfig);           // URL til at konfigurere WiFi og MQTT&lt;br /&gt;
  server.on(&amp;quot;/reset&amp;quot;, handleReset);             // URL til at nulstille WiFi&lt;br /&gt;
  server.begin();  // Starter webserveren&lt;br /&gt;
&lt;br /&gt;
  if (strlen(mqtt_server) &amp;gt; 0) { // start kun MQTT, hvis &amp;quot;mqtt_server&amp;quot; er konfigureret og gemt.&lt;br /&gt;
    // Hent MAC-adressen&lt;br /&gt;
    String mac = WiFi.macAddress();                         // Eksempel: &amp;quot;24:6F:28:1A:2B:3C&amp;quot;&lt;br /&gt;
    Serial.println(mac);&lt;br /&gt;
    mac.replace(&amp;quot;:&amp;quot;,&amp;quot;&amp;quot;);                                    // fjern kolon&#039;er&lt;br /&gt;
    String lastSixChars = mac.substring(mac.length() - 6);  // Gem de sidste 6 tegn - Resultat: &amp;quot;2B:3C&amp;quot;&lt;br /&gt;
    String mqtt_clientID = &amp;quot;hottub-&amp;quot; + lastSixChars;        // Generer klient-id baseret på de sidste 6 bogstaver og tal wifi mac adresse&lt;br /&gt;
    mqttClient.setServer(mqtt_server, mqtt_port);&lt;br /&gt;
    if (!mqttClient.connected()) {                          // HVis MQTT ikke er forbundet,&lt;br /&gt;
      reconnectMQTT();                                      // Forsøg at oprette forbindelse til MQTT.&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_server   = &amp;quot;) + mqtt_server);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_port     = &amp;quot;) + mqtt_port);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt-klientID = &amp;quot;) + mqtt_clientID);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_user     = &amp;quot;) + mqtt_user);&lt;br /&gt;
    // Serial.println(String(&amp;quot;mqtt_password = &amp;quot;) + mqtt_password);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot; - Debug beskeder kan aktiveres på seriel port med &#039;debug on&#039; eller &#039;debug off&#039;(standard).&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot; - Systemet kan nulstilles ved at sende &#039;nullify system&#039; på seriel porten.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() { // Her placeres koden der skal eksekveres kontinuerligt&lt;br /&gt;
  server.handleClient();                                   // Lad webserveren behandle indkommende HTTP-anmodninger.&lt;br /&gt;
  sensors.requestTemperatures();                           // Anmoder om temperaturmåling.&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);                // Hente temperatur fra den første sensor (index 0).&lt;br /&gt;
  updateOledScreen(tempC);                                 // Opdaterer OLED-skærmen.&lt;br /&gt;
  &lt;br /&gt;
  if (millis() - lastMQTTpublish &amp;gt;= MQTTPublishInterval) { // Send MQTT publish hvis det er tid til det (hver 15. sekunder)&lt;br /&gt;
    if (strlen(mqtt_server) &amp;gt; 0) {                         // Send kun MQTT, hvis mqtt_server er angivet&lt;br /&gt;
      if (mqttClient.connected()) {                        // Send temperaturdata til MQTT, hvis forbindelsen er aktiv&lt;br /&gt;
        sendTemperatureToMQTT(tempC);&lt;br /&gt;
      }&lt;br /&gt;
      lastMQTTpublish = millis();                          // Opdater lastMQTTpublish variablen til nu.&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if (millis() - lastCommsCheck &amp;gt;= CommsCheckInterval) {   // Tjek forbindelser hvis det er tid til det (hver 60. sekunder)&lt;br /&gt;
    if (WiFi.status() != WL_CONNECTED) {                   // Hvis WiFi ikke er forbundet,&lt;br /&gt;
      connectWiFi();                                       // Forsøg at oprette forbindelse til WiFi.&lt;br /&gt;
    }&lt;br /&gt;
    if (strlen(mqtt_server) &amp;gt; 0) {                         // Tjek kun MQTT, hvis mqtt_server er angivet&lt;br /&gt;
      if (!mqttClient.connected()) {                       // HVis MQTT ikke er forbundet,&lt;br /&gt;
        reconnectMQTT();                                   // Forsøg at oprette forbindelse til MQTT.&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    lastCommsCheck = millis();                             // Opdater lastCommsCheck variablen til nu.&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (millis() - lastLEDCheck &amp;gt;= LEDCheckInterval) {      // Blinke funktion til indikation af at loop() køre&lt;br /&gt;
&lt;br /&gt;
    // Serial.print(millis());&lt;br /&gt;
    // Serial.print(&amp;quot; - &amp;quot;);&lt;br /&gt;
    // Serial.print(lastLEDCheck);&lt;br /&gt;
    // Serial.print(&amp;quot; = &amp;quot;);&lt;br /&gt;
    // Serial.println(millis() - lastLEDCheck);&lt;br /&gt;
    // Serial.println(&amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // Læs den nuværende tilstand af LED_BUILTIN og sæt den modsatte værdi.&lt;br /&gt;
    lastLEDCheck = millis();                              // Opdater tidspunktet for LED status ændring&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (Serial.available() &amp;gt; 0) {                           // Håndter seriel kommando linje&lt;br /&gt;
    handleSerialInput();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleSerialInput() { // Håndter seriel kommando linje&lt;br /&gt;
  String input = Serial.readStringUntil(&#039;\n&#039;);  // Læs indtil linjeskift&lt;br /&gt;
  input.trim();  // Fjern eventuelle mellemrum eller linjeskift&lt;br /&gt;
&lt;br /&gt;
  if (input == &amp;quot;debug on&amp;quot;) {&lt;br /&gt;
    debugEnabled = true;&lt;br /&gt;
    Serial.println(&amp;quot;Debug mode aktiveret.&amp;quot;);&lt;br /&gt;
  } &lt;br /&gt;
  else if (input == &amp;quot;debug off&amp;quot;) {&lt;br /&gt;
    debugEnabled = false;&lt;br /&gt;
    Serial.println(&amp;quot;Debug mode deaktiveret.&amp;quot;);&lt;br /&gt;
  } &lt;br /&gt;
  else if (input == &amp;quot;nullify system&amp;quot;) {&lt;br /&gt;
    Serial.println(&amp;quot;Systemet nulstilles om 42 sekunder!&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;SLUK HVIS DET VAR EN FEJL-KOMMANDO!&amp;quot;);&lt;br /&gt;
    int seconds =42;&lt;br /&gt;
    while (seconds &amp;gt; 0) {&lt;br /&gt;
      Serial.print(&amp;quot;Tid tilbage: &amp;quot;);&lt;br /&gt;
      Serial.print(seconds);&lt;br /&gt;
      Serial.println(&amp;quot; sekunder&amp;quot;);&lt;br /&gt;
      delay(1000);  // Vent 1 sekund&lt;br /&gt;
      seconds--;&lt;br /&gt;
    }&lt;br /&gt;
    resetConfig();&lt;br /&gt;
  } &lt;br /&gt;
  else {&lt;br /&gt;
    Serial.println(&amp;quot;--&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;Ugyldig kommando!&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;--&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;&#039;debug on&#039; eller &#039;debug off&#039; tænder / slukker for debug beskeder.&amp;quot;);&lt;br /&gt;
    Serial.println(&amp;quot;&#039;nullify system&#039; nulstiller til fabriksinstillinger.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setupConfigServer() { // Starter soft-AP (WiFi access point) til konfiguration af systemet&lt;br /&gt;
  String apSSID = &amp;quot;Hottub_Config&amp;quot;;  // SSID for AP&lt;br /&gt;
  &lt;br /&gt;
  // Generer en tilfældig kode mellem 100000000 og 999999999&lt;br /&gt;
  long randomCode = random(100000000, 1000000000);&lt;br /&gt;
  String apPassword = String(randomCode);  // Konverter tallet til en streng&lt;br /&gt;
  &lt;br /&gt;
  // Start softAP&lt;br /&gt;
  WiFi.softAP(apSSID.c_str(), apPassword.c_str());&lt;br /&gt;
  &lt;br /&gt;
  // Vent et øjeblik for at sikre, at access pointet er startet&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  &lt;br /&gt;
  // Print IP-adresse for AP&lt;br /&gt;
  Serial.println(&amp;quot;Soft-AP startet.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  server.on(&amp;quot;/config&amp;quot;, handleConfig);  // Håndterer konfigurationsanmodninger&lt;br /&gt;
  server.begin();  // Starter webserveren&lt;br /&gt;
  Serial.println(&amp;quot;#############################&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;Konfigurationsserver startet.&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;SSID: &amp;quot; + apSSID);&lt;br /&gt;
  Serial.println(&amp;quot;Kode: &amp;quot; + apPassword);&lt;br /&gt;
  Serial.println(&amp;quot;Tilgå http://&amp;quot;+ WiFi.softAPIP().toString() + &amp;quot;/config for at konfigurere.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  // Vist information om konfigurationsserveren på OLED-skærmen&lt;br /&gt;
  display.clearDisplay();   // Rydder skærmen før opdatering&lt;br /&gt;
  display.setCursor(0, 0);  // Sætter cursoren i øverste venstre hjørne&lt;br /&gt;
  display.print(&amp;quot;- Config WiFi online!&amp;quot;);&lt;br /&gt;
  display.setCursor(0, 10);&lt;br /&gt;
  display.print(&amp;quot;SSID: &amp;quot; + apSSID);&lt;br /&gt;
  display.setCursor(0, 19);&lt;br /&gt;
  display.print(&amp;quot;Kodeord:&amp;quot;);&lt;br /&gt;
  display.setTextSize(2);    // Sætter skriftstørrelsen til 2 (standard er 1)&lt;br /&gt;
  display.setCursor(4, 28);&lt;br /&gt;
  display.print(apPassword); // Vis den tilfældige kode&lt;br /&gt;
  display.setTextSize(1);    // Sætter skriftstørrelsen til 1 (standard er 1)&lt;br /&gt;
  display.setCursor(0, 45);&lt;br /&gt;
  display.print(&amp;quot; IP:  &amp;quot; + WiFi.softAPIP().toString()); // Vist IP-adressen på soft-AP mode&lt;br /&gt;
  display.setCursor(0, 55);&lt;br /&gt;
  display.print(&amp;quot;http://&amp;lt;IP&amp;gt;/config&amp;quot;);&lt;br /&gt;
  display.display();        // Vis information på OLED.&lt;br /&gt;
&lt;br /&gt;
  // Vi stopper her og venter på, at brugeren gemmer en gyldig konfiguration&lt;br /&gt;
  while (strlen(ssid) == 0 || strlen(password) == 0) {&lt;br /&gt;
    delay(1000);  // Fortsæt med at vente, mens vi holder serveren aktiv&lt;br /&gt;
    server.handleClient();  // Håndter klientanmodninger under ventetid&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  // Når en gyldig konfiguration er gemt, afslut setup og gå videre til WiFi-forbindelse&lt;br /&gt;
  displayMessage(&amp;quot;Konfiguration gemt. Genstarter ESP32.&amp;quot;);&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  ESP.restart();  // Genstart for at anvende den gemte konfiguration&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void connectWiFi() { // Tilslutter til det konfigurerede Wireless netværk&lt;br /&gt;
  if (strlen(ssid) == 0 || strlen(password) == 0) {&lt;br /&gt;
    displayMessage(&amp;quot;Ugyldig WiFi config&amp;quot;);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  displayMessage(&amp;quot;Forbinder til WiFi...&amp;quot;);&lt;br /&gt;
  WiFi.begin(ssid, password);&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED &amp;amp;&amp;amp; attempts &amp;lt; 10) {&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    attempts++;&lt;br /&gt;
    displayMessage(&amp;quot;Forbinder: Forsøg &amp;quot; + String(attempts));&lt;br /&gt;
    Serial.println(&amp;quot;Forbinder til WiFi: Forsøg &amp;quot; + String(attempts));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (WiFi.status() != WL_CONNECTED) {&lt;br /&gt;
    display.clearDisplay();  // Rydder skærmen før visning&lt;br /&gt;
    display.setCursor(0, 0);&lt;br /&gt;
    display.println(&amp;quot;WiFi fejlede&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // Vis det aktuelle SSID på linje 2&lt;br /&gt;
    display.setCursor(0, 16);&lt;br /&gt;
    display.print(&amp;quot;SSID: &amp;quot;);&lt;br /&gt;
    display.print(ssid);  // Vist det aktuelle SSID&lt;br /&gt;
    &lt;br /&gt;
    display.display();  // Opdaterer skærmen&lt;br /&gt;
    Serial.println(&amp;quot;Kunne ikke forbinde til WiFi ved opstart.&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    displayMessage(&amp;quot;WiFi forbundet!&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void reconnectMQTT() { // Forsøger at oprette forbindelse til MQTT-serveren&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (!mqttClient.connected() &amp;amp;&amp;amp; attempts &amp;lt; 3) {&lt;br /&gt;
    if (mqttClient.connect(mqtt_clientID, mqtt_user, mqtt_password)) {&lt;br /&gt;
      mqttClient.publish(&amp;quot;hottubsensor/temperature&amp;quot;, &amp;quot;Forbundet til MQTT&amp;quot;);  // Send en testmeddelelse&lt;br /&gt;
    } else {&lt;br /&gt;
      Serial.print(&amp;quot;FEJL, RC=&amp;quot;);&lt;br /&gt;
      // RE 5 er f.eks. MQTT_CONNECT_UNAUTHORIZED - se https://pubsubclient.knolleary.net/api for flere fejl beskeder.&lt;br /&gt;
      Serial.print(mqttClient.state());&lt;br /&gt;
      Serial.println(&amp;quot; venter 1420 msek.&amp;quot;);&lt;br /&gt;
      delay(1420);  // Vent lidt før et nyt forsøg&lt;br /&gt;
      attempts++;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  // Hvis MQTT-forbindelsen mislykkes efter flere forsøg&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    Serial.println(&amp;quot;Kunne ikke forbinde til MQTT efter 3 forsøg.&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void sendTemperatureToMQTT(float temp) { // Sender aktuel temperatur til MQTT server&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    return;  // Send kun, hvis MQTT er forbundet&lt;br /&gt;
  }&lt;br /&gt;
  char tempStr[10];                 // Midlertidig buffer&lt;br /&gt;
  dtostrf(temp, 6, 2, tempStr);     // Konverterer float til string med 2 decimaler&lt;br /&gt;
  mqttClient.publish(&amp;quot;hottubsensor/temperature&amp;quot;, tempStr);&lt;br /&gt;
  debugEnabled ? (void)Serial.println(&amp;quot;MQTT publish: &amp;quot; + String(tempStr)) : (void)0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void updateOledScreen(float temp) { // Opdatrerer OLED skærm med information under normal drift&lt;br /&gt;
  display.clearDisplay();   // Rydder skærmen før opdatering&lt;br /&gt;
  display.setCursor(0, 0);  // Sætter cursoren i øverste venstre hjørne&lt;br /&gt;
  &lt;br /&gt;
  // Vist temperatur på den første linje&lt;br /&gt;
  display.setTextSize(3);   // Sætter skriftstørrelsen til 3 (standard er 1)&lt;br /&gt;
  // display.print(&amp;quot; &amp;quot;);       // lav et mellemrum&lt;br /&gt;
  display.print(temp);      // Vis temperatur&lt;br /&gt;
  //display.print(&amp;quot; &amp;quot;);       // lav et mellemrum&lt;br /&gt;
  display.write(247);       // 247 er ASCII-koden for &#039;°&#039; i Adafruit GFX&lt;br /&gt;
  display.print(&amp;quot;C&amp;quot;);&lt;br /&gt;
  display.setTextSize(1);   // Gå tilbage til normal skriftstørrelse for de næste linjer&lt;br /&gt;
  if (debugEnabled) {&lt;br /&gt;
    char tempStr[10];                 // Midlertidig buffer&lt;br /&gt;
    dtostrf(temp, 6, 2, tempStr);     // Konverterer float til string med 2 decimaler&lt;br /&gt;
    Serial.println(&amp;quot;OLED update: &amp;quot; + String(tempStr));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Vist IP-adresse på den næste linje&lt;br /&gt;
  display.setCursor(0, 28);  // Sætter cursoren på næste linje&lt;br /&gt;
  display.print(&amp;quot;IP:   &amp;quot;);&lt;br /&gt;
  display.print(WiFi.localIP());  // Vist IP-adresse&lt;br /&gt;
  debugEnabled ? (void)Serial.println(String(&amp;quot;OLED update: IP = &amp;quot;) + WiFi.localIP().toString()) : (void)0;&lt;br /&gt;
  &lt;br /&gt;
  // Vist WiFi status på næste linje&lt;br /&gt;
  display.setCursor(0, 41);  // Sætter cursoren på den næste linje&lt;br /&gt;
  display.print(&amp;quot;WiFi: &amp;quot;);&lt;br /&gt;
  if (WiFi.status() == WL_CONNECTED) {&lt;br /&gt;
    display.print(WiFi.SSID()); // Vis SSID for aktuel WiFi&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: SSID: &amp;quot; + WiFi.SSID()) : (void)0;&lt;br /&gt;
  } else {&lt;br /&gt;
    display.print(&amp;quot;Ikke forbundet&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: WiFi: Ikke forbundet&amp;quot;) : (void)0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Vis MQTT informationer på næste linje&lt;br /&gt;
  display.setCursor(0, 53);  // Sætter cursoren på næste linje&lt;br /&gt;
  display.print(&amp;quot;MQTT: &amp;quot;);&lt;br /&gt;
  // Vis kun MQTT status , hvis mqtt_server er angivet&lt;br /&gt;
  if (strlen(mqtt_server) &amp;gt; 0) {&lt;br /&gt;
    // Hvis MQTT er aktiveret, vis status, ellers &amp;quot;Ikke forbundet&amp;quot;&lt;br /&gt;
    display.print(mqttClient.connected() ? &amp;quot;Forbundet&amp;quot; : &amp;quot;Ikke forbundet&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: MQTT: &amp;quot; + mqttClient.connected() ? &amp;quot;Forbundet&amp;quot; : &amp;quot;Ikke forbundet&amp;quot;) : (void)0;&lt;br /&gt;
  } else {&lt;br /&gt;
    display.print(&amp;quot;Ikke i brug.&amp;quot;);&lt;br /&gt;
    debugEnabled ? (void)Serial.println(&amp;quot;OLED update: MQTT: Ikke i brug.&amp;quot;) : (void)0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  display.display();  // Opdaterer skærmen med de nye data&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayMessage(String message) { // Skriver tekst ca. midt på OLED skærmen&lt;br /&gt;
  display.clearDisplay();             // Rydder skærmen&lt;br /&gt;
  display.setCursor(0, 24);           // Sætter cursoren ca. midt på skærmen&lt;br /&gt;
  display.println(message);           // Vist besked på skærmen&lt;br /&gt;
  display.display();                  // Opdaterer skærmen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleRoot() { // Root URL, der viser temperaturdata&lt;br /&gt;
  sensors.requestTemperatures();  // Anmoder om temperaturmåling&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);  // Henter temperatur fra den første sensor (index 0)&lt;br /&gt;
  String response;&lt;br /&gt;
&lt;br /&gt;
  if (tempC == -127.0) {&lt;br /&gt;
    response = &amp;quot;&amp;lt;p id=&#039;temperature&#039; class=&#039;error&#039;&amp;gt;Fejl: Ingen sensor fundet&amp;lt;/p&amp;gt;\n&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    response = &amp;quot;&amp;lt;div class=&#039;meter-container&#039;&amp;gt;&amp;lt;meter id=&#039;temperatur&#039; value=&#039;&amp;quot; + String(tempC) + &amp;quot;&#039; min=&#039;0&#039; max=&#039;45&#039; low=&#039;5&#039; high=&#039;37&#039; optimum=&#039;40&#039;&amp;gt;&amp;lt;/meter&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
    response += &amp;quot;&amp;lt;p id=&#039;temperature&#039; class=&#039;temperature&#039;&amp;gt;Aktuel temperatur: &amp;quot; + String(tempC) + &amp;quot; &amp;amp;deg;C&amp;lt;/p&amp;gt;\n&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  String ipAddress = WiFi.localIP().toString();&lt;br /&gt;
&lt;br /&gt;
  response += &amp;quot;&amp;lt;div class=&#039;info&#039;&amp;gt;\nGå til &amp;lt;a href=&#039;http://&amp;quot; + ipAddress + &amp;quot;/reset&#039;&amp;gt;&amp;quot; + ipAddress + &amp;quot;/reset&amp;lt;/a&amp;gt; for at factory resette.&amp;lt;br /&amp;gt;\n&amp;quot;;&lt;br /&gt;
  response += &amp;quot;Gå til &amp;lt;a href=&#039;http://&amp;quot; + ipAddress + &amp;quot;/config&#039;&amp;gt;&amp;quot; + ipAddress + &amp;quot;/config&amp;lt;/a&amp;gt; for at ændre konfig.\n&amp;lt;/div&amp;gt;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  // Tilføjer HTML, CSS og JavaScript for dark mode og dynamisk temperatur-opdatering&lt;br /&gt;
  String htmlResponse = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n&amp;lt;html lang=&#039;da&#039;&amp;gt;\n&amp;lt;head&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;title&amp;gt;Hottub Temperatur&amp;lt;/title&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;style&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;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&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;h1 { color: green; font-size: 2rem; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.meter-container { margin-top: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;meter { width: 80%; max-width: 400px; height: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.temperature, .error { font-size: 1.2rem; margin-top: 10px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.error { color: red; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.info { color: silver; font-size: 1rem; margin-top: 20px; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;.info.dark-mode { color: dimgray; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;a, a:visited { color: silver; text-decoration: underline; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;a.dark-mode, a.dark-mode:visited { color: dimgray; text-decoration: underline; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;button { padding: 10px 20px; margin-top: 20px; cursor: pointer; background-color: green; color: white; border: none; border-radius: 5px; font-size: 1rem; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;button:hover { background-color: darkgreen; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;body.dark-mode { background-color: black; color: green; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;body.dark-mode a { color: dimgray; }\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/style&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/head&amp;gt;\n&amp;lt;body&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;h1&amp;gt;Temperaturmåling&amp;lt;/h1&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += response;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;button id=&#039;themeToggle&#039; onclick=&#039;toggleDarkMode()&#039;&amp;gt;Dark Mode&amp;lt;/button&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;script&amp;gt;\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  // Dark mode javascript funktion.&lt;br /&gt;
  htmlResponse += &amp;quot;function toggleDarkMode() {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.body.classList.toggle(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  const isDarkMode = document.body.classList.contains(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  localStorage.setItem(&#039;darkMode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.getElementById(&#039;themeToggle&#039;).textContent = isDarkMode ? &#039;Light Mode&#039; : &#039;Dark Mode&#039;;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Toggler dark-mode for .info elementerne\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;.info&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.toggle(&#039;dark-mode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Toggler dark-mode for &amp;lt;a&amp;gt; elementerne\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;a&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.toggle(&#039;dark-mode&#039;, isDarkMode);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;if (localStorage.getItem(&#039;darkMode&#039;) === &#039;true&#039;) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.body.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.getElementById(&#039;themeToggle&#039;).textContent = &#039;Light Mode&#039;;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Tilføjer dark-mode til .info elementerne ved sideindlæsning, hvis dark mode er aktivt\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;.info&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  // Tilføjer dark-mode til &amp;lt;a&amp;gt; elementerne ved sideindlæsning, hvis dark mode er aktivt\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  document.querySelectorAll(&#039;a&#039;).forEach(function(el) {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    el.classList.add(&#039;dark-mode&#039;);\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  // Javascript funktion til at opdatere temperatur dynamisk.&lt;br /&gt;
  htmlResponse += &amp;quot;function fetchTemperature() {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  fetch(&#039;/temperature&#039;).then(response =&amp;gt; response.text()).then(data =&amp;gt; {\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;    document.getElementById(&#039;temperature&#039;).innerHTML = data;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;  });\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;setInterval(fetchTemperature, &amp;quot; + String(updateInterval) + &amp;quot;);\n&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/script&amp;gt;\n&amp;quot;;&lt;br /&gt;
  htmlResponse += &amp;quot;&amp;lt;/body&amp;gt;\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  server.send(200, &amp;quot;text/html&amp;quot;, htmlResponse);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleTemperature() { // URL til at sende temperaturdata til JavaScript opdatering af handleRoot&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  if (tempC == -127.0) {&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, &amp;quot;&amp;lt;p class=&#039;error&#039;&amp;gt;Fejl: Ingen sensor fundet&amp;lt;/p&amp;gt;&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, &amp;quot;Aktuel temperatur: &amp;quot; + String(tempC) + &amp;quot; &amp;amp;deg;C&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleReset() { // Håndterer web kald til /reset (websiden som kan bruges til at nulstille systemet)&lt;br /&gt;
  String input = server.arg(&amp;quot;reset_input&amp;quot;);  // Få input fra formularen&lt;br /&gt;
  &lt;br /&gt;
  // Tjek om input er &amp;quot;reset&amp;quot;, uanset store/små bogstaver&lt;br /&gt;
  if (input.equalsIgnoreCase(&amp;quot;reset&amp;quot;)) {&lt;br /&gt;
    server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;System nulstilles og genstartes.&amp;quot;);  // Vist besked om nulstilling&lt;br /&gt;
    resetConfig();  // Nulstil konfiguration.&lt;br /&gt;
    delay(3000);    // Vent tre sekunder.&lt;br /&gt;
    ESP.restart();  // Genstart ESP32.&lt;br /&gt;
  } else {&lt;br /&gt;
    // Hvis input ikke er korrekt, send en fejlbesked&lt;br /&gt;
    String htmlResponse = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n&amp;lt;html lang=&#039;da&#039;&amp;gt;\n&amp;lt;head&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta http-equiv=&#039;refresh&#039; content=&#039;15&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;title&amp;gt;Hottub Temperatur&amp;lt;/title&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/head&amp;gt;\n&amp;lt;body style=&#039;color: red; text-align: center;&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;h1&amp;gt;Skriv &#039;reset&#039; i tekstboksen for, at nulstille systemet.&amp;lt;/h1&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;form method=&#039;POST&#039; action=&#039;/reset&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;input type=&#039;text&#039; name=&#039;reset_input&#039; placeholder=&#039;Skriv her&#039; required&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;input type=&#039;submit&#039; value=&#039;Nulstil&#039;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/form&amp;gt;\n&amp;quot;;&lt;br /&gt;
    htmlResponse += &amp;quot;&amp;lt;/body&amp;gt;\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
    server.send(400, &amp;quot;text/html&amp;quot;, htmlResponse);  // Send fejlmeddelelse hvis input er forkert&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleConfig() { // Håndterer web kald til /config (webside hvor systemet kan konfigureres)&lt;br /&gt;
  // Hvis det er en POST-anmodning (brugeren indsender data)&lt;br /&gt;
  if (server.method() == HTTP_POST) {&lt;br /&gt;
    // Læs konfigurationsdata fra webformularen og gem dem i variablerne&lt;br /&gt;
    strncpy(ssid, server.arg(&amp;quot;ssid&amp;quot;).c_str(), sizeof(ssid));&lt;br /&gt;
    strncpy(password, server.arg(&amp;quot;password&amp;quot;).c_str(), sizeof(password));&lt;br /&gt;
    strncpy(mqtt_server, server.arg(&amp;quot;mqtt_server&amp;quot;).c_str(), sizeof(mqtt_server));&lt;br /&gt;
    mqtt_port = server.arg(&amp;quot;mqtt_port&amp;quot;).toInt();  // Konverterer MQTT-porten fra tekst til integer&lt;br /&gt;
    if (mqtt_port &amp;lt;= 0 || mqtt_port &amp;gt; 65535) {  // Validering af portnummer&lt;br /&gt;
      mqtt_port = 1883;  // Standard MQTT-port, hvis den er ugyldig&lt;br /&gt;
    }&lt;br /&gt;
    strncpy(mqtt_user, server.arg(&amp;quot;mqtt_user&amp;quot;).c_str(), sizeof(mqtt_user));&lt;br /&gt;
    strncpy(mqtt_password, server.arg(&amp;quot;mqtt_password&amp;quot;).c_str(), sizeof(mqtt_password));&lt;br /&gt;
    &lt;br /&gt;
    // Gem konfigurationen i EEPROM&lt;br /&gt;
    saveConfig();&lt;br /&gt;
    &lt;br /&gt;
    // Bekræftelse og information&lt;br /&gt;
    server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Konfiguration gemt. Genstart ESP32.&amp;quot;);&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    ESP.restart();  // Genstart for at anvende den nye konfiguration&lt;br /&gt;
  } else {&lt;br /&gt;
    // Returner HTML-formular for konfiguration&lt;br /&gt;
    String html = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html lang=&#039;da&#039;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&#039;UTF-8&#039;&amp;gt;&amp;lt;meta name=&#039;viewport&#039; content=&#039;width=device-width, initial-scale=1.0&#039;&amp;gt;&amp;lt;title&amp;gt;Konfiguration&amp;lt;/title&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;style&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f4f4f9; color: #333; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;h1 { text-align: center; color: #4CAF50; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;.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); }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;text&#039;], input[type=&#039;password&#039;], input[type=&#039;number&#039;] { width: 100%; padding: 12px; margin: 8px 0; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;submit&#039;] { background-color: #4CAF50; color: white; padding: 14px 20px; border: none; border-radius: 4px; cursor: pointer; width: 100%; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;input[type=&#039;submit&#039;]:hover { background-color: #45a049; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;label { font-weight: bold; margin-top: 10px; display: block; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;.info { text-align: center; font-size: 14px; color: #777; margin-top: 20px; }&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;div class=&#039;container&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;h1&amp;gt;Konfiguration&amp;lt;/h1&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;form method=&#039;POST&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;ssid&#039;&amp;gt;SSID:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;ssid&#039; name=&#039;ssid&#039; type=&#039;text&#039; required&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;password&#039;&amp;gt;Password:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;password&#039; name=&#039;password&#039; type=&#039;password&#039; required&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_server&#039;&amp;gt;MQTT Server:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_server&#039; name=&#039;mqtt_server&#039; type=&#039;text&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_port&#039;&amp;gt;MQTT Port:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_port&#039; name=&#039;mqtt_port&#039; type=&#039;number&#039; value=&#039;1883&#039; min=&#039;1&#039; max=&#039;65535&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_user&#039;&amp;gt;MQTT User:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_user&#039; name=&#039;mqtt_user&#039; type=&#039;text&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;label for=&#039;mqtt_password&#039;&amp;gt;MQTT Password:&amp;lt;/label&amp;gt;&amp;lt;input id=&#039;mqtt_password&#039; name=&#039;mqtt_password&#039; type=&#039;password&#039;&amp;gt;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;input type=&#039;submit&#039; value=&#039;Gem Konfiguration&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/form&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;div class=&#039;info&#039;&amp;gt;Undlad MQTT-information, hvis den ikke skal bruges.&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
    html += &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    server.send(200, &amp;quot;text/html&amp;quot;, html);  // Sender formularen som HTML&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void resetConfig() { // Nulstiller al konfigurations data&lt;br /&gt;
  memset(ssid, 0, sizeof(ssid));&lt;br /&gt;
  memset(password, 0, sizeof(password));&lt;br /&gt;
  memset(mqtt_server, 0, sizeof(mqtt_server));&lt;br /&gt;
  memset(mqtt_user, 0, sizeof(mqtt_user));&lt;br /&gt;
  memset(mqtt_password, 0, sizeof(mqtt_password));&lt;br /&gt;
  mqtt_port = 1883;  // Default port&lt;br /&gt;
&lt;br /&gt;
  // Initialiser EEPROM med den ønskede størrelse&lt;br /&gt;
  if (!EEPROM.begin(EEPROM_SIZE)) {&lt;br /&gt;
    Serial.println(&amp;quot;EEPROM initialization failed.&amp;quot;);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Rydder EEPROM ved at skrive 0xFF til hver byte (fjerner gamle data)&lt;br /&gt;
  for (int i = 0; i &amp;lt; EEPROM_SIZE; i++) {&lt;br /&gt;
    EEPROM.write(i, 0xFF);  // Write 0xFF to each byte&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  Serial.println(&amp;quot;Skriver til alle adresser.&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  saveConfig();  // Funktion til at gemme den opdaterede (tomme) konfiguration til EEPROM&lt;br /&gt;
&lt;br /&gt;
  display.clearDisplay();  // Rydder skærmen&lt;br /&gt;
  display.setTextSize(2);   // Sætter skriftstørrelsen til 2&lt;br /&gt;
  display.println(&amp;quot;WiFi og MQTT&amp;quot;);&lt;br /&gt;
  display.println(&amp;quot; nulstillet.&amp;quot;);&lt;br /&gt;
  display.println(&amp;quot;Genstarter!&amp;quot;);&lt;br /&gt;
  Serial.println(&amp;quot;WiFi og MQTT nulstillet. Genstarter ESP32.&amp;quot;);&lt;br /&gt;
  delay(3000);&lt;br /&gt;
  ESP.restart();  // Restart the ESP32&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void saveConfig() { // Gemmer konfigurationen i EEPROM&lt;br /&gt;
  EEPROM.put(0, ssid);&lt;br /&gt;
  EEPROM.put(32, password);&lt;br /&gt;
  EEPROM.put(64, mqtt_server);&lt;br /&gt;
  EEPROM.put(128, mqtt_port);&lt;br /&gt;
  EEPROM.put(132, mqtt_user);&lt;br /&gt;
  EEPROM.put(164, mqtt_password);&lt;br /&gt;
  EEPROM.commit();  // Bekræft gemt data&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void readConfig() { // Læs konfigurationen fra EEPROM&lt;br /&gt;
  EEPROM.get(0, ssid);&lt;br /&gt;
  EEPROM.get(32, password);&lt;br /&gt;
  EEPROM.get(64, mqtt_server);&lt;br /&gt;
  EEPROM.get(128, mqtt_port);&lt;br /&gt;
  EEPROM.get(132, mqtt_user);&lt;br /&gt;
  EEPROM.get(164, mqtt_password);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diverse links ==&lt;br /&gt;
* [https://samueladesola.medium.com/how-to-set-up-esp32-wroom-32-b2100060470c How to set up ESP32-WROOM-329] in Arduino IDE.&lt;br /&gt;
&lt;br /&gt;
== Afslutning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Arduino]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2056</id>
		<title>Temperaturføler med ESP32, DS18B20 og OLED-skærm</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2056"/>
		<updated>2024-12-11T19:58:24Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Indledning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== Indkøbsliste ==&lt;br /&gt;
* [https://arduinotech.dk/shop/esp32-usb-c-iot-mainboard-devkitc-cp2102-38pin/ ESP32 USB-C IoT bundkort DevKitC CP2102 38PIN]&lt;br /&gt;
* [https://arduinotech.dk/shop/0-96%e2%80%b3-i2c-iic-spi-serial-128x64-oled-new-model/ 0,96&amp;quot; I2C 128x64 OLED-display]&lt;br /&gt;
* [https://arduinotech.dk/shop/ds18b20-waterproof-digital-thermal-probe-with-3-5mm-mini-audio-jack-1-meter-cable/ DS18B20 vandtæt digital termisk sonde (1 meter kabel)]&lt;br /&gt;
* [https://arduinotech.dk/shop/tsp-xx-replace-hlk-pmxx-ac-dc-220v-to-12v-buck-step-down-power-supply-module-converter/ HLK-PMxx 220VAC til 5VDC Step Down Converter]&lt;br /&gt;
* [https://arduinotech.dk/shop/sonoff-ip66-waterproof-case/ Sonoff IP66 vandtæt etui]&lt;br /&gt;
* [https://arduinotech.dk/shop/prototype-pcb-print/ Prototype PCB print - Type 1]&lt;br /&gt;
&lt;br /&gt;
== Byggevejledning ==&lt;br /&gt;
&lt;br /&gt;
1. Tilslutninger:  &lt;br /&gt;
   - DS18B20:  &lt;br /&gt;
     - VCC (rød) → 3.3V på ESP32  &lt;br /&gt;
     - GND (sort) → GND på ESP32  &lt;br /&gt;
     - Data (gul) → GPIO 4 på ESP32  &lt;br /&gt;
     - Tilføj en 4,7kΩ pull-up modstand mellem Data og VCC.&lt;br /&gt;
&lt;br /&gt;
   - OLED-skærm (I2C):  &lt;br /&gt;
     - VCC → 3.3V på ESP32  &lt;br /&gt;
     - GND → GND på ESP32  &lt;br /&gt;
     - SDA → GPIO 21 på ESP32  &lt;br /&gt;
     - SCL → GPIO 22 på ESP32  &lt;br /&gt;
&lt;br /&gt;
2. Strømforsyning:  &lt;br /&gt;
   Tilslut ESP32 og OLED til 5V strømforsyning via HLK-PMxx-modulet.&lt;br /&gt;
&lt;br /&gt;
3. Montering:  &lt;br /&gt;
   - Montér komponenterne på prototype-PCB.  &lt;br /&gt;
   - Placér hele opsætningen i Sonoff IP66 vandtæt etui for beskyttelse mod vejr og støv.&lt;br /&gt;
&lt;br /&gt;
== Kode til ESP32 ==&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed wikitable&amp;quot;&lt;br /&gt;
! Kopier denne kode til Arduino IDE --&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;WiFi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;WebServer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;OneWire.h&amp;gt;&lt;br /&gt;
#include &amp;lt;DallasTemperature.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_SSD1306.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// === PIN-konfigurationer ===&lt;br /&gt;
#define ONE_WIRE_BUS 4&lt;br /&gt;
#define SCREEN_WIDTH 128&lt;br /&gt;
#define SCREEN_HEIGHT 64&lt;br /&gt;
#define OLED_RESET    -1&lt;br /&gt;
&lt;br /&gt;
// === Globale objekter ===&lt;br /&gt;
OneWire oneWire(ONE_WIRE_BUS);&lt;br /&gt;
DallasTemperature sensors(&amp;amp;oneWire);&lt;br /&gt;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &amp;amp;Wire, OLED_RESET);&lt;br /&gt;
WebServer server(80);&lt;br /&gt;
WiFiClient espClient;&lt;br /&gt;
PubSubClient mqttClient(espClient);&lt;br /&gt;
&lt;br /&gt;
// === Netværksindstillinger ===&lt;br /&gt;
const char* ssid = &amp;quot;Din_SSID&amp;quot;;&lt;br /&gt;
const char* password = &amp;quot;Dit_Password&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// === MQTT-indstillinger (konfigureres senere) ===&lt;br /&gt;
const char* mqtt_server = &amp;quot;MQTT_SERVER_IP&amp;quot;;&lt;br /&gt;
const int mqtt_port = 1883;&lt;br /&gt;
const char* mqtt_user = &amp;quot;MQTT_USER&amp;quot;;&lt;br /&gt;
const char* mqtt_password = &amp;quot;MQTT_PASSWORD&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  sensors.begin();&lt;br /&gt;
  &lt;br /&gt;
  // OLED-initialisering&lt;br /&gt;
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {&lt;br /&gt;
    Serial.println(F(&amp;quot;OLED fejl!&amp;quot;));&lt;br /&gt;
    for (;;);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setTextSize(1);&lt;br /&gt;
  display.setTextColor(WHITE);&lt;br /&gt;
  &lt;br /&gt;
  // Wi-Fi-forbindelse&lt;br /&gt;
  connectWiFi();&lt;br /&gt;
&lt;br /&gt;
  // Webserver-ruter&lt;br /&gt;
  server.on(&amp;quot;/&amp;quot;, handleRoot);&lt;br /&gt;
  server.on(&amp;quot;/reset&amp;quot;, handleReset);&lt;br /&gt;
  server.begin();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  displayTemperature(tempC);&lt;br /&gt;
  &lt;br /&gt;
  server.handleClient();&lt;br /&gt;
&lt;br /&gt;
  // MQTT-forbindelse&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    reconnectMQTT();&lt;br /&gt;
  }&lt;br /&gt;
  mqttClient.loop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void connectWiFi() {&lt;br /&gt;
  displayMessage(&amp;quot;Forbinder til Wi-Fi...&amp;quot;);&lt;br /&gt;
  WiFi.begin(ssid, password);&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED &amp;amp;&amp;amp; attempts &amp;lt; 10) {&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    attempts++;&lt;br /&gt;
    displayMessage(&amp;quot;Forbinder...&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (WiFi.status() != WL_CONNECTED) {&lt;br /&gt;
    displayMessage(&amp;quot;Wi-Fi fejlede&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    displayMessage(&amp;quot;Wi-Fi forbundet!&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayTemperature(float temp) {&lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setCursor(0, 0);&lt;br /&gt;
  display.print(&amp;quot;Temp: &amp;quot;);&lt;br /&gt;
  display.print(temp);&lt;br /&gt;
  display.println(&amp;quot; C&amp;quot;);&lt;br /&gt;
  display.display();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayMessage(String message) {&lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setCursor(0, 0);&lt;br /&gt;
  display.println(message);&lt;br /&gt;
  display.display();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleRoot() {&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Temperatur: &amp;quot; + String(tempC) + &amp;quot; C&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleReset() {&lt;br /&gt;
  WiFi.disconnect();&lt;br /&gt;
  server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Wi-Fi nulstillet. Genstart ESP32.&amp;quot;);&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  ESP.restart();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void reconnectMQTT() {&lt;br /&gt;
  while (!mqttClient.connected()) {&lt;br /&gt;
    if (mqttClient.connect(&amp;quot;ESP32Client&amp;quot;, mqtt_user, mqtt_password)) {&lt;br /&gt;
      mqttClient.publish(&amp;quot;sensor/temperature&amp;quot;, &amp;quot;Forbundet til MQTT&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
      delay(5000);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diverse links ==&lt;br /&gt;
* [https://samueladesola.medium.com/how-to-set-up-esp32-wroom-32-b2100060470c How to set up ESP32-WROOM-329] in Arduino IDE.&lt;br /&gt;
&lt;br /&gt;
== Afslutning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Arduino]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2055</id>
		<title>Temperaturføler med ESP32, DS18B20 og OLED-skærm</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2055"/>
		<updated>2024-12-11T19:53:34Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Indledning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== Indkøbsliste ==&lt;br /&gt;
* [https://arduinotech.dk/shop/esp32-usb-c-iot-mainboard-devkitc-cp2102-38pin/ ESP32 USB-C IoT bundkort DevKitC CP2102 38PIN]&lt;br /&gt;
* [https://arduinotech.dk/shop/0-96%e2%80%b3-i2c-iic-spi-serial-128x64-oled-new-model/ 0,96&amp;quot; I2C 128x64 OLED-display]&lt;br /&gt;
* [https://arduinotech.dk/shop/ds18b20-waterproof-digital-thermal-probe-with-3-5mm-mini-audio-jack-1-meter-cable/ DS18B20 vandtæt digital termisk sonde (1 meter kabel)]&lt;br /&gt;
* [https://arduinotech.dk/shop/tsp-xx-replace-hlk-pmxx-ac-dc-220v-to-12v-buck-step-down-power-supply-module-converter/ HLK-PMxx 220VAC til 5VDC Step Down Converter]&lt;br /&gt;
* [https://arduinotech.dk/shop/sonoff-ip66-waterproof-case/ Sonoff IP66 vandtæt etui]&lt;br /&gt;
* [https://arduinotech.dk/shop/prototype-pcb-print/ Prototype PCB print - Type 1]&lt;br /&gt;
&lt;br /&gt;
== Byggevejledning ==&lt;br /&gt;
&lt;br /&gt;
1. Tilslutninger:  &lt;br /&gt;
   - DS18B20:  &lt;br /&gt;
     - VCC (rød) → 3.3V på ESP32  &lt;br /&gt;
     - GND (sort) → GND på ESP32  &lt;br /&gt;
     - Data (gul) → GPIO 4 på ESP32  &lt;br /&gt;
     - Tilføj en 4,7kΩ pull-up modstand mellem Data og VCC.&lt;br /&gt;
&lt;br /&gt;
   - OLED-skærm (I2C):  &lt;br /&gt;
     - VCC → 3.3V på ESP32  &lt;br /&gt;
     - GND → GND på ESP32  &lt;br /&gt;
     - SDA → GPIO 21 på ESP32  &lt;br /&gt;
     - SCL → GPIO 22 på ESP32  &lt;br /&gt;
&lt;br /&gt;
2. Strømforsyning:  &lt;br /&gt;
   Tilslut ESP32 og OLED til 5V strømforsyning via HLK-PMxx-modulet.&lt;br /&gt;
&lt;br /&gt;
3. Montering:  &lt;br /&gt;
   - Montér komponenterne på prototype-PCB.  &lt;br /&gt;
   - Placér hele opsætningen i Sonoff IP66 vandtæt etui for beskyttelse mod vejr og støv.&lt;br /&gt;
&lt;br /&gt;
== Kode til ESP32 ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;WiFi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;WebServer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;OneWire.h&amp;gt;&lt;br /&gt;
#include &amp;lt;DallasTemperature.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_SSD1306.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// === PIN-konfigurationer ===&lt;br /&gt;
#define ONE_WIRE_BUS 4&lt;br /&gt;
#define SCREEN_WIDTH 128&lt;br /&gt;
#define SCREEN_HEIGHT 64&lt;br /&gt;
#define OLED_RESET    -1&lt;br /&gt;
&lt;br /&gt;
// === Globale objekter ===&lt;br /&gt;
OneWire oneWire(ONE_WIRE_BUS);&lt;br /&gt;
DallasTemperature sensors(&amp;amp;oneWire);&lt;br /&gt;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &amp;amp;Wire, OLED_RESET);&lt;br /&gt;
WebServer server(80);&lt;br /&gt;
WiFiClient espClient;&lt;br /&gt;
PubSubClient mqttClient(espClient);&lt;br /&gt;
&lt;br /&gt;
// === Netværksindstillinger ===&lt;br /&gt;
const char* ssid = &amp;quot;Din_SSID&amp;quot;;&lt;br /&gt;
const char* password = &amp;quot;Dit_Password&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// === MQTT-indstillinger (konfigureres senere) ===&lt;br /&gt;
const char* mqtt_server = &amp;quot;MQTT_SERVER_IP&amp;quot;;&lt;br /&gt;
const int mqtt_port = 1883;&lt;br /&gt;
const char* mqtt_user = &amp;quot;MQTT_USER&amp;quot;;&lt;br /&gt;
const char* mqtt_password = &amp;quot;MQTT_PASSWORD&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  sensors.begin();&lt;br /&gt;
  &lt;br /&gt;
  // OLED-initialisering&lt;br /&gt;
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {&lt;br /&gt;
    Serial.println(F(&amp;quot;OLED fejl!&amp;quot;));&lt;br /&gt;
    for (;;);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setTextSize(1);&lt;br /&gt;
  display.setTextColor(WHITE);&lt;br /&gt;
  &lt;br /&gt;
  // Wi-Fi-forbindelse&lt;br /&gt;
  connectWiFi();&lt;br /&gt;
&lt;br /&gt;
  // Webserver-ruter&lt;br /&gt;
  server.on(&amp;quot;/&amp;quot;, handleRoot);&lt;br /&gt;
  server.on(&amp;quot;/reset&amp;quot;, handleReset);&lt;br /&gt;
  server.begin();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  displayTemperature(tempC);&lt;br /&gt;
  &lt;br /&gt;
  server.handleClient();&lt;br /&gt;
&lt;br /&gt;
  // MQTT-forbindelse&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    reconnectMQTT();&lt;br /&gt;
  }&lt;br /&gt;
  mqttClient.loop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void connectWiFi() {&lt;br /&gt;
  displayMessage(&amp;quot;Forbinder til Wi-Fi...&amp;quot;);&lt;br /&gt;
  WiFi.begin(ssid, password);&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED &amp;amp;&amp;amp; attempts &amp;lt; 10) {&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    attempts++;&lt;br /&gt;
    displayMessage(&amp;quot;Forbinder...&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (WiFi.status() != WL_CONNECTED) {&lt;br /&gt;
    displayMessage(&amp;quot;Wi-Fi fejlede&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    displayMessage(&amp;quot;Wi-Fi forbundet!&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayTemperature(float temp) {&lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setCursor(0, 0);&lt;br /&gt;
  display.print(&amp;quot;Temp: &amp;quot;);&lt;br /&gt;
  display.print(temp);&lt;br /&gt;
  display.println(&amp;quot; C&amp;quot;);&lt;br /&gt;
  display.display();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayMessage(String message) {&lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setCursor(0, 0);&lt;br /&gt;
  display.println(message);&lt;br /&gt;
  display.display();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleRoot() {&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Temperatur: &amp;quot; + String(tempC) + &amp;quot; C&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleReset() {&lt;br /&gt;
  WiFi.disconnect();&lt;br /&gt;
  server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Wi-Fi nulstillet. Genstart ESP32.&amp;quot;);&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  ESP.restart();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void reconnectMQTT() {&lt;br /&gt;
  while (!mqttClient.connected()) {&lt;br /&gt;
    if (mqttClient.connect(&amp;quot;ESP32Client&amp;quot;, mqtt_user, mqtt_password)) {&lt;br /&gt;
      mqttClient.publish(&amp;quot;sensor/temperature&amp;quot;, &amp;quot;Forbundet til MQTT&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
      delay(5000);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Diverse links ==&lt;br /&gt;
* [https://samueladesola.medium.com/how-to-set-up-esp32-wroom-32-b2100060470c How to set up ESP32-WROOM-329] in Arduino IDE.&lt;br /&gt;
&lt;br /&gt;
== Afslutning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Arduino]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2054</id>
		<title>Temperaturføler med ESP32, DS18B20 og OLED-skærm</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2054"/>
		<updated>2024-12-11T19:43:25Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Afslutning */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Indledning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== Indkøbsliste ==&lt;br /&gt;
* [https://arduinotech.dk/shop/esp32-usb-c-iot-mainboard-devkitc-cp2102-38pin/ ESP32 USB-C IoT bundkort DevKitC CP2102 38PIN]&lt;br /&gt;
* [https://arduinotech.dk/shop/0-96%e2%80%b3-i2c-iic-spi-serial-128x64-oled-new-model/ 0,96&amp;quot; I2C 128x64 OLED-display]&lt;br /&gt;
* [https://arduinotech.dk/shop/ds18b20-waterproof-digital-thermal-probe-with-3-5mm-mini-audio-jack-1-meter-cable/ DS18B20 vandtæt digital termisk sonde (1 meter kabel)]&lt;br /&gt;
* [https://arduinotech.dk/shop/tsp-xx-replace-hlk-pmxx-ac-dc-220v-to-12v-buck-step-down-power-supply-module-converter/ HLK-PMxx 220VAC til 5VDC Step Down Converter]&lt;br /&gt;
* [https://arduinotech.dk/shop/sonoff-ip66-waterproof-case/ Sonoff IP66 vandtæt etui]&lt;br /&gt;
* [https://arduinotech.dk/shop/prototype-pcb-print/ Prototype PCB print - Type 1]&lt;br /&gt;
&lt;br /&gt;
== Byggevejledning ==&lt;br /&gt;
&lt;br /&gt;
1. Tilslutninger:  &lt;br /&gt;
   - DS18B20:  &lt;br /&gt;
     - VCC (rød) → 3.3V på ESP32  &lt;br /&gt;
     - GND (sort) → GND på ESP32  &lt;br /&gt;
     - Data (gul) → GPIO 4 på ESP32  &lt;br /&gt;
     - Tilføj en 4,7kΩ pull-up modstand mellem Data og VCC.&lt;br /&gt;
&lt;br /&gt;
   - OLED-skærm (I2C):  &lt;br /&gt;
     - VCC → 3.3V på ESP32  &lt;br /&gt;
     - GND → GND på ESP32  &lt;br /&gt;
     - SDA → GPIO 21 på ESP32  &lt;br /&gt;
     - SCL → GPIO 22 på ESP32  &lt;br /&gt;
&lt;br /&gt;
2. Strømforsyning:  &lt;br /&gt;
   Tilslut ESP32 og OLED til 5V strømforsyning via HLK-PMxx-modulet.&lt;br /&gt;
&lt;br /&gt;
3. Montering:  &lt;br /&gt;
   - Montér komponenterne på prototype-PCB.  &lt;br /&gt;
   - Placér hele opsætningen i Sonoff IP66 vandtæt etui for beskyttelse mod vejr og støv.&lt;br /&gt;
&lt;br /&gt;
== Kode til ESP32 ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;WiFi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;WebServer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;OneWire.h&amp;gt;&lt;br /&gt;
#include &amp;lt;DallasTemperature.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_SSD1306.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// === PIN-konfigurationer ===&lt;br /&gt;
#define ONE_WIRE_BUS 4&lt;br /&gt;
#define SCREEN_WIDTH 128&lt;br /&gt;
#define SCREEN_HEIGHT 64&lt;br /&gt;
#define OLED_RESET    -1&lt;br /&gt;
&lt;br /&gt;
// === Globale objekter ===&lt;br /&gt;
OneWire oneWire(ONE_WIRE_BUS);&lt;br /&gt;
DallasTemperature sensors(&amp;amp;oneWire);&lt;br /&gt;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &amp;amp;Wire, OLED_RESET);&lt;br /&gt;
WebServer server(80);&lt;br /&gt;
WiFiClient espClient;&lt;br /&gt;
PubSubClient mqttClient(espClient);&lt;br /&gt;
&lt;br /&gt;
// === Netværksindstillinger ===&lt;br /&gt;
const char* ssid = &amp;quot;Din_SSID&amp;quot;;&lt;br /&gt;
const char* password = &amp;quot;Dit_Password&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// === MQTT-indstillinger (konfigureres senere) ===&lt;br /&gt;
const char* mqtt_server = &amp;quot;MQTT_SERVER_IP&amp;quot;;&lt;br /&gt;
const int mqtt_port = 1883;&lt;br /&gt;
const char* mqtt_user = &amp;quot;MQTT_USER&amp;quot;;&lt;br /&gt;
const char* mqtt_password = &amp;quot;MQTT_PASSWORD&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  sensors.begin();&lt;br /&gt;
  &lt;br /&gt;
  // OLED-initialisering&lt;br /&gt;
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {&lt;br /&gt;
    Serial.println(F(&amp;quot;OLED fejl!&amp;quot;));&lt;br /&gt;
    for (;;);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setTextSize(1);&lt;br /&gt;
  display.setTextColor(WHITE);&lt;br /&gt;
  &lt;br /&gt;
  // Wi-Fi-forbindelse&lt;br /&gt;
  connectWiFi();&lt;br /&gt;
&lt;br /&gt;
  // Webserver-ruter&lt;br /&gt;
  server.on(&amp;quot;/&amp;quot;, handleRoot);&lt;br /&gt;
  server.on(&amp;quot;/reset&amp;quot;, handleReset);&lt;br /&gt;
  server.begin();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  displayTemperature(tempC);&lt;br /&gt;
  &lt;br /&gt;
  server.handleClient();&lt;br /&gt;
&lt;br /&gt;
  // MQTT-forbindelse&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    reconnectMQTT();&lt;br /&gt;
  }&lt;br /&gt;
  mqttClient.loop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void connectWiFi() {&lt;br /&gt;
  displayMessage(&amp;quot;Forbinder til Wi-Fi...&amp;quot;);&lt;br /&gt;
  WiFi.begin(ssid, password);&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED &amp;amp;&amp;amp; attempts &amp;lt; 10) {&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    attempts++;&lt;br /&gt;
    displayMessage(&amp;quot;Forbinder...&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (WiFi.status() != WL_CONNECTED) {&lt;br /&gt;
    displayMessage(&amp;quot;Wi-Fi fejlede&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    displayMessage(&amp;quot;Wi-Fi forbundet!&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayTemperature(float temp) {&lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setCursor(0, 0);&lt;br /&gt;
  display.print(&amp;quot;Temp: &amp;quot;);&lt;br /&gt;
  display.print(temp);&lt;br /&gt;
  display.println(&amp;quot; C&amp;quot;);&lt;br /&gt;
  display.display();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayMessage(String message) {&lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setCursor(0, 0);&lt;br /&gt;
  display.println(message);&lt;br /&gt;
  display.display();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleRoot() {&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Temperatur: &amp;quot; + String(tempC) + &amp;quot; C&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleReset() {&lt;br /&gt;
  WiFi.disconnect();&lt;br /&gt;
  server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Wi-Fi nulstillet. Genstart ESP32.&amp;quot;);&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  ESP.restart();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void reconnectMQTT() {&lt;br /&gt;
  while (!mqttClient.connected()) {&lt;br /&gt;
    if (mqttClient.connect(&amp;quot;ESP32Client&amp;quot;, mqtt_user, mqtt_password)) {&lt;br /&gt;
      mqttClient.publish(&amp;quot;sensor/temperature&amp;quot;, &amp;quot;Forbundet til MQTT&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
      delay(5000);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Diverse links ==&lt;br /&gt;
* [https://samueladesola.medium.com/how-to-set-up-esp32-wroom-32-b2100060470c How to set up ESP32-WROOM-329] in Arduino IDE.&lt;br /&gt;
&lt;br /&gt;
== Afslutning ==&lt;br /&gt;
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.&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2053</id>
		<title>Temperaturføler med ESP32, DS18B20 og OLED-skærm</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2053"/>
		<updated>2024-12-08T19:04:59Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Byggevejledning */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Indledning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== Indkøbsliste ==&lt;br /&gt;
* [https://arduinotech.dk/shop/esp32-usb-c-iot-mainboard-devkitc-cp2102-38pin/ ESP32 USB-C IoT bundkort DevKitC CP2102 38PIN]&lt;br /&gt;
* [https://arduinotech.dk/shop/0-96%e2%80%b3-i2c-iic-spi-serial-128x64-oled-new-model/ 0,96&amp;quot; I2C 128x64 OLED-display]&lt;br /&gt;
* [https://arduinotech.dk/shop/ds18b20-waterproof-digital-thermal-probe-with-3-5mm-mini-audio-jack-1-meter-cable/ DS18B20 vandtæt digital termisk sonde (1 meter kabel)]&lt;br /&gt;
* [https://arduinotech.dk/shop/tsp-xx-replace-hlk-pmxx-ac-dc-220v-to-12v-buck-step-down-power-supply-module-converter/ HLK-PMxx 220VAC til 5VDC Step Down Converter]&lt;br /&gt;
* [https://arduinotech.dk/shop/sonoff-ip66-waterproof-case/ Sonoff IP66 vandtæt etui]&lt;br /&gt;
* [https://arduinotech.dk/shop/prototype-pcb-print/ Prototype PCB print - Type 1]&lt;br /&gt;
&lt;br /&gt;
== Byggevejledning ==&lt;br /&gt;
&lt;br /&gt;
1. Tilslutninger:  &lt;br /&gt;
   - DS18B20:  &lt;br /&gt;
     - VCC (rød) → 3.3V på ESP32  &lt;br /&gt;
     - GND (sort) → GND på ESP32  &lt;br /&gt;
     - Data (gul) → GPIO 4 på ESP32  &lt;br /&gt;
     - Tilføj en 4,7kΩ pull-up modstand mellem Data og VCC.&lt;br /&gt;
&lt;br /&gt;
   - OLED-skærm (I2C):  &lt;br /&gt;
     - VCC → 3.3V på ESP32  &lt;br /&gt;
     - GND → GND på ESP32  &lt;br /&gt;
     - SDA → GPIO 21 på ESP32  &lt;br /&gt;
     - SCL → GPIO 22 på ESP32  &lt;br /&gt;
&lt;br /&gt;
2. Strømforsyning:  &lt;br /&gt;
   Tilslut ESP32 og OLED til 5V strømforsyning via HLK-PMxx-modulet.&lt;br /&gt;
&lt;br /&gt;
3. Montering:  &lt;br /&gt;
   - Montér komponenterne på prototype-PCB.  &lt;br /&gt;
   - Placér hele opsætningen i Sonoff IP66 vandtæt etui for beskyttelse mod vejr og støv.&lt;br /&gt;
&lt;br /&gt;
== Kode til ESP32 ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;WiFi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;WebServer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;OneWire.h&amp;gt;&lt;br /&gt;
#include &amp;lt;DallasTemperature.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_SSD1306.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// === PIN-konfigurationer ===&lt;br /&gt;
#define ONE_WIRE_BUS 4&lt;br /&gt;
#define SCREEN_WIDTH 128&lt;br /&gt;
#define SCREEN_HEIGHT 64&lt;br /&gt;
#define OLED_RESET    -1&lt;br /&gt;
&lt;br /&gt;
// === Globale objekter ===&lt;br /&gt;
OneWire oneWire(ONE_WIRE_BUS);&lt;br /&gt;
DallasTemperature sensors(&amp;amp;oneWire);&lt;br /&gt;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &amp;amp;Wire, OLED_RESET);&lt;br /&gt;
WebServer server(80);&lt;br /&gt;
WiFiClient espClient;&lt;br /&gt;
PubSubClient mqttClient(espClient);&lt;br /&gt;
&lt;br /&gt;
// === Netværksindstillinger ===&lt;br /&gt;
const char* ssid = &amp;quot;Din_SSID&amp;quot;;&lt;br /&gt;
const char* password = &amp;quot;Dit_Password&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// === MQTT-indstillinger (konfigureres senere) ===&lt;br /&gt;
const char* mqtt_server = &amp;quot;MQTT_SERVER_IP&amp;quot;;&lt;br /&gt;
const int mqtt_port = 1883;&lt;br /&gt;
const char* mqtt_user = &amp;quot;MQTT_USER&amp;quot;;&lt;br /&gt;
const char* mqtt_password = &amp;quot;MQTT_PASSWORD&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  sensors.begin();&lt;br /&gt;
  &lt;br /&gt;
  // OLED-initialisering&lt;br /&gt;
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {&lt;br /&gt;
    Serial.println(F(&amp;quot;OLED fejl!&amp;quot;));&lt;br /&gt;
    for (;;);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setTextSize(1);&lt;br /&gt;
  display.setTextColor(WHITE);&lt;br /&gt;
  &lt;br /&gt;
  // Wi-Fi-forbindelse&lt;br /&gt;
  connectWiFi();&lt;br /&gt;
&lt;br /&gt;
  // Webserver-ruter&lt;br /&gt;
  server.on(&amp;quot;/&amp;quot;, handleRoot);&lt;br /&gt;
  server.on(&amp;quot;/reset&amp;quot;, handleReset);&lt;br /&gt;
  server.begin();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  displayTemperature(tempC);&lt;br /&gt;
  &lt;br /&gt;
  server.handleClient();&lt;br /&gt;
&lt;br /&gt;
  // MQTT-forbindelse&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    reconnectMQTT();&lt;br /&gt;
  }&lt;br /&gt;
  mqttClient.loop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void connectWiFi() {&lt;br /&gt;
  displayMessage(&amp;quot;Forbinder til Wi-Fi...&amp;quot;);&lt;br /&gt;
  WiFi.begin(ssid, password);&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED &amp;amp;&amp;amp; attempts &amp;lt; 10) {&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    attempts++;&lt;br /&gt;
    displayMessage(&amp;quot;Forbinder...&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (WiFi.status() != WL_CONNECTED) {&lt;br /&gt;
    displayMessage(&amp;quot;Wi-Fi fejlede&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    displayMessage(&amp;quot;Wi-Fi forbundet!&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayTemperature(float temp) {&lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setCursor(0, 0);&lt;br /&gt;
  display.print(&amp;quot;Temp: &amp;quot;);&lt;br /&gt;
  display.print(temp);&lt;br /&gt;
  display.println(&amp;quot; C&amp;quot;);&lt;br /&gt;
  display.display();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayMessage(String message) {&lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setCursor(0, 0);&lt;br /&gt;
  display.println(message);&lt;br /&gt;
  display.display();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleRoot() {&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Temperatur: &amp;quot; + String(tempC) + &amp;quot; C&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleReset() {&lt;br /&gt;
  WiFi.disconnect();&lt;br /&gt;
  server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Wi-Fi nulstillet. Genstart ESP32.&amp;quot;);&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  ESP.restart();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void reconnectMQTT() {&lt;br /&gt;
  while (!mqttClient.connected()) {&lt;br /&gt;
    if (mqttClient.connect(&amp;quot;ESP32Client&amp;quot;, mqtt_user, mqtt_password)) {&lt;br /&gt;
      mqttClient.publish(&amp;quot;sensor/temperature&amp;quot;, &amp;quot;Forbundet til MQTT&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
      delay(5000);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Afslutning ==&lt;br /&gt;
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.&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2052</id>
		<title>Temperaturføler med ESP32, DS18B20 og OLED-skærm</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2052"/>
		<updated>2024-12-08T19:03:54Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Indkøbsliste */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Indledning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== Indkøbsliste ==&lt;br /&gt;
* [https://arduinotech.dk/shop/esp32-usb-c-iot-mainboard-devkitc-cp2102-38pin/ ESP32 USB-C IoT bundkort DevKitC CP2102 38PIN]&lt;br /&gt;
* [https://arduinotech.dk/shop/0-96%e2%80%b3-i2c-iic-spi-serial-128x64-oled-new-model/ 0,96&amp;quot; I2C 128x64 OLED-display]&lt;br /&gt;
* [https://arduinotech.dk/shop/ds18b20-waterproof-digital-thermal-probe-with-3-5mm-mini-audio-jack-1-meter-cable/ DS18B20 vandtæt digital termisk sonde (1 meter kabel)]&lt;br /&gt;
* [https://arduinotech.dk/shop/tsp-xx-replace-hlk-pmxx-ac-dc-220v-to-12v-buck-step-down-power-supply-module-converter/ HLK-PMxx 220VAC til 5VDC Step Down Converter]&lt;br /&gt;
* [https://arduinotech.dk/shop/sonoff-ip66-waterproof-case/ Sonoff IP66 vandtæt etui]&lt;br /&gt;
* [https://arduinotech.dk/shop/prototype-pcb-print/ Prototype PCB print - Type 1]&lt;br /&gt;
&lt;br /&gt;
== Byggevejledning ==&lt;br /&gt;
&lt;br /&gt;
1. Tilslutninger:  &lt;br /&gt;
   - DS18B20:  &lt;br /&gt;
     - VCC (rød) → 3.3V på ESP32  &lt;br /&gt;
     - GND (sort) → GND på ESP32  &lt;br /&gt;
     - Data (gul) → GPIO 4 på ESP32  &lt;br /&gt;
     - Tilføj en 4,7kΩ pull-up modstand mellem Data og VCC.&lt;br /&gt;
&lt;br /&gt;
   - OLED-skærm (I2C):  &lt;br /&gt;
     - VCC → 3.3V på ESP32  &lt;br /&gt;
     - GND → GND på ESP32  &lt;br /&gt;
     - SDA → GPIO 21 på ESP32  &lt;br /&gt;
     - SCL → GPIO 22 på ESP32  &lt;br /&gt;
&lt;br /&gt;
2. Strømforsyning:  &lt;br /&gt;
   Tilslut ESP32 og OLED til en stabil 5V strømforsyning via HLK-PMxx-modulet.&lt;br /&gt;
&lt;br /&gt;
3. Montering:  &lt;br /&gt;
   - Montér komponenterne på prototype-PCB.  &lt;br /&gt;
   - Placér hele opsætningen i Sonoff IP66 vandtæt etui for beskyttelse mod vejr og støv.&lt;br /&gt;
&lt;br /&gt;
== Kode til ESP32 ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;WiFi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;WebServer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;OneWire.h&amp;gt;&lt;br /&gt;
#include &amp;lt;DallasTemperature.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_SSD1306.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// === PIN-konfigurationer ===&lt;br /&gt;
#define ONE_WIRE_BUS 4&lt;br /&gt;
#define SCREEN_WIDTH 128&lt;br /&gt;
#define SCREEN_HEIGHT 64&lt;br /&gt;
#define OLED_RESET    -1&lt;br /&gt;
&lt;br /&gt;
// === Globale objekter ===&lt;br /&gt;
OneWire oneWire(ONE_WIRE_BUS);&lt;br /&gt;
DallasTemperature sensors(&amp;amp;oneWire);&lt;br /&gt;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &amp;amp;Wire, OLED_RESET);&lt;br /&gt;
WebServer server(80);&lt;br /&gt;
WiFiClient espClient;&lt;br /&gt;
PubSubClient mqttClient(espClient);&lt;br /&gt;
&lt;br /&gt;
// === Netværksindstillinger ===&lt;br /&gt;
const char* ssid = &amp;quot;Din_SSID&amp;quot;;&lt;br /&gt;
const char* password = &amp;quot;Dit_Password&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// === MQTT-indstillinger (konfigureres senere) ===&lt;br /&gt;
const char* mqtt_server = &amp;quot;MQTT_SERVER_IP&amp;quot;;&lt;br /&gt;
const int mqtt_port = 1883;&lt;br /&gt;
const char* mqtt_user = &amp;quot;MQTT_USER&amp;quot;;&lt;br /&gt;
const char* mqtt_password = &amp;quot;MQTT_PASSWORD&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  sensors.begin();&lt;br /&gt;
  &lt;br /&gt;
  // OLED-initialisering&lt;br /&gt;
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {&lt;br /&gt;
    Serial.println(F(&amp;quot;OLED fejl!&amp;quot;));&lt;br /&gt;
    for (;;);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setTextSize(1);&lt;br /&gt;
  display.setTextColor(WHITE);&lt;br /&gt;
  &lt;br /&gt;
  // Wi-Fi-forbindelse&lt;br /&gt;
  connectWiFi();&lt;br /&gt;
&lt;br /&gt;
  // Webserver-ruter&lt;br /&gt;
  server.on(&amp;quot;/&amp;quot;, handleRoot);&lt;br /&gt;
  server.on(&amp;quot;/reset&amp;quot;, handleReset);&lt;br /&gt;
  server.begin();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  displayTemperature(tempC);&lt;br /&gt;
  &lt;br /&gt;
  server.handleClient();&lt;br /&gt;
&lt;br /&gt;
  // MQTT-forbindelse&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    reconnectMQTT();&lt;br /&gt;
  }&lt;br /&gt;
  mqttClient.loop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void connectWiFi() {&lt;br /&gt;
  displayMessage(&amp;quot;Forbinder til Wi-Fi...&amp;quot;);&lt;br /&gt;
  WiFi.begin(ssid, password);&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED &amp;amp;&amp;amp; attempts &amp;lt; 10) {&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    attempts++;&lt;br /&gt;
    displayMessage(&amp;quot;Forbinder...&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (WiFi.status() != WL_CONNECTED) {&lt;br /&gt;
    displayMessage(&amp;quot;Wi-Fi fejlede&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    displayMessage(&amp;quot;Wi-Fi forbundet!&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayTemperature(float temp) {&lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setCursor(0, 0);&lt;br /&gt;
  display.print(&amp;quot;Temp: &amp;quot;);&lt;br /&gt;
  display.print(temp);&lt;br /&gt;
  display.println(&amp;quot; C&amp;quot;);&lt;br /&gt;
  display.display();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayMessage(String message) {&lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setCursor(0, 0);&lt;br /&gt;
  display.println(message);&lt;br /&gt;
  display.display();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleRoot() {&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Temperatur: &amp;quot; + String(tempC) + &amp;quot; C&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleReset() {&lt;br /&gt;
  WiFi.disconnect();&lt;br /&gt;
  server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Wi-Fi nulstillet. Genstart ESP32.&amp;quot;);&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  ESP.restart();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void reconnectMQTT() {&lt;br /&gt;
  while (!mqttClient.connected()) {&lt;br /&gt;
    if (mqttClient.connect(&amp;quot;ESP32Client&amp;quot;, mqtt_user, mqtt_password)) {&lt;br /&gt;
      mqttClient.publish(&amp;quot;sensor/temperature&amp;quot;, &amp;quot;Forbundet til MQTT&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
      delay(5000);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Afslutning ==&lt;br /&gt;
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.&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2051</id>
		<title>Temperaturføler med ESP32, DS18B20 og OLED-skærm</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Temperaturf%C3%B8ler_med_ESP32,_DS18B20_og_OLED-sk%C3%A6rm&amp;diff=2051"/>
		<updated>2024-12-08T18:58:47Z</updated>

		<summary type="html">&lt;p&gt;Admin: Created page with &amp;quot;== 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 == * [https://arduinotech.dk/shop/esp32-usb-c-iot-mainboard-devkitc-cp2102-38pin/ ESP32 US...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Indledning ==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== Indkøbsliste ==&lt;br /&gt;
* [https://arduinotech.dk/shop/esp32-usb-c-iot-mainboard-devkitc-cp2102-38pin/ ESP32 USB-C IoT bundkort DevKitC CP2102 38PIN]&lt;br /&gt;
* [https://arduinotech.dk/shop/096-i2c-iic-spi-seriel-128x64-oled-ny-model-096-oled-4-pin-blue/ 0,96&amp;quot; I2C 128x64 OLED-display (blue)]&lt;br /&gt;
* [https://arduinotech.dk/shop/ds18b20-vandtaet-digital-termisk-sonde/ DS18B20 vandtæt digital termisk sonde (1 meter kabel)]&lt;br /&gt;
* [https://arduinotech.dk/shop/hlk-pmxx-220vac-til-3-3v-eller-5v-eller-12vdc-buck-step-down-power-supply-module-converter-5vdc3w/ HLK-PMxx 220VAC til 5VDC Step Down Converter]&lt;br /&gt;
* [https://arduinotech.dk/shop/sonoff-ip66-vandtaet-etui/ Sonoff IP66 vandtæt etui]&lt;br /&gt;
* [https://arduinotech.dk/shop/prototype-pcb-print-type-1/ Prototype PCB print - Type 1]&lt;br /&gt;
&lt;br /&gt;
== Byggevejledning ==&lt;br /&gt;
&lt;br /&gt;
1. Tilslutninger:  &lt;br /&gt;
   - DS18B20:  &lt;br /&gt;
     - VCC (rød) → 3.3V på ESP32  &lt;br /&gt;
     - GND (sort) → GND på ESP32  &lt;br /&gt;
     - Data (gul) → GPIO 4 på ESP32  &lt;br /&gt;
     - Tilføj en 4,7kΩ pull-up modstand mellem Data og VCC.&lt;br /&gt;
&lt;br /&gt;
   - OLED-skærm (I2C):  &lt;br /&gt;
     - VCC → 3.3V på ESP32  &lt;br /&gt;
     - GND → GND på ESP32  &lt;br /&gt;
     - SDA → GPIO 21 på ESP32  &lt;br /&gt;
     - SCL → GPIO 22 på ESP32  &lt;br /&gt;
&lt;br /&gt;
2. Strømforsyning:  &lt;br /&gt;
   Tilslut ESP32 og OLED til en stabil 5V strømforsyning via HLK-PMxx-modulet.&lt;br /&gt;
&lt;br /&gt;
3. Montering:  &lt;br /&gt;
   - Montér komponenterne på prototype-PCB.  &lt;br /&gt;
   - Placér hele opsætningen i Sonoff IP66 vandtæt etui for beskyttelse mod vejr og støv.&lt;br /&gt;
&lt;br /&gt;
== Kode til ESP32 ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;WiFi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;WebServer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;OneWire.h&amp;gt;&lt;br /&gt;
#include &amp;lt;DallasTemperature.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Adafruit_SSD1306.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// === PIN-konfigurationer ===&lt;br /&gt;
#define ONE_WIRE_BUS 4&lt;br /&gt;
#define SCREEN_WIDTH 128&lt;br /&gt;
#define SCREEN_HEIGHT 64&lt;br /&gt;
#define OLED_RESET    -1&lt;br /&gt;
&lt;br /&gt;
// === Globale objekter ===&lt;br /&gt;
OneWire oneWire(ONE_WIRE_BUS);&lt;br /&gt;
DallasTemperature sensors(&amp;amp;oneWire);&lt;br /&gt;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &amp;amp;Wire, OLED_RESET);&lt;br /&gt;
WebServer server(80);&lt;br /&gt;
WiFiClient espClient;&lt;br /&gt;
PubSubClient mqttClient(espClient);&lt;br /&gt;
&lt;br /&gt;
// === Netværksindstillinger ===&lt;br /&gt;
const char* ssid = &amp;quot;Din_SSID&amp;quot;;&lt;br /&gt;
const char* password = &amp;quot;Dit_Password&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// === MQTT-indstillinger (konfigureres senere) ===&lt;br /&gt;
const char* mqtt_server = &amp;quot;MQTT_SERVER_IP&amp;quot;;&lt;br /&gt;
const int mqtt_port = 1883;&lt;br /&gt;
const char* mqtt_user = &amp;quot;MQTT_USER&amp;quot;;&lt;br /&gt;
const char* mqtt_password = &amp;quot;MQTT_PASSWORD&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  sensors.begin();&lt;br /&gt;
  &lt;br /&gt;
  // OLED-initialisering&lt;br /&gt;
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {&lt;br /&gt;
    Serial.println(F(&amp;quot;OLED fejl!&amp;quot;));&lt;br /&gt;
    for (;;);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setTextSize(1);&lt;br /&gt;
  display.setTextColor(WHITE);&lt;br /&gt;
  &lt;br /&gt;
  // Wi-Fi-forbindelse&lt;br /&gt;
  connectWiFi();&lt;br /&gt;
&lt;br /&gt;
  // Webserver-ruter&lt;br /&gt;
  server.on(&amp;quot;/&amp;quot;, handleRoot);&lt;br /&gt;
  server.on(&amp;quot;/reset&amp;quot;, handleReset);&lt;br /&gt;
  server.begin();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  displayTemperature(tempC);&lt;br /&gt;
  &lt;br /&gt;
  server.handleClient();&lt;br /&gt;
&lt;br /&gt;
  // MQTT-forbindelse&lt;br /&gt;
  if (!mqttClient.connected()) {&lt;br /&gt;
    reconnectMQTT();&lt;br /&gt;
  }&lt;br /&gt;
  mqttClient.loop();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void connectWiFi() {&lt;br /&gt;
  displayMessage(&amp;quot;Forbinder til Wi-Fi...&amp;quot;);&lt;br /&gt;
  WiFi.begin(ssid, password);&lt;br /&gt;
  int attempts = 0;&lt;br /&gt;
  while (WiFi.status() != WL_CONNECTED &amp;amp;&amp;amp; attempts &amp;lt; 10) {&lt;br /&gt;
    delay(1000);&lt;br /&gt;
    attempts++;&lt;br /&gt;
    displayMessage(&amp;quot;Forbinder...&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (WiFi.status() != WL_CONNECTED) {&lt;br /&gt;
    displayMessage(&amp;quot;Wi-Fi fejlede&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    displayMessage(&amp;quot;Wi-Fi forbundet!&amp;quot;);&lt;br /&gt;
    Serial.println(WiFi.localIP());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayTemperature(float temp) {&lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setCursor(0, 0);&lt;br /&gt;
  display.print(&amp;quot;Temp: &amp;quot;);&lt;br /&gt;
  display.print(temp);&lt;br /&gt;
  display.println(&amp;quot; C&amp;quot;);&lt;br /&gt;
  display.display();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void displayMessage(String message) {&lt;br /&gt;
  display.clearDisplay();&lt;br /&gt;
  display.setCursor(0, 0);&lt;br /&gt;
  display.println(message);&lt;br /&gt;
  display.display();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleRoot() {&lt;br /&gt;
  sensors.requestTemperatures();&lt;br /&gt;
  float tempC = sensors.getTempCByIndex(0);&lt;br /&gt;
  server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Temperatur: &amp;quot; + String(tempC) + &amp;quot; C&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void handleReset() {&lt;br /&gt;
  WiFi.disconnect();&lt;br /&gt;
  server.send(200, &amp;quot;text/plain&amp;quot;, &amp;quot;Wi-Fi nulstillet. Genstart ESP32.&amp;quot;);&lt;br /&gt;
  delay(1000);&lt;br /&gt;
  ESP.restart();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void reconnectMQTT() {&lt;br /&gt;
  while (!mqttClient.connected()) {&lt;br /&gt;
    if (mqttClient.connect(&amp;quot;ESP32Client&amp;quot;, mqtt_user, mqtt_password)) {&lt;br /&gt;
      mqttClient.publish(&amp;quot;sensor/temperature&amp;quot;, &amp;quot;Forbundet til MQTT&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
      delay(5000);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Afslutning ==&lt;br /&gt;
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.&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Main_Page&amp;diff=2050</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Main_Page&amp;diff=2050"/>
		<updated>2024-07-30T09:48:53Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&amp;lt;!-- START TABLE --&amp;gt;&lt;br /&gt;
{| style=&amp;quot;width: 100%; margin:4px 0 0 0; background:none; border-spacing: 0px;&amp;quot;&lt;br /&gt;
| class=&amp;quot;MainPageBG&amp;quot; style=&amp;quot;width:40%; border:1px solid #cef2e0; background:#f5fffa; vertical-align:top; color:#000;&amp;quot; |&lt;br /&gt;
{| style=&amp;quot;vertical-align:top; background:#f5fffa; width:100%&amp;quot;&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center; white-space:nowrap; color:#000;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-size:162%; border:none; margin:0; padding:.1em; color:#000;&amp;quot;&amp;gt;Welcome to&amp;lt;/div&amp;gt;&amp;lt;span style=&amp;quot;font-size:142%; border:none; margin:0; padding:.1em; color:#000;&amp;quot;&amp;gt;munkjensen.net/wiki&amp;lt;/span&amp;gt;&lt;br /&gt;
[[File:TiredGuardianAngel.png|frameless|Tired guardian angel.]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;top:+0.2em; font-size:95%;&amp;quot;&amp;gt;the encyclopedia that only &#039;&#039;&#039;I&#039;&#039;&#039; can edit.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;!-- Highlights --&amp;gt;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;padding:2px;&amp;quot; | &amp;lt;h2 style=&amp;quot;margin:3px; background:#cef2e0; font-size:120%; font-weight:bold; border:1px solid #a3bfb1; text-align:left; color:#000; padding:0.2em 0.4em;&amp;quot;&amp;gt;Highlights&amp;lt;/h2&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;color:#000; padding:2px 5px 5px; width:50%;&amp;quot; |  &lt;br /&gt;
* [[El spotpris]]&lt;br /&gt;
* [[RainViewer]]&lt;br /&gt;
* [[DMI Vejr Ikast|DMI Weather Ikast]]&lt;br /&gt;
* [[Ender 5 Plus + Klipper]].&lt;br /&gt;
* [[Escape from Tarkov guide]]&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- CATEGORIES --&amp;gt;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;padding:2px;&amp;quot; |&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2 style=&amp;quot;margin:3px; background:#cedff2; font-size:120%; font-weight:bold; border:1px solid #a3bfb1; text-align:left; color:#000; padding:0.2em 0.4em;&amp;quot;&amp;gt;[[:Special:Categories|Categories]]&amp;lt;/h2&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;color:#000; padding:2px 5px 5px; width:50%;&amp;quot; style=&amp;quot;color:#000; padding:2px 5px 5px;&amp;quot;| &lt;br /&gt;
* [[:Category:Apple|Apple]].&lt;br /&gt;
* [[:Category:Pi-Hole|Pi-Hole]].&lt;br /&gt;
* [[:Category:Windows|Windows]].&lt;br /&gt;
* [[: category:Elgato_Stream_Deck| Elgato Stream Deck]].&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=PowerShell_stuph...&amp;diff=2049</id>
		<title>PowerShell stuph...</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=PowerShell_stuph...&amp;diff=2049"/>
		<updated>2024-01-20T10:50:39Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Scripts i (ab)use */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&#039;&#039;&#039;Do &#039;&#039;not&#039;&#039; use these scripts if you do not understand 100% what they do ! &#039;&#039;&#039;&amp;lt;/center&amp;gt;&lt;br /&gt;
[http://www.howtogeek.com/141495/geek-school-writing-your-first-full-powershell-script/ Go here] to get started on PowerShell.&lt;br /&gt;
= Scripts i (ab)use =&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed wikitable&amp;quot;&lt;br /&gt;
! Setting network type using PowerShell   --&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Open PowerShell with &#039;&#039;administrative privileges&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 Run the following command:&lt;br /&gt;
&amp;lt;pre&amp;gt;Get-NetConnectionProfile&amp;lt;/pre&amp;gt;&lt;br /&gt;
 Find the network &#039;&#039;Name&#039;&#039; where you want to change its type and run the following command:&lt;br /&gt;
&amp;lt;pre&amp;gt;Set-NetConnectionProfile -Name &amp;quot;ChangeThis&amp;quot; -NetworkCategory Public&amp;lt;/pre&amp;gt;&lt;br /&gt;
 Where &#039;&#039;ChangeThis&#039;&#039; is the name of your network.&lt;br /&gt;
 You can change the -NetworkCategory switch value to Public or Private.&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed wikitable&amp;quot;&lt;br /&gt;
! Config NIC as DHCP Client   --&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;pre&amp;gt;$IPType = &amp;quot;IPv4&amp;quot;&lt;br /&gt;
$adapter = Get-NetAdapter | ? {$_.Status -eq &amp;quot;up&amp;quot;}&lt;br /&gt;
$interface = $adapter | Get-NetIPInterface -AddressFamily $IPType&lt;br /&gt;
&lt;br /&gt;
If ($interface.Dhcp -eq &amp;quot;Disabled&amp;quot;) {&lt;br /&gt;
    # Remove existing gateway&lt;br /&gt;
    If (($interface | Get-NetIPConfiguration).Ipv4DefaultGateway) {&lt;br /&gt;
        $interface | Remove-NetRoute -Confirm:$false&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Enable DHCP&lt;br /&gt;
    $interface | Set-NetIPInterface -DHCP Enabled&lt;br /&gt;
&lt;br /&gt;
    # Configure the  DNS Servers automatically&lt;br /&gt;
    $interface | Set-DnsClientServerAddress -ResetServerAddresses&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed wikitable&amp;quot;&lt;br /&gt;
! Config NIC with static IP --&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;pre&amp;gt;$IP = &amp;quot;172.29.106.41&amp;quot;&lt;br /&gt;
$MaskBits = 27 # 24 = 255.255.255.0 | 27 = 255.255.255.224&lt;br /&gt;
$Gateway = &amp;quot;172.29.106.62&amp;quot;&lt;br /&gt;
$Dns = &amp;quot;172.29.10.69&amp;quot;&lt;br /&gt;
$IPType = &amp;quot;IPv4&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Retrieve the network adapter that you want to configure&lt;br /&gt;
$adapter = Get-NetAdapter | ? {$_.Status -eq &amp;quot;up&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
# Remove any existing IP, gateway from our ipv4 adapter&lt;br /&gt;
If (($adapter | Get-NetIPConfiguration).IPv4Address.IPAddress) {&lt;br /&gt;
    $adapter | Remove-NetIPAddress -AddressFamily $IPType -Confirm:$false&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
If (($adapter | Get-NetIPConfiguration).Ipv4DefaultGateway) {&lt;br /&gt;
    $adapter | Remove-NetRoute -AddressFamily $IPType -Confirm:$false&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Configure the IP address and default gateway&lt;br /&gt;
$adapter | New-NetIPAddress `&lt;br /&gt;
    -AddressFamily $IPType `&lt;br /&gt;
    -IPAddress $IP `&lt;br /&gt;
    -PrefixLength $MaskBits `&lt;br /&gt;
    -DefaultGateway $Gateway&lt;br /&gt;
&lt;br /&gt;
# Configure the DNS client server IP addresses&lt;br /&gt;
$adapter | Set-DnsClientServerAddress -ServerAddresses $DNS&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed wikitable&amp;quot;&lt;br /&gt;
! Uninstall Win 10 CrapWare --&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;pre&amp;gt;Get-AppxPackage *3dbuilder* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *windowsalarms* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *windowscommunicationsapps* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *windowscamera* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *officehub* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *skypeapp* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *getstarted* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *zunemusic* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *windowsmaps* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *bingfinance* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *zunevideo* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *bingnews* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *onenote* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *people* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *windowsphone* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *photos* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *bingsports* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *bingweather* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *xboxapp* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *candy* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *sway* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *solitaire* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *messaging* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *WindowsSoundRecorder* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *windowscommunicationsapps* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *YourPhone* | Remove-AppxPackage&lt;br /&gt;
Get-AppxPackage *sticky* | Remove-AppxPackage&lt;br /&gt;
&lt;br /&gt;
Get-ProvisionedAppxPackage -Online | Where-Object { $_.PackageName -match &amp;quot;xbox&amp;quot; } | ForEach-Object { Remove-ProvisionedAppxPackage -Online -PackageName $_.PackageName }&lt;br /&gt;
Get-AppxPackage | Where-Object { $_.Name -match &amp;quot;xbox&amp;quot; } | Remove-AppxPackage&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;mw-collapsible mw-collapsed wikitable&amp;quot;&lt;br /&gt;
! Uninstall OneDrive (Commandline script) --&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;pre&amp;gt;REM *** Uninstall OneDrive ***&lt;br /&gt;
start /wait &amp;quot;&amp;quot; &amp;quot;%SYSTEMROOT%\SYSWOW64\ONEDRIVESETUP.EXE&amp;quot; /UNINSTALL&lt;br /&gt;
rd C:\OneDriveTemp /Q /S &amp;gt;NUL 2&amp;gt;&amp;amp;1&lt;br /&gt;
rd &amp;quot;%USERPROFILE%\OneDrive&amp;quot; /Q /S &amp;gt;NUL 2&amp;gt;&amp;amp;1&lt;br /&gt;
rd &amp;quot;%LOCALAPPDATA%\Microsoft\OneDrive&amp;quot; /Q /S &amp;gt;NUL 2&amp;gt;&amp;amp;1&lt;br /&gt;
rd &amp;quot;%PROGRAMDATA%\Microsoft OneDrive&amp;quot; /Q /S &amp;gt;NUL 2&amp;gt;&amp;amp;1&lt;br /&gt;
reg add &amp;quot;HKEY_CLASSES_ROOT\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}\ShellFolder&amp;quot; /f /v Attributes /t REG_DWORD /d 0 &amp;gt;NUL 2&amp;gt;&amp;amp;1&lt;br /&gt;
reg add &amp;quot;HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}\ShellFolder&amp;quot; /f /v Attributes /t REG_DWORD /d 0 &amp;gt;NUL 2&amp;gt;&amp;amp;1&lt;br /&gt;
echo OneDrive has been removed. Windows Explorer needs to be restarted.&lt;br /&gt;
pause&lt;br /&gt;
start /wait TASKKILL /F /IM explorer.exe&lt;br /&gt;
start explorer.exe&amp;lt;/pre&amp;gt;&lt;br /&gt;
Credits: https://gist.github.com/matthewjberger/2f4295887d6cb5738fa34e597f457b7f#file-remove-windows10-bloat-bat-L124&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Windows]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=Cherry_MX_Board_1.0_RGB&amp;diff=2048</id>
		<title>Cherry MX Board 1.0 RGB</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=Cherry_MX_Board_1.0_RGB&amp;diff=2048"/>
		<updated>2023-11-28T08:56:18Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;I bought this mechanical keyboard resently and experienced some problems with the RGB part of it. It was not possible for me to edit or change anything as the [https://www.cherry-world.com/software Cherry Utility software] (v 1.0.9) gave an expression of should be possible. My keyboard was shipped with the 0.1.0.1 firmware and i had a suspicion that it might need an upgrade due to the way it behaved.&lt;br /&gt;
&lt;br /&gt;
I searched the accompanying documentation (it was very sparse) and Intarwebs but found absolutely nothing that indicated any firmware updates were released :-/&lt;br /&gt;
&lt;br /&gt;
Some time later I was looking thrugh the installation folders and suddenly found what very much looked like firmware upgrade files for SEVERAL diferent Cherry Keyboards !!&lt;br /&gt;
&lt;br /&gt;
It turns out that this keyboard can easily be firmware upgraded althoug I could not find any information on how to do it.&lt;br /&gt;
&lt;br /&gt;
All i had to du was run the file that corrosponds to the keyboard model i got :-)&lt;br /&gt;
(i did it as administrator just to be shure it would go without Windows interfering)&lt;br /&gt;
&lt;br /&gt;
After this EVERYTHING worked as expected.&lt;br /&gt;
&lt;br /&gt;
I do not understand why Chery didn&#039;t flag this up much more. . . that doesn&#039;t make sense !?!&lt;br /&gt;
&lt;br /&gt;
The path where i found the firmware files was:&lt;br /&gt;
 C:\Program Files (x86)\Cherry\UtilitySoftware\UpdateData&lt;br /&gt;
&lt;br /&gt;
This is a directory listing of the content:&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
C:\Program Files (x86)\Cherry\UtilitySoftware\UpdateData&amp;gt;dir&lt;br /&gt;
 Volume in drive C has no label.&lt;br /&gt;
 Volume Serial Number is 1234-5678&lt;br /&gt;
&lt;br /&gt;
 Directory of C:\Program Files (x86)\Cherry\UtilitySoftware\UpdateData&lt;br /&gt;
&lt;br /&gt;
27-04-2022  08:26    &amp;lt;DIR&amp;gt;          .&lt;br /&gt;
27-04-2022  08:26    &amp;lt;DIR&amp;gt;          ..&lt;br /&gt;
01-12-2020  05:27         3.309.568 CCF_MX_1.0_TKL_BL_0104.exe&lt;br /&gt;
01-08-2020  03:48         3.309.056 CCF_MX_1.0_TKL_NBL_0105.exe&lt;br /&gt;
13-10-2020  13:14         3.309.568 CCF_MX_8.0_TKL_BL_0104.exe&lt;br /&gt;
18-09-2021  08:37            15.399 FWVerInfo.Ver&lt;br /&gt;
23-06-2021  03:27         2.947.584 G80_3000N_FL_RGB_0102.exe&lt;br /&gt;
18-09-2021  05:28         3.047.424 G80_3000N_FL_RGB_DE_OFFICE_0102.exe&lt;br /&gt;
18-09-2021  05:25         3.047.424 G80_3000N_FL_RGB_EU_OFFICE_0102.exe&lt;br /&gt;
15-09-2021  10:19         3.043.840 G80_3000N_TKL_RGB_DE_V0102.exe&lt;br /&gt;
15-09-2021  10:24         3.043.840 G80_3000N_TKL_RGB_V0102.exe&lt;br /&gt;
03-12-2020  08:28         3.312.640 G80_3000_TKL_NBL_0103.exe&lt;br /&gt;
09-06-2020  07:41         3.312.640 G80_3000_TKL_NBL_KOREAN_0100.exe&lt;br /&gt;
07-08-2021  05:32         3.332.096 G80_3000_TKL_RGB_0106.exe&lt;br /&gt;
27-05-2021  10:20         3.309.056 MC_2.1_0104.exe&lt;br /&gt;
03-06-2020  05:13         3.349.504 MC_3.1_0107.exe&lt;br /&gt;
20-05-2021  07:48         3.060.224 MC_3.1_3333_0103.exe&lt;br /&gt;
25-12-2019  08:18         3.349.504 MC_8.1_0107.exe&lt;br /&gt;
17-08-2021  11:45         4.219.904 MC_9620_0131.exe&lt;br /&gt;
03-12-2020  09:47         3.335.168 MV_BOARD_3.0_FL_RGB_0105.exe&lt;br /&gt;
03-12-2020  10:09         3.313.152 MX_1.0_FL_BL_0103.exe&lt;br /&gt;
17-08-2020  09:15         3.312.640 MX_1.0_FL_NBL_0102.exe&lt;br /&gt;
03-12-2020  10:00         3.335.168 MX_1.0_FL_RGB_0105.exe&lt;br /&gt;
25-04-2020  11:22         2.125.824 MX_BOARD_1.0_TKL_BL_0111.exe&lt;br /&gt;
09-11-2020  04:03         3.312.128 MX_BOARD_1.0_TKL_RGB_0104.exe&lt;br /&gt;
27-10-2020  11:10         3.312.128 MX_BOARD_1.0_TKL_RGB_DE_0102.exe&lt;br /&gt;
18-09-2021  08:25         3.047.936 MX_BOARD_10.0N_FL_RGB_0101.exe&lt;br /&gt;
18-09-2021  08:18         3.047.936 MX_BOARD_10.0N_FL_RGB_DE_0104.exe&lt;br /&gt;
01-07-2021  05:45         3.335.168 MX_BOARD_10.0_FL_RGB_0109.exe&lt;br /&gt;
02-12-2020  03:24         3.312.640 MX_BOARD_2.0S_FL_NBL_0104.exe&lt;br /&gt;
12-08-2021  08:10         3.335.680 MX_BOARD_2.0S_FL_RGB_0104.exe&lt;br /&gt;
20-08-2021  05:42         3.335.680 MX_BOARD_2.0S_FL_RGB_DE_0101.exe&lt;br /&gt;
17-12-2020  03:34         3.337.728 MX_BOARD_2.0S_FL_RGB_KR_0101.exe&lt;br /&gt;
18-06-2021  08:37         3.312.128 MX_BOARD_3.0S_FL_NBL_0120.exe&lt;br /&gt;
14-09-2021  04:14         3.335.168 MX_BOARD_3.0S_FL_RGB_0132.exe&lt;br /&gt;
19-05-2020  10:27         3.334.144 MX_BOARD_3.0S_FL_RGB_KOREAN_0104.exe&lt;br /&gt;
28-07-2021  11:32         4.709.888 MX_BOARD_3.0S_FL_RGB_THREEMODE_0113.exe&lt;br /&gt;
19-07-2021  12:02         4.693.504 MX_BOARD_3.0S_FL_RGB_THREEMODE_DONGLE_0109.exe&lt;br /&gt;
12-08-2021  11:04         3.312.640 MX_BOARD_8.0_TKL_RGB_0120.exe&lt;br /&gt;
              38 File(s)    119.815.751 bytes&lt;br /&gt;
               2 Dir(s)  171.767.029.760 bytes free&lt;br /&gt;
&lt;br /&gt;
C:\Program Files (x86)\Cherry\UtilitySoftware\UpdateData&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You are welcome :-)&lt;br /&gt;
[[Category:Gaming]][[Category:Hardware]][[Category:Windows]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2047</id>
		<title>7 Days To Die Server and Mod installation</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2047"/>
		<updated>2023-11-28T08:54:28Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Notes: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==== Guide: ====&lt;br /&gt;
# Install Debian. I used Debian 12.&lt;br /&gt;
# Update the OS.&lt;br /&gt;
# Follow the LGSM-install guide here --&amp;gt; https://linuxgsm.com/servers/sdtdserver/ &lt;br /&gt;
# You may need to install SteamCMD seperately using the guide here --&amp;gt; https://developer.valvesoftware.com/wiki/SteamCMD&lt;br /&gt;
# Install the mods like this https://shockbyte.com/billing/knowledgebase/322/How-to-Install-Mods-on-Your-7-Days-to-Die-Server.html or this https://7dac.net/linux-server-setup-guide/&lt;br /&gt;
# Start server and enjoy...&lt;br /&gt;
&lt;br /&gt;
==== Notes: ====&lt;br /&gt;
*You may need to disable EAC on the server... this will probably not be any problem, since you most likely will know and trust everyone you give access to your server anyway ;-)&lt;br /&gt;
* Launch your Client using the https://github.com/The7D2DModLauncher/7D2DModLauncherV5/releases&lt;br /&gt;
* Reset the game to start of Day 1 using this guide —-&amp;gt; https://shockbyte.com/billing/knowledgebase/320/How-to-Reset-Your-7-Days-to-Die-Server-World.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Gaming]][[Category:Debian]][[Category:SteamServer]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_War_of_the_Walkers_Client_Mod_guide&amp;diff=2046</id>
		<title>7 Days To Die War of the Walkers Client Mod guide</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_War_of_the_Walkers_Client_Mod_guide&amp;diff=2046"/>
		<updated>2023-11-27T18:25:46Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is focused on Alpha 21.1 - use the apropriate version in the future ;-)&lt;br /&gt;
&lt;br /&gt;
# Install 7D2D  like any other Steam game.&lt;br /&gt;
# Click the settings &amp;quot;cog-wheel&amp;quot; on the right after game is installed and select Properties.&lt;br /&gt;
# Select &amp;quot;Betas&amp;quot; on the left menu.&lt;br /&gt;
# Select &amp;quot;alpha21.1 - Alpha 21.1 Stable&amp;quot; in the dropdown box.&lt;br /&gt;
# Close the Properties windows on the X top right. Game should start downloading the 21.1 release-&lt;br /&gt;
# Download the latest release 7D2DModLauncherV5 https://github.com/The7D2DModLauncher/7D2DModLauncherV5/releases&lt;br /&gt;
# Install the 7D2DModLauncherV5 - accept the blue warning that author is unknown and bla bla blah ;-)&lt;br /&gt;
# Launch the Launcher...&lt;br /&gt;
# Click &amp;quot;Install New Mod&amp;quot; button&lt;br /&gt;
# Select &amp;quot;War of the Walkers v21.x.x.x Stable&amp;quot; in the list&lt;br /&gt;
# Click &amp;quot;Install&amp;quot; button.&lt;br /&gt;
# After the mod is installed you should see it in the list on the right - It is probably the only mod on the list...&lt;br /&gt;
# Make sure the correct game copy is selected (Alpha 21.1)&lt;br /&gt;
# Make sure to select the apropriate Mod version - ASK the server Admin about this! My server runs V21.1.2.4 released 11/18/2023 ;-)&lt;br /&gt;
# Click the &amp;quot;Download this version&amp;quot; button.&lt;br /&gt;
# Click the &amp;quot;Pre-Sync&amp;quot; button left of the green Play button.&lt;br /&gt;
# Click the &amp;quot;Play&amp;quot; button and watch the game launch...&lt;br /&gt;
# Enjoy your sparetime disappear like dew in the sun :-D&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
See https://7daystodiemods.com/war-of-the-walkers-mod/ for more info on the mod.&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_War_of_the_Walkers_Client_Mod_guide&amp;diff=2045</id>
		<title>7 Days To Die War of the Walkers Client Mod guide</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_War_of_the_Walkers_Client_Mod_guide&amp;diff=2045"/>
		<updated>2023-11-27T18:21:23Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is focused on Alpha 21.1 - use the apropriate version in the future ;-)&lt;br /&gt;
&lt;br /&gt;
# Install 7D2D  like any other Steam game.&lt;br /&gt;
# Click the settings &amp;quot;cog-wheel&amp;quot; on the right after game is installed and select Properties.&lt;br /&gt;
# Select &amp;quot;Betas&amp;quot; on the left menu.&lt;br /&gt;
# Select &amp;quot;alpha21.1 - Alpha 21.1 Stable&amp;quot; in the dropdown box.&lt;br /&gt;
# Close the Properties windows on the X top right. Game should start downloading the 21.1 release-&lt;br /&gt;
# Download the latest release 7D2DModLauncherV5 https://github.com/The7D2DModLauncher/7D2DModLauncherV5/releases&lt;br /&gt;
# Install the 7D2DModLauncherV5 - accept the blue warning that author is unknown and bla bla blah ;-)&lt;br /&gt;
# Launch the Launcher...&lt;br /&gt;
# Click &amp;quot;Install New Mod&amp;quot; button&lt;br /&gt;
# Select &amp;quot;War of the Walkers v21.x.x.x Stable&amp;quot; in the list&lt;br /&gt;
# Click &amp;quot;Install&amp;quot; button.&lt;br /&gt;
# After the mod is installed you should see it in the list on the right - It is probably the only mod on the list...&lt;br /&gt;
# Make sure the correct game copy is selected (Alpha 21.1)&lt;br /&gt;
# Make sure to select the apropriate Mod version - ASK the server Admin about this ;-)&lt;br /&gt;
# Click the &amp;quot;Download this version&amp;quot; button.&lt;br /&gt;
# Click the &amp;quot;Pre-Sync&amp;quot; button left of the green Play button.&lt;br /&gt;
# Click the &amp;quot;Play&amp;quot; button and watch the game launch...&lt;br /&gt;
# Enjoy your sparetime disappear like dew in the sun :-D&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
See https://7daystodiemods.com/war-of-the-walkers-mod/ for more info on the mod.&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_War_of_the_Walkers_Client_Mod_guide&amp;diff=2044</id>
		<title>7 Days To Die War of the Walkers Client Mod guide</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_War_of_the_Walkers_Client_Mod_guide&amp;diff=2044"/>
		<updated>2023-11-27T18:19:11Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;https://7daystodiemods.com/war-of-the-walkers-mod/&lt;br /&gt;
&lt;br /&gt;
# Install 7D2D  like any other Steam game.&lt;br /&gt;
# Click the settings &amp;quot;cog-wheel&amp;quot; on the right after game is installed and select Properties.&lt;br /&gt;
# Select &amp;quot;Betas&amp;quot; on the left menu.&lt;br /&gt;
# Select &amp;quot;alpha21.1 - Alpha 21.1 Stable&amp;quot; in the dropdown box.&lt;br /&gt;
# Close the Properties windows on the X top right. Game should start downloading the 21.1 release-&lt;br /&gt;
# Download the latest release 7D2DModLauncherV5 https://github.com/The7D2DModLauncher/7D2DModLauncherV5/releases&lt;br /&gt;
# Install the 7D2DModLauncherV5 - accept the blue warning that author is unknown and bla bla blah ;-)&lt;br /&gt;
# Launch the Launcher...&lt;br /&gt;
# Click &amp;quot;Install New Mod&amp;quot; button&lt;br /&gt;
# Select &amp;quot;War of the Walkers v21.x.x.x Stable&amp;quot; in the list&lt;br /&gt;
# Click &amp;quot;Install&amp;quot; button.&lt;br /&gt;
# After the mod is installed you should see it in the list on the right - It is probably the only mod on the list...&lt;br /&gt;
# Make sure the correct game copy is selected (Alpha 21.1)&lt;br /&gt;
# Make sure to select the apropriate Mod version - ASK the server Admin about this ;-)&lt;br /&gt;
# Click the &amp;quot;Download this version&amp;quot; button.&lt;br /&gt;
# Click the &amp;quot;Pre-Sync&amp;quot; button left of the green Play button.&lt;br /&gt;
# Click the &amp;quot;Play&amp;quot; button and watch the game launch...&lt;br /&gt;
# Enjoy your sparetime disappear like dew in the sun :-D&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_War_of_the_Walkers_Client_Mod_guide&amp;diff=2043</id>
		<title>7 Days To Die War of the Walkers Client Mod guide</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_War_of_the_Walkers_Client_Mod_guide&amp;diff=2043"/>
		<updated>2023-11-27T17:47:28Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;https://7daystodiemods.com/war-of-the-walkers-mod/&lt;br /&gt;
&lt;br /&gt;
# Install 7D2D  like any other Steam game.&lt;br /&gt;
# Click the settings &amp;quot;cog-wheel&amp;quot; on the right after game is installed and select Properties.&lt;br /&gt;
# Select &amp;quot;Betas&amp;quot; on the left menu.&lt;br /&gt;
# Select &amp;quot;alpha21.1 - Alpha 21.1 Stable&amp;quot; in the dropdown box.&lt;br /&gt;
# Close the Properties windows on the X top right. Game should start downloading the 21.1 release&lt;br /&gt;
# &lt;br /&gt;
# &lt;br /&gt;
# &lt;br /&gt;
# &lt;br /&gt;
# Download the latest release 7D2DModLauncherV5 https://github.com/The7D2DModLauncher/7D2DModLauncherV5/releases&lt;br /&gt;
# Install the 7D2DModLauncherV5 - accept the blue warning that author is unknown and bla bla blah ;-)&lt;br /&gt;
# Launch the Launcher...&lt;br /&gt;
# Click &amp;quot;Install New Mod&amp;quot; button&lt;br /&gt;
# Select &amp;quot;War of the Walkers v21.x.x.x Stable&amp;quot; in the list&lt;br /&gt;
# Click &amp;quot;Install&amp;quot; button.&lt;br /&gt;
# After the mod is installed you should see it in the list on the right - It is probably the only mod on the list...&lt;br /&gt;
# Make sure the correct game copy is selected&lt;br /&gt;
# &lt;br /&gt;
# &lt;br /&gt;
# &lt;br /&gt;
# &lt;br /&gt;
.&lt;br /&gt;
# Click the &amp;quot;Play&amp;quot; button when the game is ready - A dialog box should pop up.&lt;br /&gt;
# Select the radio-button &amp;quot;Show game launcher&amp;quot;&lt;br /&gt;
# Click &amp;quot;Play&amp;quot; - the original 7 Days to die game-launcher should pop up.&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_War_of_the_Walkers_Client_Mod_guide&amp;diff=2042</id>
		<title>7 Days To Die War of the Walkers Client Mod guide</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_War_of_the_Walkers_Client_Mod_guide&amp;diff=2042"/>
		<updated>2023-11-27T17:33:53Z</updated>

		<summary type="html">&lt;p&gt;Admin: Created page with &amp;quot;https://7daystodiemods.com/war-of-the-walkers-mod/  # Install 7D2D  like any other Steam game. # Download the latest release 7D2DModLauncherV5 https://github.com/The7D2DModLauncher/7D2DModLauncherV5/releases # Install the 7D2DModLauncherV5 - accept the blue warning that author is unknown and bla bla blah.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;https://7daystodiemods.com/war-of-the-walkers-mod/&lt;br /&gt;
&lt;br /&gt;
# Install 7D2D  like any other Steam game.&lt;br /&gt;
# Download the latest release 7D2DModLauncherV5 https://github.com/The7D2DModLauncher/7D2DModLauncherV5/releases&lt;br /&gt;
# Install the 7D2DModLauncherV5 - accept the blue warning that author is unknown and bla bla blah.&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2041</id>
		<title>7 Days To Die Server and Mod installation</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2041"/>
		<updated>2023-11-27T17:28:19Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Notes: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==== Guide: ====&lt;br /&gt;
# Install Debian. I used Debian 12.&lt;br /&gt;
# Update the OS.&lt;br /&gt;
# Follow the LGSM-install guide here --&amp;gt; https://linuxgsm.com/servers/sdtdserver/ &lt;br /&gt;
# You may need to install SteamCMD seperately using the guide here --&amp;gt; https://developer.valvesoftware.com/wiki/SteamCMD&lt;br /&gt;
# Install the mods like this https://shockbyte.com/billing/knowledgebase/322/How-to-Install-Mods-on-Your-7-Days-to-Die-Server.html or this https://7dac.net/linux-server-setup-guide/&lt;br /&gt;
# Start server and enjoy...&lt;br /&gt;
&lt;br /&gt;
==== Notes: ====&lt;br /&gt;
*You may need to disable EAC on the server... this will probably not be any problem, since you most likely will know and trust everyone you give access to your server anyway ;-)&lt;br /&gt;
* Launch your Client using the https://github.com/The7D2DModLauncher/7D2DModLauncherV5/releases&lt;br /&gt;
[[Category:Gaming]][[Category:Debian]][[Category:SteamServer]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2040</id>
		<title>7 Days To Die Server and Mod installation</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2040"/>
		<updated>2023-11-27T17:26:50Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==== Guide: ====&lt;br /&gt;
# Install Debian. I used Debian 12.&lt;br /&gt;
# Update the OS.&lt;br /&gt;
# Follow the LGSM-install guide here --&amp;gt; https://linuxgsm.com/servers/sdtdserver/ &lt;br /&gt;
# You may need to install SteamCMD seperately using the guide here --&amp;gt; https://developer.valvesoftware.com/wiki/SteamCMD&lt;br /&gt;
# Install the mods like this https://shockbyte.com/billing/knowledgebase/322/How-to-Install-Mods-on-Your-7-Days-to-Die-Server.html or this https://7dac.net/linux-server-setup-guide/&lt;br /&gt;
# Start server and enjoy...&lt;br /&gt;
&lt;br /&gt;
==== Notes: ====&lt;br /&gt;
*You may need to disable EAC on the server... this will probably not be any problem, since you most likely will know and trust everyone you give access to your server anyway ;-)&lt;br /&gt;
* Launch your Clinet using the https://github.com/The7D2DModLauncher/7D2DModLauncherV5/releases&lt;br /&gt;
[[Category:Gaming]][[Category:Debian]][[Category:SteamServer]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2039</id>
		<title>7 Days To Die Server and Mod installation</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2039"/>
		<updated>2023-11-27T17:26:09Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;# Install Debian. I used Debian 12.&lt;br /&gt;
# Update the OS.&lt;br /&gt;
# Follow the LGSM-install guide here --&amp;gt; https://linuxgsm.com/servers/sdtdserver/ &lt;br /&gt;
# You may need to install SteamCMD seperately using the guide here --&amp;gt; https://developer.valvesoftware.com/wiki/SteamCMD&lt;br /&gt;
# Install the mods like this https://shockbyte.com/billing/knowledgebase/322/How-to-Install-Mods-on-Your-7-Days-to-Die-Server.html or this https://7dac.net/linux-server-setup-guide/&lt;br /&gt;
# Start server and enjoy...&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
*You may need to disable EAC on the server... this will probably not be any problem, since you most likely will know and trust everyone you give access to your server anyway ;-)&lt;br /&gt;
* Launch your Clinet using the https://github.com/The7D2DModLauncher/7D2DModLauncherV5/releases&lt;br /&gt;
&lt;br /&gt;
[[Category:Gaming]][[Category:Debian]][[Category:SteamServer]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2038</id>
		<title>7 Days To Die Server and Mod installation</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2038"/>
		<updated>2023-11-27T17:24:27Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;# Install Debian. I used Debian 12.&lt;br /&gt;
# Update the OS.&lt;br /&gt;
# Follow the LGSM-install guide here --&amp;gt; https://linuxgsm.com/servers/sdtdserver/ &lt;br /&gt;
# You may need to install SteamCMD seperately using the guide here --&amp;gt; https://developer.valvesoftware.com/wiki/SteamCMD&lt;br /&gt;
# Install the mods like this https://shockbyte.com/billing/knowledgebase/322/How-to-Install-Mods-on-Your-7-Days-to-Die-Server.html or this https://7dac.net/linux-server-setup-guide/&lt;br /&gt;
# Start server and enjoy...&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
&lt;br /&gt;
You may need to disable EAC on the server... this will probably not be any problem, since you most likely will know and trust everyone you give access to your server anyway ;-)&lt;br /&gt;
&lt;br /&gt;
[[Category:Gaming]][[Category:Debian]][[Category:SteamServer]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2037</id>
		<title>7 Days To Die Server and Mod installation</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2037"/>
		<updated>2023-11-27T17:23:25Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;# Install Debian. I used Debian 12.&lt;br /&gt;
# Update the OS.&lt;br /&gt;
# Follow the LGSM-install guide here --&amp;gt; https://linuxgsm.com/servers/sdtdserver/ &lt;br /&gt;
# You may need to install SteamCMD seperately using the guide here --&amp;gt; https://developer.valvesoftware.com/wiki/SteamCMD&lt;br /&gt;
# Install the mods like this https://shockbyte.com/billing/knowledgebase/322/How-to-Install-Mods-on-Your-7-Days-to-Die-Server.html or this https://7dac.net/linux-server-setup-guide/&lt;br /&gt;
# Start server and enjoy...&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
&lt;br /&gt;
You may need to disable EAC on the server... this will probably not be any problem, since you most likely will know and trust everyone you give access to your server anyway ;-)&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2036</id>
		<title>7 Days To Die Server and Mod installation</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2036"/>
		<updated>2023-11-26T13:18:47Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;# Install Debian. I used Debian 12.&lt;br /&gt;
# Update the OS.&lt;br /&gt;
# Follow the LGSM install here --&amp;gt; https://linuxgsm.com/servers/sdtdserver/ &lt;br /&gt;
# You may need to install SteamCMD seperately using the guide here --&amp;gt; https://developer.valvesoftware.com/wiki/SteamCMD&lt;br /&gt;
# Install the mode like this https://shockbyte.com/billing/knowledgebase/322/How-to-Install-Mods-on-Your-7-Days-to-Die-Server.html&lt;br /&gt;
# Start server and enjoy&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2035</id>
		<title>7 Days To Die Server and Mod installation</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2035"/>
		<updated>2023-11-26T10:08:21Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;# Install Debian. I used Debian 12.&lt;br /&gt;
# Update the OS.&lt;br /&gt;
# Follow the LGSM install here --&amp;gt; https://linuxgsm.com/servers/sdtdserver/ &lt;br /&gt;
# You may need to install SteamCMD seperately using the guide here --&amp;gt; https://developer.valvesoftware.com/wiki/SteamCMD&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2034</id>
		<title>7 Days To Die Server and Mod installation</title>
		<link rel="alternate" type="text/html" href="https://munkjensen.net/wiki/index.php?title=7_Days_To_Die_Server_and_Mod_installation&amp;diff=2034"/>
		<updated>2023-11-26T10:04:01Z</updated>

		<summary type="html">&lt;p&gt;Admin: Created page with &amp;quot;# Install Debian. I used Debian 12. # Update the OS. # Follow the LGSM install here --&amp;gt; https://linuxgsm.com/servers/sdtdserver/&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;# Install Debian. I used Debian 12.&lt;br /&gt;
# Update the OS.&lt;br /&gt;
# Follow the LGSM install here --&amp;gt; https://linuxgsm.com/servers/sdtdserver/&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
</feed>