DS18B20 mit asynchroner Messung bringt Fehlermeldung

Hallo liebe Mitstreiter.
Ich würde gerne die Temperaturen mit einem DS18B20 über meine ESP 8266 D1 mini asynchrone messen. Leider funktioniert das nicht zuverlässig. Die meiste Zeit kommt Sensorfehler (Temperatur -127 °C) raus. Bei synchroner Messung also mit internem Delay, den ich gerne Vermeiden würde läuft der Sensor einwandfrei!
Hintergrund, es läuft noch ein kleiner Webserver und der OTA mit. Die unzuverlässig sind wenn delays den loop “hängen” lassen.
Hier mal mein setup und mein loop:

 void setup() {
  // Display Begrüßung
  u8g2.begin();
  u8g2.clearBuffer();
  u8g2.setFont(font1);
  u8g2.setContrast(contrast_hell);
  u8g2.setCursor(0, 12);
  u8g2.print(F("Hallo Nutzer"));
  u8g2.setCursor(0, 24);
  u8g2.print(F("Sensorname: "));
  u8g2.print(DEVICE_NAME);
  u8g2.setCursor(0, 36);
  u8g2.print(F("Version: "));
  u8g2.print(DEVICE_VERSION);
  u8g2.sendBuffer();

  // Web-Routen
  DGHOME_web().on("/",           handleRoot);
  DGHOME_web().on("/thermostat", HTTP_POST, handleThermostatSet);

  // DG_Home_WLAN starten
  DGWLAN::begin();

  init_multicall_config();

  // Taster
  pinMode(TA_1, INPUT_PULLUP);
  pinMode(TA_2, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(TA_1), taster_01, FALLING);
  attachInterrupt(digitalPinToInterrupt(TA_2), taster_02, FALLING);

  // DS18B20 initialisieren inkl. erster Messung
  sensors.begin();
  sensors.setResolution(12);
  sensors.setWaitForConversion(false);
  sensors.requestTemperatures();
  delay(800);  // erste Konvertierung abwarten
  klima_values[0] = sensors.getTempCByIndex(0);

  // RSSI initial + Balken
  float rssi = DGWLAN::rssi();
  klima_values[1] = rssi;
  int rssi_level =
    (rssi >= -55) ? 3 :
    (rssi >= -65) ? 2 :
    (rssi >= -75) ? 1 : 0;
  rssi_graph = rssi_level;

  last_display = millis();
  loop_count   = loop_send;   // direkt beim ersten Durchlauf MultiCall
}

// ---------------------------------------------------------------------------
// LOOP
// ---------------------------------------------------------------------------

void loop() {
  // Einmal Zeit holen
  unsigned long now = millis();

  // WLAN/OTA/HTTP-Service der Lib
  DGWLAN::loop();

  // Display dimmen
  if (last_display && (now - last_display >= (unsigned long)display_dunkel)) {
    last_display = 0;
    u8g2.setContrast(contrast_dunkel);
  }

  // Taster-Entprellung & Thermostat-Verstellung
  if (!last_debounce) {
    if (pm_change) {
      last_debounce = now;
      u8g2.setContrast(contrast_hell);
      last_display = now;

      // nur verstellen, wenn keine Fenster-Sperre
      if (!fenster) {
        m_thermostat = 1;

        if (pm_change == 1 && thermostat >= 15.5f) {
          if (thermostat > 27.0f) {
            thermostat = 27.0f;
          } else {
            thermostat -= 0.5f;
          }
        } else if (pm_change == 2 && thermostat <= 26.5f) {
          if (thermostat < 15.0f) {
            thermostat = 15.0f;
          } else {
            thermostat += 0.5f;
          }
        }
        refresh = true;
      }
    }
  } else if (now - last_debounce >= (unsigned long)debounce) {
    pm_change = 0;
    last_debounce = 0;
  }

  // 1 Sekunde vor dem Auslesen, Sensor anstoßen
  if (!data_read && (now - last_data_send >= (data_send - 1000UL))) {
    sensors.requestTemperatures();
    data_read = true;
  }

  // Zyklische Klima-Abfrage / MultiCall
  if (now - last_data_send >= data_send) {
    last_data_send = now;
    data_read      = false;

    // Temperatur lesen & runden (1 Nachkommastelle, ohne math.h)
    float t = sensors.getTempCByIndex(0);
    t = ((int)(t * 10.0f + 0.5f)) / 10.0f;

    if (klima_values[0] != t) {
      klima_values[0] = t;
      refresh = true;
    }

    // RSSI: für Server immer aktualisieren,
    // Display nur bei Stufenänderung anfassen
    float rssi = DGWLAN::rssi();

    if (klima_values[1] != rssi) {
      klima_values[1] = rssi;

      int rssi_level =
        (rssi >= -55) ? 3 :
        (rssi >= -65) ? 2 :
        (rssi >= -75) ? 1 : 0;

      if (rssi_graph != rssi_level) {
        rssi_graph = rssi_level;
        refresh = true;
      }
    }

    // Wenn Benutzer kurz zuvor verstellt hat,
    // dann beim nächsten Sendezyklus ans Server-Thermostat schicken
    if (m_thermostat) {
      if (m_thermostat == 1) {
        m_thermostat = 2;
      } else if (m_thermostat == 2) {
        m_thermostat = 0;
        thermostat_soll(thermostat);
      }
    }

    // Multicall
    if (loop_count >= loop_send) {
      loop_count = 0;
      send_multicall();
    } else {
      loop_count++;
    }
  }

  // Display nur bei Änderungen neu aufbauen
  if (refresh) {
    refresh_display();
    refresh = false;
  }
}

Vielleicht kennt ja jemand das Problem, und hat eine Lösung.

Vielen Dank für Eure Mithilfe.

Im englischen Teil des Forum müssen die Beiträge und Diskussionen in englischer Sprache verfasst werden. Deswegen wurde diese Diskussion in den deutschen Teil des Forums verschoben.

mfg ein Moderator.

welche Library verwendest du dafür ganz genau?

Egal auch wie bei 12bit Resolution musst die 750ms irgend wie überbrücken, oder auf 9bit runtergehen was aber bei den China nachbauten = billigen nicht geht, haben kein EEPROM.
Hier eine Diskussion über DS18B20
Auszug aus dem Datenblatt

Ich verwende diese Initialisierung:

// Klimasensor definieren
#include <OneWire.h>
#include <DallasTemperature.h>
// GPIO where the DS18B20 is connected to
const int oneWireBus = 2; 
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(oneWireBus);
// Pass our oneWire reference to Dallas Temperature sensor 
DallasTemperature sensors(&oneWire);


Wie groß ist data_send?
Warum setzt Du data_read nicht erst nach der Sekunde und verriegelst die Temperaturabfrage dagegen?

Ja, ich starte die Messung 1 sec vor der Auslesung, als mehr als die 750ms. Ich denke es liegt an der Chinaversion. Wobei eines Interessant ist: Zwischendurch zeigt das Display den richtigen wert, bei der nächsten Messung nicht mehr….
Zyklus (data_send) sind 10 Sekunden.

// 1 Sekunde vor dem Auslesen, Sensor anstoßen
  if (!data_read && (now - last_data_send >= (data_send - 1000UL))) {
    sensors.requestTemperatures();
    data_read = true;
  }

  // Zyklische Klima-Abfrage / MultiCall
  if (now - last_data_send >= data_send) {
    last_data_send = now;
    data_read      = false;

    // Temperatur lesen & runden (1 Nachkommastelle, ohne math.h)
    float t = sensors.getTempCByIndex(0);
    t = ((int)(t * 10.0f + 0.5f)) / 10.0f;
...

diese gibt es um die 10 mall :wink:

gibt die Lib zurück, wenn der Sensor nicht gelesen werden kann.
Du hast entweder ein Kontakt- oder ein Versorgungsproblem.

(Es gibt noch den anderen Fall: 80°C, der das lesen des Sensors bestätigt, aber die Konversation noch nicht abgeschlossen ist)

Benutzt Du die Module mit den Sensoren drauf?

Diese Version:
DallasTemperature von Miles Burton Version 4.0.5

Verbindung, bzw. Kontaktproblem habe ich auch schon überlegt, aber das widerspricht doch der Zuverlässigkeit bei der synchronen Messung. Hier gibt es seit Monaten keine Aussetzer und das Ding misst alle 10 sekunden.

Dir fehlt in jedem Fall die Versorgung, sodass das Konversationsergebnis nicht ausgegeben werden kann.

Es spricht etwas gegen Kontaktprpbleme, aber nichts gegen fehlende Ladung.
Letzter Satz:

Ich weiss noch immer nicht, ob Du die Module benutzt oder den rohen Sensor.

Ich benutze eine rohen Sensor.

So verkabelt:

Da Du nur 3,3V zur Verfügung hast, würde ich anstelle mit den sonst anliegenden 4,7Kohm mit einem 2Kohm bis maximal 3Kohm R zwischen DATA und +3,3V arbeiten.

Und dann zeig mal den kompletten Code, damit ich nicht alle erraten muss.

Also die schlimmsten China nachbauten = Keine Möglichkeit die Resolution ändern, mansche funktionieren nicht bei 3,3V, kein EEPROM usw.

#include <Arduino.h>

#include <U8g2lib.h>

#include <Wire.h>

#include <OneWire.h>

#include <DallasTemperature.h>

#include "DG_Home_WLAN.h"




// ---------------------------------------------------------------------------

// Raumvariable

// ---------------------------------------------------------------------------

// Alle Fensterkontakte in diesem Raum

const int fenster_ids[]         = { 42 };

// Alle Heizkreise in diesem Raum

const int heizung_ids[]         = { 147 };




// ---------------------------------------------------------------------------

// Geräte Hardware

// ---------------------------------------------------------------------------

// Taster / Bedienung

const byte TA_1 = 0;   // runter

const byte TA_2 = 14;  // rauf




// Entprellzeit

const int  debounce             = 100;

unsigned long last_debounce     = 0;




// ---------------------------------------------------------------------------

// Display / UI

// ---------------------------------------------------------------------------

#define font1 u8g2_font_helvR08_te

#define font2 u8g2_font_helvB12_te

#define font3 u8g2_font_helvB14_te




const byte contrast_hell        = 255;

const byte contrast_dunkel      = 0;

const int  display_dunkel       = 5000;   // 5 s bis Dunkelmodus




volatile unsigned long last_display = 0;

byte  rssi_graph                 = 0;     // 0..3: RSSI-Balkenstufe

bool  refresh                    = false;




U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(

  U8G2_R0,

  /* reset = */ U8X8_PIN_NONE

);




// ---------------------------------------------------------------------------

// DS18B20

// ---------------------------------------------------------------------------

const byte ONE_WIRE_BUS         = 2;

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);




// ---------------------------------------------------------------------------

// Klima-Variablen

// ---------------------------------------------------------------------------

// klima_values[0] = Temperatur, klima_values[1] = RSSI

float klima_values[]            = { 0.0f, 0.0f }; 




// Defaulttemperatur für Thermostat

float thermostat                = 22.0f;    // °C




unsigned long last_data_send    = 0;

const unsigned long data_send   = 10000UL;  // alle 10 s Klima holen / senden

bool data_read                  = false;    // Sensor im Lesemodus angestoßen




const byte loop_send            = 6;        // nach 6 * data_send MultiCall

volatile byte pm_change         = 0;        // 0=keine, 1=runter, 2=rauf

byte m_thermostat               = 0;




bool  hk                        = false;    // Heizkörper an/aus

volatile bool fenster           = false;

byte  loop_count                = 0;




// ---------------------------------------------------------------------------

// Globale Konstanten / Multicall-Layout

// ---------------------------------------------------------------------------




constexpr size_t FENSTER_COUNT      = sizeof(fenster_ids) / sizeof(fenster_ids[0]);

constexpr size_t HEIZUNG_COUNT      = sizeof(heizung_ids) / sizeof(heizung_ids[0]);

constexpr size_t KLIMA_COUNT        = sizeof(klima_values) / sizeof(*klima_values);




// Index-Bereiche im Multicall-Array

constexpr size_t IDX_OFFSET_FENSTER = 0;

constexpr size_t IDX_OFFSET_HEIZUNG = IDX_OFFSET_FENSTER + FENSTER_COUNT;

constexpr size_t IDX_HZG_GET        = IDX_OFFSET_HEIZUNG + HEIZUNG_COUNT;

constexpr size_t IDX_KLIMA_SET      = IDX_HZG_GET + 1;

constexpr size_t MC_COUNT           = IDX_KLIMA_SET + 1;




String  mc_methods[MC_COUNT];

uint8_t mc_param_types[MC_COUNT];

String  mc_params[MC_COUNT][2];

float   mc_results[MC_COUNT];




void handleRoot();

void handleThermostatSet();

void refresh_display();




// ---------------------------------------------------------------------------

// Multicall-Konfiguration

// ---------------------------------------------------------------------------




void init_multicall_config() {

size_t idx = 0;




  // 1) Alle Fenster → sensor_read(fenster_id, 0)

for (size_t i = 0; i < FENSTER_COUNT; ++i, ++idx) {

mc_methods[idx]      = F("sensor_read");

mc_param_types[idx]  = 1;                  // int

mc_params[idx][0]    = String(fenster_ids[i]);

mc_params[idx][1]    = F("0");

}




  // 2) Alle Heizungen → aktor_read(heizung_id)

for (size_t i = 0; i < HEIZUNG_COUNT; ++i, ++idx) {

mc_methods[idx]      = F("aktor_read");

mc_param_types[idx]  = 1;                  // int

mc_params[idx][0]    = String(heizung_ids[i]);

mc_params[idx][1]    = String();           // leer

}




  // 3) hzg_get("<DEVICE_NAME>-00")

mc_methods[idx]        = F("hzg_get");

mc_param_types[idx]    = 3;                  // string

mc_params[idx][0]      = String(DEVICE_NAME) + "-00";

mc_params[idx][1]      = String();

  ++idx;




  // 4) klima_daten_set(DEVICE_ID, struct)

mc_methods[idx]        = F("klima_daten_set");

mc_param_types[idx]    = 1;                  // int

mc_params[idx][0]      = String(DEVICE_ID);

mc_params[idx][1]      = String();

  ++idx;




if (idx != MC_COUNT) {

    // optional: Debug-Ausgabe

}

}




// ---------------------------------------------------------------------------

// Helfer: Einzel-XML-RPC (über DG_Home_WLAN)

// ---------------------------------------------------------------------------




// num_type: 0=boolean, 1=int, 2=double(*10 → int)

int send_message(const String& methodBody, uint8_t num_type) {

return DGWLAN::send_message(methodBody, num_type);

}




// Aktor/Sensor lesen/setzen

int aktor_sensor(int id_as, int set, bool aktor) {

  String msg = F("<methodName>");

if (set == -1) {

    msg += aktor ? F("aktor_read") : F("sensor_read");

} else {

    msg += aktor ? F("aktor_set") : F("sensor_set");

}

  msg += F("</methodName>\n<params>\n");




  // Param 1: ID

  msg += F("<param><value><int>");

  msg += id_as;

  msg += F("</int></value></param>\n");




  // Param 2: optional set-Wert (double)

if (set != -1) {

    msg += F("<param><value><double>");

    msg += set;

    msg += F("</double></value></param>\n");

}

  msg += F("</params>\n");




if (set == -1) {

    // lesen: Double *10 als int

int v = send_message(msg, 2);

return v;

} else {

    // schreiben: boolean

int ok = send_message(msg, 0);

return ok;

}

}




// Solltemperatur an Server schicken

void thermostat_soll(float temp) {

  String name = String(DEVICE_NAME) + "-00";




  String msg = F("<methodName>hzg_change</methodName>\n<params>\n");




  // Param 1: Name (string)

  msg += F("<param><value><string>");

  msg += name;

  msg += F("</string></value></param>\n");




  // Param 2: Temperatur (double)

  msg += F("<param><value><double>");

  msg += String(temp, 1);

  msg += F("</double></value></param>\n</params>\n");




int ok = send_message(msg, 0);

(void)ok;

}




// ---------------------------------------------------------------------------

// WEBSEITEN

// ---------------------------------------------------------------------------




void handleRoot() {

  // Verbindung & Qualität

const bool connected = DGWLAN::isConnected();

const String ip      = DGWLAN::ip();

const uint8_t err    = DGWLAN::getLastError();

const char* err_txt  = DGWLAN::getLastErrorText();




  String quality;

if (connected) {

const char* bars =

(rssi_graph > 2) ? "▮▮▮▮" :

(rssi_graph > 1) ? "▮▮▮▯" :

(rssi_graph > 0) ? "▮▮▯▯" : "▮▯▯▯";

    quality = String(bars) + "  " + String((int)klima_values[1]) + " dBm";

} else {

    quality = F("nicht verbunden");

}




  // Fenstertext

const char* fenster_txt = fenster ? "mind. eins offen" : "alle zu";




  // Heizungstext

const char* hk_txt = hk ? "An" : "Aus";




  // Ist-Temperatur (mit Sensorfehler-Abfang)

  String temp_ist_str;

if (klima_values[0] < -100.0f) {   // z. B. -127 bei Fehler

    temp_ist_str = F("Sensorfehler");

} else {

    temp_ist_str = String(klima_values[0], 1) + " °C";

}




  // Soll-Temperatur

  String temp_soll_str = String(thermostat, 1) + " °C";




  String s = DGWLAN::DGHOME_html_header("Raumklima");




  s += F("<table style='width:100%;max-width:480px;margin:0 auto;border-collapse:collapse;'>");




  // --- Titelzeile ---

  s += F("<tr><th colspan='2'>");

  s += DEVICE_NAME;

  s += F("</th></tr>");




  // --- System / Verbindung ---

  s += F("<tr><th colspan='2'>System & Verbindung</th></tr>");




  s += F("<tr><td>Hostname</td><td align='right'><code>");

  s += DGWLAN::hostname();

  s += F("</code></td></tr>");




  s += F("<tr><td>Version</td><td align='right'>");

  s += DEVICE_VERSION;

  s += F("</td></tr>");




  s += F("<tr><td>Datum</td><td align='right'>");

  s += DEVICE_DATE;

  s += F("</td></tr>");




  s += F("<tr><td>CPU</td><td align='right'>");

  s += DEVICE_CPU;

  s += F("</td></tr>");




  s += F("<tr><td>Autor</td><td align='right'>");

  s += DEVICE_AUTHOR;

  s += F("</td></tr>");




  s += F("<tr><td>IP-Adresse</td><td align='right'><code>");

  s += ip;

  s += F("</code></td></tr>");




  s += F("<tr><td>Verbindung</td><td align='right'>");

  s += connected ? F("<b>verbunden</b>") : F("<b>nicht verbunden</b>");

  s += F("</td></tr>");




  s += F("<tr><td>Netzqualität</td><td align='right'>");

  s += quality;

  s += F("</td></tr>");




  s += F("<tr><td>Fehler</td><td align='right'><code>");

  s += String(err);

  s += F(" – ");

  s += err_txt;

  s += F("</code></td></tr>");




  // --- Klima & Heizung ---

  s += F("<tr><th colspan='2'>Raumklima</th></tr>");




  s += F("<tr><td>Temperatur (Ist)</td><td align='right'>");

  s += temp_ist_str;

  s += F("</td></tr>");




  s += F("<tr><td>Temperatur (Soll)</td><td align='right'>");

  s += temp_soll_str;

  s += F("</td></tr>");




  s += F("<tr><td>Fenster</td><td align='right'>");

  s += fenster_txt;

  s += F("</td></tr>");




  s += F("<tr><td>Heizung</td><td align='right'>");

  s += hk_txt;

  s += F("</td></tr>");




  // --- Thermostat-Einstellung ---

  s += F("<tr><th colspan='2'>Thermostat einstellen</th></tr>");

  s += F("<tr><td colspan='2'>");

  s += F("<form method='POST' action='/thermostat' "

"style='display:block;width:100%;max-width:480px;margin:0 auto;'>");




  s += F("<label for='soll'>Soll-Temperatur [°C]</label>");

  s += F("<input type='number' id='soll' name='soll' "

"step='0.5' min='15.0' max='27.0' "

"style='width:100%;box-sizing:border-box;' value='");

  s += String(thermostat, 1);

  s += F("'>");




  s += F("<button type='submit' class='B00 Blue' "

"style='width:100%;margin-top:6px;'>Speichern</button>");




  s += F("</form>");

  s += F("</td></tr>");




  // --- Buttons unten ---

  s += F("<tr><td colspan='2' style='padding-top:10px;'>");




  s += F("<a href='/' style='display:block;margin-top:4px;'>"

"<button type='button' class='B00 Yellow' style='width:100%'>Aktualisieren</button>"

"</a>");




  s += F("<a href='/wlan' style='display:block;margin-top:4px;'>"

"<button type='button' class='B00 Blue' style='width:100%'>WLAN-Konfiguration</button>"

"</a>");




  s += F("<a href='/diag' style='display:block;margin-top:4px;'>"

"<button type='button' class='B00 Grey2' style='width:100%'>Diagnose</button>"

"</a>");




  s += F("</td></tr>");




  // Footer

  s += F("<tr><td colspan='2' align='center' class='muted' style='padding-top:10px'>© by ");

  s += DEVICE_AUTHOR;

  s += F(" ");

  s += DEVICE_DATE;

  s += F("</td></tr>");




  s += F("</table>");




  s += DGWLAN::DGHOME_html_footer();

DGHOME_web().send(200, "text/html; charset=utf-8", s);

}




void handleThermostatSet() {

if (DGHOME_web().hasArg("soll")) {

    String v = DGHOME_web().arg("soll");

v.trim();

if (v.length()) {

float t = v.toFloat();




// Clamp auf 15–27 °C

if (t < 15.0f) t = 15.0f;

if (t > 27.0f) t = 27.0f;




      // auf 0.5 °C runden OHNE math.h

      t = ((int)(t * 2.0f + 0.5f)) / 2.0f;




      thermostat = t;




      // Direkt an den Server schicken

thermostat_soll(thermostat);




      // Display wieder hell machen

u8g2.setContrast(contrast_hell);

      last_display = millis();




      // Anzeige im nächsten loop-Durchgang aktualisieren

      refresh = true;

}

}

  // Danach wieder zurück zur Hauptseite

handleRoot();

}





// ---------------------------------------------------------------------------

// MultiCall über DG_Home_WLAN

// ---------------------------------------------------------------------------




bool send_multicall() {

bool ok = DGWLAN::send_multicall(

    mc_methods,

    mc_param_types,

    mc_params,

    mc_results,

    MC_COUNT,

"klima_daten_set",

    DEVICE_NAME,

    klima_values,

    KLIMA_COUNT

);




if (!ok) {

return false;

}




  // 1) Fenster

bool irgendeinFensterOffen = false;

for (size_t i = 0; i < FENSTER_COUNT; ++i) {

if (mc_results[IDX_OFFSET_FENSTER + i] != 0.0f) {

      irgendeinFensterOffen = true;

break;

}

}

if (fenster != irgendeinFensterOffen) {

    fenster = irgendeinFensterOffen;

    refresh = true;

}




  // 2) Heizung (Hauptstatus)

bool h = false;

if (HEIZUNG_COUNT > 0) {

    h = (mc_results[IDX_OFFSET_HEIZUNG] != 0.0f);

}

if (hk != h) {

    hk = h;

    refresh = true;

}




  // 3) Thermostat-Sollwert vom Server (nur übernehmen,

  //    wenn lokal nichts in Bearbeitung)

if (m_thermostat == 0) {

float t = mc_results[IDX_HZG_GET];

if (thermostat != t) {

      thermostat = t;

      refresh = true;

}

}




  // 4) klima_daten_set Ergebnis: IDX_KLIMA_SET (optional auswertbar)




return true;

}




// ---------------------------------------------------------------------------

// Anzeige

// ---------------------------------------------------------------------------




void refresh_display() {

u8g2.clearBuffer();




u8g2.setFont(font1);

u8g2.drawUTF8(0, 8, "Raumklima");




  // Ist-Temperatur

u8g2.setFont(font2);

u8g2.setCursor(0, 26);




if (klima_values[0] < -100.0f) {  // Sensorfehler, z.B. -127

u8g2.drawUTF8(0, 26, "Sensorfehler");

} else {

u8g2.print(klima_values[0], 1);

u8g2.drawUTF8(32, 26, "°C");

}




u8g2.setFont(font2);

u8g2.setCursor(0, 64);




uint8_t err = DGWLAN::getLastError();




if (err == 0) {

    // kein Kommunikationsfehler

if (!fenster) {

      // Solltemperatur

u8g2.print(thermostat, 1);

u8g2.drawUTF8(32, 64, "°C");




      // Status Heizung

u8g2.setFont(font1);

u8g2.setCursor(0, 46);

u8g2.print(F("Thermostat"));

u8g2.setCursor(64, 46);

u8g2.print(F("Heizung"));




u8g2.setFont(font1);

u8g2.setCursor(64, 64);

if (hk) {

u8g2.print(F("An"));

} else {

u8g2.print(F("Aus"));

}

} else {

      // Fenster offen

u8g2.print(F("Fenster offen!"));

}

} else {

    // Fehlermeldung aus DG_Home_WLAN

u8g2.print(DGWLAN::getLastErrorText());

}




  // WLAN-Signalbalken (nur wenn nicht "Kein WLAN")

if (err != 1) {

    // immer ein Balken

u8g2.drawBox(108, 3, 2, 2);

if (rssi_graph > 0) {

u8g2.drawBox(112, 2, 4, 3);

}

if (rssi_graph > 1) {

u8g2.drawBox(118, 1, 4, 4);

}

if (rssi_graph > 2) {

u8g2.drawBox(124, 0, 4, 5);

}

}




u8g2.sendBuffer();

}




// ---------------------------------------------------------------------------

// Interrupts (2 Taster)

// ---------------------------------------------------------------------------




IRAM_ATTR void taster_01() {

if (!pm_change) {

    pm_change = 1;   // runter

}

}




IRAM_ATTR void taster_02() {

if (!pm_change) {

    pm_change = 2;   // rauf

}

}




// ---------------------------------------------------------------------------

// SETUP

// ---------------------------------------------------------------------------




void setup() {

  // Display Begrüßung

u8g2.begin();

u8g2.clearBuffer();

u8g2.setFont(font1);

u8g2.setContrast(contrast_hell);

u8g2.setCursor(0, 12);

u8g2.print(F("Hallo Birgit & Dirk"));

u8g2.setCursor(0, 24);

u8g2.print(F("Sensorname: "));

u8g2.print(DEVICE_NAME);

u8g2.setCursor(0, 36);

u8g2.print(F("Version: "));

u8g2.print(DEVICE_VERSION);

u8g2.sendBuffer();




  // Web-Routen

DGHOME_web().on("/",           handleRoot);

DGHOME_web().on("/thermostat", HTTP_POST, handleThermostatSet);




  // DG_Home_WLAN starten

  DGWLAN::begin();




init_multicall_config();




  // Taster

pinMode(TA_1, INPUT_PULLUP);

pinMode(TA_2, INPUT_PULLUP);




attachInterrupt(digitalPinToInterrupt(TA_1), taster_01, FALLING);

attachInterrupt(digitalPinToInterrupt(TA_2), taster_02, FALLING);




  // DS18B20 initialisieren inkl. erster Messung

sensors.begin();

sensors.setResolution(12);

sensors.setWaitForConversion(false);

sensors.requestTemperatures();

delay(800);  // erste Konvertierung abwarten

klima_values[0] = sensors.getTempCByIndex(0);




  // RSSI initial + Balken

float rssi = DGWLAN::rssi();

klima_values[1] = rssi;

int rssi_level =

(rssi >= -55) ? 3 :

(rssi >= -65) ? 2 :

(rssi >= -75) ? 1 : 0;

  rssi_graph = rssi_level;




  last_display = millis();

  loop_count   = loop_send;   // direkt beim ersten Durchlauf MultiCall

}




// ---------------------------------------------------------------------------

// LOOP

// ---------------------------------------------------------------------------




void loop() {

  // Einmal Zeit holen

unsigned long now = millis();




  // WLAN/OTA/HTTP-Service der Lib

  DGWLAN::loop();




  // Display dimmen

if (last_display && (now - last_display >= (unsigned long)display_dunkel)) {

    last_display = 0;

u8g2.setContrast(contrast_dunkel);

}




  // Taster-Entprellung & Thermostat-Verstellung

if (!last_debounce) {

if (pm_change) {

      last_debounce = now;

u8g2.setContrast(contrast_hell);

      last_display = now;




      // nur verstellen, wenn keine Fenster-Sperre

if (!fenster) {

        m_thermostat = 1;




if (pm_change == 1 && thermostat >= 15.5f) {

if (thermostat > 27.0f) {

            thermostat = 27.0f;

} else {

            thermostat -= 0.5f;

}

} else if (pm_change == 2 && thermostat <= 26.5f) {

if (thermostat < 15.0f) {

            thermostat = 15.0f;

} else {

            thermostat += 0.5f;

}

}

        refresh = true;

}

}

} else if (now - last_debounce >= (unsigned long)debounce) {

    pm_change = 0;

    last_debounce = 0;

}




  // 1 Sekunde vor dem Auslesen, Sensor anstoßen

if (!data_read && (now - last_data_send >= (data_send - 1000UL))) {

sensors.requestTemperatures();

    data_read = true;

}




  // Zyklische Klima-Abfrage / MultiCall

if (now - last_data_send >= data_send) {

    last_data_send = now;

    data_read      = false;




    // Temperatur lesen & runden (1 Nachkommastelle, ohne math.h)

float t = sensors.getTempCByIndex(0);

    t = ((int)(t * 10.0f + 0.5f)) / 10.0f;




if (klima_values[0] != t) {

klima_values[0] = t;

      refresh = true;

}




    // RSSI: für Server immer aktualisieren,

    // Display nur bei Stufenänderung anfassen

float rssi = DGWLAN::rssi();




if (klima_values[1] != rssi) {

klima_values[1] = rssi;




int rssi_level =

(rssi >= -55) ? 3 :

(rssi >= -65) ? 2 :

(rssi >= -75) ? 1 : 0;




if (rssi_graph != rssi_level) {

        rssi_graph = rssi_level;

        refresh = true;

}

}




    // Wenn Benutzer kurz zuvor verstellt hat,

    // dann beim nächsten Sendezyklus ans Server-Thermostat schicken

if (m_thermostat) {

if (m_thermostat == 1) {

        m_thermostat = 2;

} else if (m_thermostat == 2) {

        m_thermostat = 0;

thermostat_soll(thermostat);

}

}




    // Multicall

if (loop_count >= loop_send) {

      loop_count = 0;

send_multicall();

} else {

      loop_count++;

}

}




  // Display nur bei Änderungen neu aufbauen

if (refresh) {

refresh_display();

    refresh = false;

}

}

Mach mall die alle Pausen raus und drück in der IDE Strg+t, du kommst auf 1500 Zeilen effektiv werden das wo möglich 100.
Das ist nicht lesbar.
Das ist der Hammer, es gehört in eine Zeile, ein ungeübte wird hier kein Display vermuten

U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(

  U8G2_R0,

  /* reset = */ U8X8_PIN_NONE

);

#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "DG_Home_WLAN.h"

// ---------------------------------------------------------------------------
// Raumvariable
// ---------------------------------------------------------------------------
// Alle Fensterkontakte in diesem Raum
const int fenster_ids[] = { 42 };
// Alle Heizkreise in diesem Raum
const int heizung_ids[] = { 147 };

// ---------------------------------------------------------------------------
// Geräte Hardware
// ---------------------------------------------------------------------------
// Taster / Bedienung
const byte TA_1 = 0;   // runter
const byte TA_2 = 14;  // rauf

// Entprellzeit
const int debounce = 100;
unsigned long last_debounce = 0;

// ---------------------------------------------------------------------------
// Display / UI
// ---------------------------------------------------------------------------
#define font1 u8g2_font_helvR08_te
#define font2 u8g2_font_helvB12_te
#define font3 u8g2_font_helvB14_te

const byte contrast_hell = 255;
const byte contrast_dunkel = 0;
const int display_dunkel = 5000;  // 5 s bis Dunkelmodus

volatile unsigned long last_display = 0;
byte rssi_graph = 0;  // 0..3: RSSI-Balkenstufe
bool refresh = false;

U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(
  U8G2_R0,
  /* reset = */ U8X8_PIN_NONE);

// ---------------------------------------------------------------------------
// DS18B20
// ---------------------------------------------------------------------------
const byte ONE_WIRE_BUS = 2;
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

// ---------------------------------------------------------------------------
// Klima-Variablen
// ---------------------------------------------------------------------------
// klima_values[0] = Temperatur, klima_values[1] = RSSI
float klima_values[] = { 0.0f, 0.0f };

// Defaulttemperatur für Thermostat
float thermostat = 22.0f;  // °C

unsigned long last_data_send = 0;
const unsigned long data_send = 10000UL;  // alle 10 s Klima holen / senden
bool data_read = false;                   // Sensor im Lesemodus angestoßen

const byte loop_send = 6;     // nach 6 * data_send MultiCall
volatile byte pm_change = 0;  // 0=keine, 1=runter, 2=rauf
byte m_thermostat = 0;

bool hk = false;  // Heizkörper an/aus
volatile bool fenster = false;
byte loop_count = 0;

// ---------------------------------------------------------------------------
// Globale Konstanten / Multicall-Layout
// ---------------------------------------------------------------------------

constexpr size_t FENSTER_COUNT = sizeof(fenster_ids) / sizeof(fenster_ids[0]);
constexpr size_t HEIZUNG_COUNT = sizeof(heizung_ids) / sizeof(heizung_ids[0]);
constexpr size_t KLIMA_COUNT = sizeof(klima_values) / sizeof(*klima_values);

// Index-Bereiche im Multicall-Array
constexpr size_t IDX_OFFSET_FENSTER = 0;
constexpr size_t IDX_OFFSET_HEIZUNG = IDX_OFFSET_FENSTER + FENSTER_COUNT;
constexpr size_t IDX_HZG_GET = IDX_OFFSET_HEIZUNG + HEIZUNG_COUNT;
constexpr size_t IDX_KLIMA_SET = IDX_HZG_GET + 1;
constexpr size_t MC_COUNT = IDX_KLIMA_SET + 1;

String mc_methods[MC_COUNT];
uint8_t mc_param_types[MC_COUNT];
String mc_params[MC_COUNT][2];
float mc_results[MC_COUNT];

void handleRoot();
void handleThermostatSet();
void refresh_display();

// ---------------------------------------------------------------------------
// Multicall-Konfiguration
// ---------------------------------------------------------------------------

void init_multicall_config() {
  size_t idx = 0;

  // 1) Alle Fenster → sensor_read(fenster_id, 0)
  for (size_t i = 0; i < FENSTER_COUNT; ++i, ++idx) {
    mc_methods[idx] = F("sensor_read");
    mc_param_types[idx] = 1;  // int
    mc_params[idx][0] = String(fenster_ids[i]);
    mc_params[idx][1] = F("0");
  }

  // 2) Alle Heizungen → aktor_read(heizung_id)
  for (size_t i = 0; i < HEIZUNG_COUNT; ++i, ++idx) {
    mc_methods[idx] = F("aktor_read");
    mc_param_types[idx] = 1;  // int
    mc_params[idx][0] = String(heizung_ids[i]);
    mc_params[idx][1] = String();  // leer
  }

  // 3) hzg_get("<DEVICE_NAME>-00")
  mc_methods[idx] = F("hzg_get");
  mc_param_types[idx] = 3;  // string
  mc_params[idx][0] = String(DEVICE_NAME) + "-00";
  mc_params[idx][1] = String();
  ++idx;

  // 4) klima_daten_set(DEVICE_ID, struct)
  mc_methods[idx] = F("klima_daten_set");
  mc_param_types[idx] = 1;  // int
  mc_params[idx][0] = String(DEVICE_ID);
  mc_params[idx][1] = String();
  ++idx;

  if (idx != MC_COUNT) {
    // optional: Debug-Ausgabe
  }
}

// ---------------------------------------------------------------------------
// Helfer: Einzel-XML-RPC (über DG_Home_WLAN)
// ---------------------------------------------------------------------------

// num_type: 0=boolean, 1=int, 2=double(*10 → int)
int send_message(const String& methodBody, uint8_t num_type) {
  return DGWLAN::send_message(methodBody, num_type);
}

// Aktor/Sensor lesen/setzen
int aktor_sensor(int id_as, int set, bool aktor) {
  String msg = F("<methodName>");
  if (set == -1) {
    msg += aktor ? F("aktor_read") : F("sensor_read");
  } else {
    msg += aktor ? F("aktor_set") : F("sensor_set");
  }
  msg += F("</methodName>\n<params>\n");

  // Param 1: ID
  msg += F("<param><value><int>");
  msg += id_as;
  msg += F("</int></value></param>\n");

  // Param 2: optional set-Wert (double)
  if (set != -1) {
    msg += F("<param><value><double>");
    msg += set;
    msg += F("</double></value></param>\n");
  }
  msg += F("</params>\n");

  if (set == -1) {
    // lesen: Double *10 als int
    int v = send_message(msg, 2);
    return v;
  } else {
    // schreiben: boolean
    int ok = send_message(msg, 0);
    return ok;
  }
}

// Solltemperatur an Server schicken
void thermostat_soll(float temp) {
  String name = String(DEVICE_NAME) + "-00";

  String msg = F("<methodName>hzg_change</methodName>\n<params>\n");

  // Param 1: Name (string)
  msg += F("<param><value><string>");
  msg += name;
  msg += F("</string></value></param>\n");

  // Param 2: Temperatur (double)
  msg += F("<param><value><double>");
  msg += String(temp, 1);
  msg += F("</double></value></param>\n</params>\n");

  int ok = send_message(msg, 0);
  (void)ok;
}

// ---------------------------------------------------------------------------
// WEBSEITEN
// ---------------------------------------------------------------------------

void handleRoot() {
  // Verbindung & Qualität
  const bool connected = DGWLAN::isConnected();
  const String ip = DGWLAN::ip();
  const uint8_t err = DGWLAN::getLastError();
  const char* err_txt = DGWLAN::getLastErrorText();

  String quality;
  if (connected) {
    const char* bars =
      (rssi_graph > 2) ? "▮▮▮▮" : (rssi_graph > 1) ? "▮▮▮▯"
                                : (rssi_graph > 0) ? "▮▮▯▯"
                                                   : "▮▯▯▯";
    quality = String(bars) + "  " + String((int)klima_values[1]) + " dBm";
  } else {
    quality = F("nicht verbunden");
  }

  // Fenstertext
  const char* fenster_txt = fenster ? "mind. eins offen" : "alle zu";

  // Heizungstext
  const char* hk_txt = hk ? "An" : "Aus";

  // Ist-Temperatur (mit Sensorfehler-Abfang)
  String temp_ist_str;
  if (klima_values[0] < -100.0f) {  // z. B. -127 bei Fehler
    temp_ist_str = F("Sensorfehler");
  } else {
    temp_ist_str = String(klima_values[0], 1) + " °C";
  }

  // Soll-Temperatur
  String temp_soll_str = String(thermostat, 1) + " °C";

  String s = DGWLAN::DGHOME_html_header("Raumklima");

  s += F("<table style='width:100%;max-width:480px;margin:0 auto;border-collapse:collapse;'>");

  // --- Titelzeile ---
  s += F("<tr><th colspan='2'>");
  s += DEVICE_NAME;
  s += F("</th></tr>");

  // --- System / Verbindung ---
  s += F("<tr><th colspan='2'>System & Verbindung</th></tr>");

  s += F("<tr><td>Hostname</td><td align='right'><code>");
  s += DGWLAN::hostname();
  s += F("</code></td></tr>");

  s += F("<tr><td>Version</td><td align='right'>");
  s += DEVICE_VERSION;
  s += F("</td></tr>");

  s += F("<tr><td>Datum</td><td align='right'>");
  s += DEVICE_DATE;
  s += F("</td></tr>");

  s += F("<tr><td>CPU</td><td align='right'>");
  s += DEVICE_CPU;
  s += F("</td></tr>");

  s += F("<tr><td>Autor</td><td align='right'>");
  s += DEVICE_AUTHOR;
  s += F("</td></tr>");

  s += F("<tr><td>IP-Adresse</td><td align='right'><code>");
  s += ip;
  s += F("</code></td></tr>");

  s += F("<tr><td>Verbindung</td><td align='right'>");
  s += connected ? F("<b>verbunden</b>") : F("<b>nicht verbunden</b>");
  s += F("</td></tr>");

  s += F("<tr><td>Netzqualität</td><td align='right'>");
  s += quality;
  s += F("</td></tr>");

  s += F("<tr><td>Fehler</td><td align='right'><code>");
  s += String(err);
  s += F(" – ");
  s += err_txt;
  s += F("</code></td></tr>");

  // --- Klima & Heizung ---
  s += F("<tr><th colspan='2'>Raumklima</th></tr>");

  s += F("<tr><td>Temperatur (Ist)</td><td align='right'>");
  s += temp_ist_str;
  s += F("</td></tr>");

  s += F("<tr><td>Temperatur (Soll)</td><td align='right'>");
  s += temp_soll_str;
  s += F("</td></tr>");

  s += F("<tr><td>Fenster</td><td align='right'>");
  s += fenster_txt;
  s += F("</td></tr>");

  s += F("<tr><td>Heizung</td><td align='right'>");
  s += hk_txt;
  s += F("</td></tr>");

  // --- Thermostat-Einstellung ---
  s += F("<tr><th colspan='2'>Thermostat einstellen</th></tr>");
  s += F("<tr><td colspan='2'>");
  s += F("<form method='POST' action='/thermostat' "
         "style='display:block;width:100%;max-width:480px;margin:0 auto;'>");

  s += F("<label for='soll'>Soll-Temperatur [°C]</label>");
  s += F("<input type='number' id='soll' name='soll' "
         "step='0.5' min='15.0' max='27.0' "
         "style='width:100%;box-sizing:border-box;' value='");
  s += String(thermostat, 1);
  s += F("'>");

  s += F("<button type='submit' class='B00 Blue' "
         "style='width:100%;margin-top:6px;'>Speichern</button>");

  s += F("</form>");
  s += F("</td></tr>");

  // --- Buttons unten ---
  s += F("<tr><td colspan='2' style='padding-top:10px;'>");

  s += F("<a href='/' style='display:block;margin-top:4px;'>"
         "<button type='button' class='B00 Yellow' style='width:100%'>Aktualisieren</button>"
         "</a>");

  s += F("<a href='/wlan' style='display:block;margin-top:4px;'>"
         "<button type='button' class='B00 Blue' style='width:100%'>WLAN-Konfiguration</button>"
         "</a>");

  s += F("<a href='/diag' style='display:block;margin-top:4px;'>"
         "<button type='button' class='B00 Grey2' style='width:100%'>Diagnose</button>"
         "</a>");

  s += F("</td></tr>");

  // Footer
  s += F("<tr><td colspan='2' align='center' class='muted' style='padding-top:10px'>© by ");
  s += DEVICE_AUTHOR;
  s += F(" ");
  s += DEVICE_DATE;
  s += F("</td></tr>");

  s += F("</table>");

  s += DGWLAN::DGHOME_html_footer();
  DGHOME_web().send(200, "text/html; charset=utf-8", s);
}

void handleThermostatSet() {
  if (DGHOME_web().hasArg("soll")) {
    String v = DGHOME_web().arg("soll");
    v.trim();
    if (v.length()) {
      float t = v.toFloat();

      // Clamp auf 15–27 °C
      if (t < 15.0f) t = 15.0f;
      if (t > 27.0f) t = 27.0f;

      // auf 0.5 °C runden OHNE math.h
      t = ((int)(t * 2.0f + 0.5f)) / 2.0f;

      thermostat = t;

      // Direkt an den Server schicken
      thermostat_soll(thermostat);

      // Display wieder hell machen
      u8g2.setContrast(contrast_hell);
      last_display = millis();

      // Anzeige im nächsten loop-Durchgang aktualisieren
      refresh = true;
    }
  }
  // Danach wieder zurück zur Hauptseite
  handleRoot();
}


// ---------------------------------------------------------------------------
// MultiCall über DG_Home_WLAN
// ---------------------------------------------------------------------------

bool send_multicall() {
  bool ok = DGWLAN::send_multicall(
    mc_methods,
    mc_param_types,
    mc_params,
    mc_results,
    MC_COUNT,
    "klima_daten_set",
    DEVICE_NAME,
    klima_values,
    KLIMA_COUNT);

  if (!ok) {
    return false;
  }

  // 1) Fenster
  bool irgendeinFensterOffen = false;
  for (size_t i = 0; i < FENSTER_COUNT; ++i) {
    if (mc_results[IDX_OFFSET_FENSTER + i] != 0.0f) {
      irgendeinFensterOffen = true;
      break;
    }
  }
  if (fenster != irgendeinFensterOffen) {
    fenster = irgendeinFensterOffen;
    refresh = true;
  }

  // 2) Heizung (Hauptstatus)
  bool h = false;
  if (HEIZUNG_COUNT > 0) {
    h = (mc_results[IDX_OFFSET_HEIZUNG] != 0.0f);
  }
  if (hk != h) {
    hk = h;
    refresh = true;
  }

  // 3) Thermostat-Sollwert vom Server (nur übernehmen,
  //    wenn lokal nichts in Bearbeitung)
  if (m_thermostat == 0) {
    float t = mc_results[IDX_HZG_GET];
    if (thermostat != t) {
      thermostat = t;
      refresh = true;
    }
  }

  // 4) klima_daten_set Ergebnis: IDX_KLIMA_SET (optional auswertbar)

  return true;
}

// ---------------------------------------------------------------------------
// Anzeige
// ---------------------------------------------------------------------------

void refresh_display() {
  u8g2.clearBuffer();

  u8g2.setFont(font1);
  u8g2.drawUTF8(0, 8, "Raumklima");

  // Ist-Temperatur
  u8g2.setFont(font2);
  u8g2.setCursor(0, 26);

  if (klima_values[0] < -100.0f) {  // Sensorfehler, z.B. -127
    u8g2.drawUTF8(0, 26, "Sensorfehler");
  } else {
    u8g2.print(klima_values[0], 1);
    u8g2.drawUTF8(32, 26, "°C");
  }

  u8g2.setFont(font2);
  u8g2.setCursor(0, 64);

  uint8_t err = DGWLAN::getLastError();

  if (err == 0) {
    // kein Kommunikationsfehler
    if (!fenster) {
      // Solltemperatur
      u8g2.print(thermostat, 1);
      u8g2.drawUTF8(32, 64, "°C");

      // Status Heizung
      u8g2.setFont(font1);
      u8g2.setCursor(0, 46);
      u8g2.print(F("Thermostat"));
      u8g2.setCursor(64, 46);
      u8g2.print(F("Heizung"));

      u8g2.setFont(font1);
      u8g2.setCursor(64, 64);
      if (hk) {
        u8g2.print(F("An"));
      } else {
        u8g2.print(F("Aus"));
      }
    } else {
      // Fenster offen
      u8g2.print(F("Fenster offen!"));
    }
  } else {
    // Fehlermeldung aus DG_Home_WLAN
    u8g2.print(DGWLAN::getLastErrorText());
  }

  // WLAN-Signalbalken (nur wenn nicht "Kein WLAN")
  if (err != 1) {
    // immer ein Balken
    u8g2.drawBox(108, 3, 2, 2);
    if (rssi_graph > 0) {
      u8g2.drawBox(112, 2, 4, 3);
    }
    if (rssi_graph > 1) {
      u8g2.drawBox(118, 1, 4, 4);
    }
    if (rssi_graph > 2) {
      u8g2.drawBox(124, 0, 4, 5);
    }
  }

  u8g2.sendBuffer();
}

// ---------------------------------------------------------------------------
// Interrupts (2 Taster)
// ---------------------------------------------------------------------------

IRAM_ATTR void taster_01() {
  if (!pm_change) {
    pm_change = 1;  // runter
  }
}

IRAM_ATTR void taster_02() {
  if (!pm_change) {
    pm_change = 2;  // rauf
  }
}

// ---------------------------------------------------------------------------
// SETUP
// ---------------------------------------------------------------------------

void setup() {
  // Display Begrüßung
  u8g2.begin();
  u8g2.clearBuffer();
  u8g2.setFont(font1);
  u8g2.setContrast(contrast_hell);
  u8g2.setCursor(0, 12);
  u8g2.print(F("Hallo Birgit & Dirk"));
  u8g2.setCursor(0, 24);
  u8g2.print(F("Sensorname: "));
  u8g2.print(DEVICE_NAME);
  u8g2.setCursor(0, 36);
  u8g2.print(F("Version: "));
  u8g2.print(DEVICE_VERSION);
  u8g2.sendBuffer();

  // Web-Routen
  DGHOME_web().on("/", handleRoot);
  DGHOME_web().on("/thermostat", HTTP_POST, handleThermostatSet);

  // DG_Home_WLAN starten
  DGWLAN::begin();

  init_multicall_config();

  // Taster
  pinMode(TA_1, INPUT_PULLUP);
  pinMode(TA_2, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(TA_1), taster_01, FALLING);
  attachInterrupt(digitalPinToInterrupt(TA_2), taster_02, FALLING);

  // DS18B20 initialisieren inkl. erster Messung
  sensors.begin();
  sensors.setResolution(12);
  sensors.setWaitForConversion(false);
  sensors.requestTemperatures();
  delay(800);  // erste Konvertierung abwarten
  klima_values[0] = sensors.getTempCByIndex(0);

  // RSSI initial + Balken
  float rssi = DGWLAN::rssi();
  klima_values[1] = rssi;
  int rssi_level =
    (rssi >= -55) ? 3 : (rssi >= -65) ? 2
                      : (rssi >= -75) ? 1
                                      : 0;
  rssi_graph = rssi_level;

  last_display = millis();
  loop_count = loop_send;  // direkt beim ersten Durchlauf MultiCall
}

// ---------------------------------------------------------------------------
// LOOP
// ---------------------------------------------------------------------------

void loop() {
  // Einmal Zeit holen
  unsigned long now = millis();

  // WLAN/OTA/HTTP-Service der Lib
  DGWLAN::loop();

  // Display dimmen
  if (last_display && (now - last_display >= (unsigned long)display_dunkel)) {
    last_display = 0;
    u8g2.setContrast(contrast_dunkel);
  }

  // Taster-Entprellung & Thermostat-Verstellung
  if (!last_debounce) {
    if (pm_change) {
      last_debounce = now;
      u8g2.setContrast(contrast_hell);
      last_display = now;

      // nur verstellen, wenn keine Fenster-Sperre
      if (!fenster) {
        m_thermostat = 1;

        if (pm_change == 1 && thermostat >= 15.5f) {
          if (thermostat > 27.0f) {
            thermostat = 27.0f;
          } else {
            thermostat -= 0.5f;
          }
        } else if (pm_change == 2 && thermostat <= 26.5f) {
          if (thermostat < 15.0f) {
            thermostat = 15.0f;
          } else {
            thermostat += 0.5f;
          }
        }
        refresh = true;
      }
    }
  } else if (now - last_debounce >= (unsigned long)debounce) {
    pm_change = 0;
    last_debounce = 0;
  }

  // 1 Sekunde vor dem Auslesen, Sensor anstoßen
  if (!data_read && (now - last_data_send >= (data_send - 1000UL))) {
    sensors.requestTemperatures();
    data_read = true;
  }

  // Zyklische Klima-Abfrage / MultiCall
  if (now - last_data_send >= data_send) {
    last_data_send = now;
    data_read = false;

    // Temperatur lesen & runden (1 Nachkommastelle, ohne math.h)
    float t = sensors.getTempCByIndex(0);
    t = ((int)(t * 10.0f + 0.5f)) / 10.0f;

    if (klima_values[0] != t) {
      klima_values[0] = t;
      refresh = true;
    }

    // RSSI: für Server immer aktualisieren,
    // Display nur bei Stufenänderung anfassen
    float rssi = DGWLAN::rssi();

    if (klima_values[1] != rssi) {
      klima_values[1] = rssi;

      int rssi_level =
        (rssi >= -55) ? 3 : (rssi >= -65) ? 2
                          : (rssi >= -75) ? 1
                                          : 0;

      if (rssi_graph != rssi_level) {
        rssi_graph = rssi_level;
        refresh = true;
      }
    }

    // Wenn Benutzer kurz zuvor verstellt hat,
    // dann beim nächsten Sendezyklus ans Server-Thermostat schicken
    if (m_thermostat) {
      if (m_thermostat == 1) {
        m_thermostat = 2;
      } else if (m_thermostat == 2) {
        m_thermostat = 0;
        thermostat_soll(thermostat);
      }
    }

    // Multicall
    if (loop_count >= loop_send) {
      loop_count = 0;
      send_multicall();
    } else {
      loop_count++;
    }
  }

  // Display nur bei Änderungen neu aufbauen
  if (refresh) {
    refresh_display();
    refresh = false;
  }
}

Sorry: Bedienerfehler im Forum. Erst auf "Code" einstellen dann Code hineinkopieren...

Ok, der Teil kommt komplett raus:

Dafür kommt der Anstoss des request in die nächste Funktion:

  // Zyklische Klima-Abfrage / MultiCall

  if (now - last_data_send >= data_send)
  {
    last_data_send = now;
    data_read = false;
    // Temperatur lesen & runden (1 Nachkommastelle, ohne math.h)
    float t = sensors.getTempCByIndex(0);
    t = ((int)(t * 10.0f + 0.5f)) / 10.0f;
    sensors.requestTemperatures();            // >> Den nächsten Request nach dem lesen auslösen.

    if (klima_values[0] != t)
    {
      klima_values[0] = t;
      refresh = true;
    }

Wenn das funktioniert, hast Du zwar einen 9 Sekunden Versatz, dann lösen wir aber die Funktion so auf, dass Du wieder die richtigen Zahlen bekommst.