TonUINO ESP32 Tag Writer / Alternative zur App

Danke, Problem gelöst! es fehlten 2 Zeilen in deinem Code.

:+1:

Ja das hängt von deinen libs dann ab, muss man eben immer anpassen, daher vermeide ich das. Ich lass bei mir immer alles in Container laufen da geht das. Sonst wird man ja wahnsinnig wenn man bei jedem Projekt da erst die libs wieder alle wechseln muss. Zumindest wenn man diese „einfache“ Arduino IDE benutzt.

Das lag nicht an den libs.

Achso naja an was auch immer, bei mir geht’s auch ohne die 2 Zeilen :wink:

So, ich habe mich mal mit dem ESP32-Sketch beschäftigt und einige Verbesserungen eingebaut. Die Ausgabe ist nun komplett in Englisch (bis auf wenige Ausgaben beim FW-Update, da habe ich nicht gefunden, wo die Strings herkommen). Da ich ein Fan von deutschen Benutzeroberflächen bin, kann man jetzt im Config-Menü die Sprache auch komplett auf Deutsch umstellen. Dafür reicht ein Häkchen mit anschließendem Neustart. Es läuft zwar noch nicht alles so rund, wie ich mir das vorstelle, aber zunächst einmal funktioniert es. Auch der Bug mit dem Wegnehmen und Neuauflegen der Karte ist behoben. Es reicht nun, wenn man den Lesen-Button ein zweites Mal drückt. Nicht optimal aber zu verschmerzen. Ein wenig hakt es auch noch beim Speichern der Konfiguration. Man muss auf jeden Fall „Save“ bzw. „Sichern“ drücken, sonst läuft man Gefahr, dass die Einstellungen neu eingegeben werden müssen (was aber auch kein Drama ist). Der Button „Update“ in diesem Menü geht ins Leere ich weiß nicht, wofür der an dieser Stelle gut war.

Cool! Ja englisch reicht mir immer, aber ich bin auch nicht so ein GUI Freund, daher ist das immer das unwichtigste, funktionieren muss es :wink:
Wichtig ist da eigentlich ne API, das man per Webcall die karte lesen und schreiben kann (Um das dann über das Excel Tool zu beschreiben, so die Idee). Das geht bei mir auch schon, aber die Rückgabewerte fehlen noch.
Den Update Button im config, na siehste doch unten, der soll zu FW Update zeigen. Da es beim ESP32 aber /ota ist kann es sein, dass der noch nach /update verweist. Ich habe da verschiedene OTA libs versucht daher kann es sein, dass das nicht passt.
Wegen dem Lesen Button 2 mal drücken, nee das geht auch mit nur einmal drücken. Ich muss das im neuen Jahr mal raussuchen, das gingt auch ohne den IRQ.
Speichern der config? Das sollte aber alles klappen, es sei denn du hast da was hinzugefügt. Und klar save muss man drücken, dafür ist der Button ja da oder, verstehe ich nicht.
Aber hier bin ich das am JSON6 umstellen, damit man von dem 6er kram mal weg ist. Da spart man sich die allocierung der Buffer etc.

Dafür ist ja der Reiter FW-Update da, darüber funktioniert OTA. An dieser Stelle ist der Button eher verwirrend und findet auch kein Ziel. Ich habe ihn daher hier entfernt.

Ich glaube, das ist ein Missverständnis. Bei deiner FW war es so, dass wenn die Karte auf dem Leser lag und der Lesen Button gedrückt wurde, funktionierte das beim zweiten Mal nicht. Die Karte musste erst entfernt und wieder neu aufgelegt werden, erst dann führte das zweite Drücken zum Ziel. Nun ist es so, dass die Karte liegen bleiben kann. Wenn ein zweites Mal gedrückt wird, werden die Werte auch ein zweites Mal eingelesen. Das meinte ich damit.

Dann probier mal Folgendes: Gehe ins Config Menü, ändere nichts, gehe ins Tonuino Menu und wieder zurück ins Config Menü. Sind die Werte weg oder immer noch gespeichert? Das ist eindeutig ein Bug, das darf nicht sein. Wenn du nach jedem Weg ins Config Menü den Save Button drückst passiert das nicht.

Wo kommen denn die Strings aus dem OTA Menü her? („Datei auswählen“ und „Keine Datei ausgewählt“) Die finde ich im Code nicht und auch in der Library scheint es sie nicht zu geben.

Ja der Button kann weg, das überlasse ich dir, mich stört er nicht :wink: Die Buttons oben sollten eigentlich nicht sein, das kam erst später. Stört aber auch nicht, wenn es da ist der frisst kein Brot der Button. Wie gesagt ich lege kein Wert auf GUI, dient nicht der Funktion :wink:
Mit dem Lesen, naja dann ist es doch so wie es sein soll oder was erwartest du? Das er beim Auflegen automatisch die Karte liest? Dann musste HTML5 nehmen, da geht das relativ easy wenn man das möchte. Wenn du dann nicht ein ewigen leseloop haben möchtest, schließt du einfach den IRQ PIN an und machst das damit. Je nach Gusto.
Wegen dem config, verstehe ich nicht, bei mir geht das mit der Version. Erschließt sich mir auch nicht wie du das beschreibst. Dann hast du da definitiv was falsch gebaut. Schau dir mal den Code genau an „wo“ die Daten herkommen und „was“ passiert wenn du den URL aufrufst.
Strings aus dem OT Menü? Das kommt alles aus der lib für den WebServer. Ich weiß aber auch nicht was du da meinst für Strings.

Steht das bei dir nicht da? Wenn ich „FW-Update“ klicke, steht da ein Button mit „Datei auswählen“ und eine Mitteilung „Keine Datei ausgewählt“. Diese Strings meine ich.

Das schrieb ich ja schon, dass da der Wurm drin ist. In der Konsole sehe ich folgende Meldung, wenn ich z.B. aus dem Tonuino Menü ins Config Menü gehe:

22:37:34.949 -> JSO: {"hostname":"TTW","ntpserver":"EUROPE.POOL.NTP.ORG","ntpenabled":"1","DEenabled":"1"}
22:37:34.949 -> JSO: parsed json
22:37:34.949 -> WEB: Number of args received: 0
22:37:34.949 -> 
22:37:34.949 -> SPI: save config.json...
22:37:34.949 -> {"hostname":"","ntpserver":"","ntpenabled":"0","DEenabled":"0"}

Das heißt, dass die Werte nicht übertragen werden, sie waren vorher drin und sind dann weg (args 0). In den Feldern sieht man sie aber noch eingetragen. Wenn ich den Button Save drücke, ist alles OK. Wenn nicht, wird der Status ohne die Werte gespeichert.

Hier muss ich noch etwas suchen und mich rein denken.

Ja wie gesagt, dann prüfe noch mal, was du da geändert hast mit deiner Sprache etc. Bei mir geht alles. Ich hatte ja bereits gesagt, dass ich keinen Support geben kann wenn ich den Code rausgebe. Ich hab da leider keine Zeit für, weil ich noch gefühlt 120 andere Projekte auf dem Tisch habe, das tut mir leid. Dann musst du deinen Code hier reinstellen mit allen Versionen der libs die du verwendest und dann kann man schauen, aber wie gesagt bei mir mangelt es gerade an Zeit und auch Lust, weil so wie es läuft reicht mir das momentan. Mein Fokus ist eher auf die WebAPI da ich diese GUI nicht benutze(n möchte) aber selbst dafür fehlt mir gerade die Zeit.

OK, ich will keinen Support aber vielleicht hat ja noch eine(r) eine Idee.
Ich habe nichts anderes gemacht als in der Config Seite eine zusätzliche Checkbox exakt nach dem Muster der NTP Checkbox einzubauen. Das funktioniert auch alles. Wenn die Checkbox mit einem Haken versehen ist und das Board neu gestartet wurde, wird alles in deutscher Sprache angezeigt. Wenn ich dann die Config Seite neu aufrufe, scheint alles in Ordnung zu sein, alle Einstellungen sind drin. Allerdings muss ich dann den Save Button drücken, sonst wird das nicht gespeichert. In der Ursprungsversion funktioniert das alles. Ich habe den Fehler nun so lokalisiert, dass ich eine Zeile auskommentieren kann und das Verhalten ist korrekt. Wenn die Zeile aktiv ist, werden die Einstellungen vergessen, wenn ich nicht abspeichere. Die Zeile ist in der Routine loadConfig() und zwar die Zeile
if (json.containsKey("deenabled")) de_enabled = json["deenabled"]; Die Zeile einfach weg lassen ist selbstverständlich auch keine Lösung, weil dann die neu eingebaute Checkbox wirkungslos ist.

bool loadConfig() {
  if (SPIFFS.exists("/config.json")) {
      File configFile = SPIFFS.open("/config.json", "r");
      if (configFile) {
        DEBUG_PRINTLN("opened config file");
        size_t size = configFile.size();
        if (size > 1024) {
          DEBUG_PRINTLN("Config file size is too large");
          return false;
        }
        std::unique_ptr<char[]> buf(new char[size]);            // Allocate a buffer to store contents of the file.
        configFile.readBytes(buf.get(), size);                  // Input Buffer (needed by ArduinoJson)
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject(buf.get());
        #ifdef DEBUG
          json.printTo(Serial);
        #endif
        if (json.success()) {
          DEBUG_PRINTLN("\nparsed json");
          if (json.containsKey("hostname")) strncpy(host_name, json["hostname"], 20);
          if (json.containsKey("ntpserver")) strncpy(ntpserver, json["ntpserver"], 30);
          if (json.containsKey("ntpenabled")) ntp_enabled = json["ntpenabled"];
          if (json.containsKey("deenabled")) de_enabled = json["deenabled"];
        }
        else {
          DEBUG_PRINTLN("Failed to parse config file");
          return false;
        }
      }
      else {
        DEBUG_PRINTLN("Failed to open config file");
        return false;
      }
  }
  return true;
}

Hat irgend jemand noch einen Hinweis, wo ich ansetzen muss?

Da ich bei der Fehlersuche auch Variablenbezeichnungen geändert habe, lautet die Ausgabe der Konsole jetzt natürlich so:

15:11:17.758 -> JSO: {"hostname":"TTW","ntpserver":"EUROPE.POOL.NTP.ORG","ntpenabled":"1","deenabled":"1"}
15:11:17.758 -> JSO: parsed json
15:11:17.758 -> WEB: Number of args received: 0
15:11:17.758 -> 
15:11:17.758 -> SPI: save config.json...
15:11:17.758 -> {"hostname":"","ntpserver":"","ntpenabled":"0","deenabled":"0"}

und nach dem Speichern:

15:11:25.100 -> WEB: Connection received - /config (save)
15:11:25.100 -> WEB: Number of args received: 4
15:11:25.137 -> Arg 0 –> host_name_conf:TTW
15:11:25.137 -> Arg 1 –> ntpserver_conf:EUROPE.POOL.NTP.ORG
15:11:25.137 -> Arg 2 –> ntpenabled:on
15:11:25.137 -> Arg 3 –> deenabled:on
15:11:25.137 -> 
15:11:25.137 -> SPI: save config.json...
15:11:25.137 -> {"hostname":"TTW","ntpserver":"EUROPE.POOL.NTP.ORG","ntpenabled":"1","deenabled":"1"}

Wegen dem OTA, schau mal übrigens hier rein, da ist doch alles: „void Handle_ota(){“

Wegen dem config Problem, also du startest den ESP, gehts auf die UI, dann wählst du config aus, es stehen alle Werte drin, dann geht du auf Tonuino und wieder zurück auf config und dann ist alles leer?
Hast du die void sendConfigPage auch angepasst? Dort läd er ja die aktuellen Daten aus der config sofern der type nicht gesetzt ist (also wenn type = 1 bedeutet das, du hast vorher save gedrückt) Die loadconfig routine wird nur nach einem reset ausgeführt.

Achso und noch ein Tip wegen der Sprache, dann leg die verschiedenen Sprachen doch als Textfile ab und machste lieber eine Liste und dann kannste da „de“ „en“ „es“ was nicht alles reinpacken und dann werden nur die entsprechenden strings aus dem textfile gelesen. So hatte ich das mal gemacht. Aber irgendwie bin ich von dem multi language weg :wink:

Die Strings „Datei auswählen“ und „Keine Datei ausgewählt“ sind da aber nicht zu finden. Die Routine hatte ich ja schon angesehen.

Ich wähle Config aus und es stehen alle Werte drin, aber in der Konsole finde ich

11:47:13.333 -> JSO: 
{"hostname":"TTW","ntpserver":"EUROPE.POOL.NTP.ORG","ntpenabled":"1","deenabled":"1"}
11:47:13.333 -> JSO: parsed json
11:47:13.333 -> WEB: Number of args received: 0
11:47:13.333 -> 
11:47:13.333 -> SPI: save config.json...
11:47:13.333 -> {"hostname":"","ntpserver":"","ntpenabled":"0","deenabled":"0"}

man beachte die letzten Zeilen: WEB: Number of args received: 0
und {„hostname“:„“,„ntpserver“:„“,„ntpenabled“:„0“,„deenabled“:„0“}
Wenn ich dann „Save“ drücke ist alles ok aber wenn nicht, ist alles weg, wenn ich einen anderen Menüpunkt aufrufe. Die void sendConfigPage habe ich wie folgt angepasst:

void sendConfigPage(String message, String header, int type, int httpcode)
{
  char host_name_conf[20] = "";
  char ntpserver_conf[30] = "";
  bool ntpenabled_conf;
  bool deenabled_conf;

  if (type == 1){                                              // Type 1 -> save data
    String message = "WEB: Number of args received: ";
    message += String(httpServer.args()) + "\n";
    for (int i = 0; i < httpServer.args(); i++) {
      message += "Arg " + (String)i + " –> ";
      message += httpServer.argName(i) + ":" ;
      message += httpServer.arg(i) + "\n";
    }
    
    strncpy(host_name_conf, httpServer.arg("host_name_conf").c_str(), 20);
    strncpy(ntpserver_conf, httpServer.arg("ntpserver_conf").c_str(), 30);
    if (httpServer.hasArg("ntpenabled")) {ntpenabled_conf = true;} else {ntpenabled_conf = false;}
    if (httpServer.hasArg("deenabled")) {deenabled_conf = true;} else {deenabled_conf = false;}

    DEBUG_PRINTLN(message);

    // validate values before saving
    bool validconf = true;
    if (validconf)
    {
      DEBUG_PRINTLN("SPI: save config.json...");
      DynamicJsonBuffer jsonBuffer;
      JsonObject& json = jsonBuffer.createObject();
      json["hostname"] = String(host_name_conf);
      json["ntpserver"] = String(ntpserver_conf);
      json["ntpenabled"] = String(ntpenabled_conf);
      json["deenabled"] = String(deenabled_conf);

      File configFile = SPIFFS.open("/config.json", "w");
      if (!configFile) {
        DEBUG_PRINTLN("SPI: failed to open config file for writing");
      }
      #ifdef DEBUG
        json.printTo(Serial);
      #endif
      DEBUG_PRINTLN("");
      json.printTo(configFile);
      configFile.close();
      //end save
    }
  } else {                                // Type 0 -> load data
    if (SPIFFS.begin())
    {
      DEBUG_PRINTLN("SPI: mounted file system");
      if (SPIFFS.exists("/config.json"))
      {
        //file exists, reading and loading
        DEBUG_PRINTLN("SPI: reading config file");
        File configFile = SPIFFS.open("/config.json", "r");
        if (configFile) {
          DEBUG_PRINTLN("SPI: opened config file");
          size_t size = configFile.size();
          // Allocate a buffer to store contents of the file.
          std::unique_ptr<char[]> buf(new char[size]);

          configFile.readBytes(buf.get(), size);
          DynamicJsonBuffer jsonBuffer;
          JsonObject& json = jsonBuffer.parseObject(buf.get());
          DEBUG_PRINT("JSO: ");
          #ifdef DEBUG
            json.printTo(Serial);
          #endif
          if (json.success()) {
            DEBUG_PRINTLN("\nJSO: parsed json");
            if (json.containsKey("hostname")) strncpy(host_name_conf, json["hostname"], 20);
            if (json.containsKey("ntpserver")) strncpy(ntpserver_conf, json["ntpserver"], 30);
            if (json.containsKey("ntpenabled")) ntpenabled_conf = json["ntpenabled"];
            if (json.containsKey("deenabled")) deenabled_conf = json["deenabled"];
          } else {
            DEBUG_PRINTLN("JSO: failed to load json config");
          }
        }
      }
    } else {
      DEBUG_PRINTLN("SPI: failed to mount FS");
    }
  }
  String htmlDataconf;        // Hold the HTML Code
  buildHeader();
  buildFooter();
  htmlDataconf=htmlHeader;
  
  if (type == 1)
    htmlDataconf+="      <div class='row'><div class='col-md-12'><div class='alert alert-success'><strong>" + header + "</strong> " + message + "</div></div></div>\n";
  if (type == 2)
    htmlDataconf+="      <div class='row'><div class='col-md-12'><div class='alert alert-warning'><strong>" + header + "</strong> " + message + "</div></div></div>\n";
  if (type == 3)
    htmlDataconf+="      <div class='row'><div class='col-md-12'><div class='alert alert-danger'><strong>" + header + "</strong> " + message + "</div></div></div>\n";
  htmlDataconf+="      <div class='row'>\n";
  htmlDataconf+="<form method='post' action='/config'>";
  htmlDataconf+="        <div class='col-md-12'>\n";
  if (de_enabled) {
    htmlDataconf+="          <h3>Konfiguration</h3>\n";
  } 
  else {
    htmlDataconf+="          <h3>Configuration</h3>\n";
  }
  htmlDataconf+="          <table class='table table-striped' style='table-layout: fixed;'>\n";
  if (de_enabled) {
    htmlDataconf+="            <thead><tr><th>Option</th><th>Aktueller Wert</th><th>Neuer Wert</th></tr></thead>\n";
    } 
  else {
    htmlDataconf+="            <thead><tr><th>Option</th><th>Current value</th><th>New value</th></tr></thead>\n";
    }
  htmlDataconf+="            <tbody>\n";
  htmlDataconf+="            <tr class='text-uppercase'><td>Hostname</td><td><code>" + ((host_name_conf[0] == 0 ) ? String("(" + String(host_name) + ")") : String(host_name_conf)) + "</code></td><td><input type='text' id='host_name_conf' name='host_name_conf' value='" + String(host_name_conf) + "'></td></tr>\n";
  htmlDataconf+="            <tr class='text-uppercase'><td>NTP Server</td><td><code>" + ((ntpserver_conf[0] == 0 ) ? String("(" + String(poolServerName) + ")") : String(ntpserver_conf)) + "</code></td><td><input type='text' id='ntpserver_conf' name='ntpserver_conf' value='" + String(ntpserver_conf) + "'></td></tr>\n";
  if (de_enabled) {
    htmlDataconf+="            <tr class='text-uppercase'><td>NTP ein?</td><td><code>" + (ntpenabled_conf ? String("Ja") : String("Nein")) + "</code></td><td><input type='checkbox' id='ntpena' name='ntpenabled' " + (ntpenabled_conf ? String("checked") : String("")) + "></td></tr>";
    htmlDataconf+="            <tr class='text-uppercase'><td>Sprache Deutsch ein?</td><td><code>" + (deenabled_conf ? String("Ja") : String("Nein")) + "</code></td><td><input type='checkbox' id='deenab' name='deenabled' " + (deenabled_conf ? String("checked") : String("")) + "></td></tr>";
//    htmlDataconf+=" <tr><td colspan='5' class='text-center'><em><a href='/reboot' class='btn btn-sm btn-danger'>Neustart</a>  <a href='/update' class='btn btn-sm btn-warning'>Update</a>  <button type='submit' class='btn btn-sm btn-success'>Sichern</button>  <a href='/' class='btn btn-sm btn-primary'>Abbruch</a></em></td></tr>";
    htmlDataconf+=" <tr><td colspan='5' class='text-center'><em><a href='/reboot' class='btn btn-sm btn-danger'>Neustart</a>  <button type='submit' class='btn btn-sm btn-success'>Sichern</button>  <a href='/' class='btn btn-sm btn-primary'>Abbruch</a></em></td></tr>";
  }
  else {
    htmlDataconf+="            <tr class='text-uppercase'><td>NTP on?</td><td><code>" + (ntpenabled_conf ? String("Yes") : String("No")) + "</code></td><td><input type='checkbox' id='ntpena' name='ntpenabled' " + (ntpenabled_conf ? String("checked") : String("")) + "></td></tr>";
    htmlDataconf+="            <tr class='text-uppercase'><td>German language on?</td><td><code>" + (deenabled_conf ? String("YES") : String("No")) + "</code></td><td><input type='checkbox' id='deenab' name='deenabled' " + (deenabled_conf ? String("checked") : String("")) + "></td></tr>";
//    htmlDataconf+=" <tr><td colspan='5' class='text-center'><em><a href='/reboot' class='btn btn-sm btn-danger'>Roboot</a>  <a href='/update' class='btn btn-sm btn-warning'>Update</a>  <button type='submit' class='btn btn-sm btn-success'>Save</button>  <a href='/' class='btn btn-sm btn-primary'>Cancel</a></em></td></tr>";
    htmlDataconf+=" <tr><td colspan='5' class='text-center'><em><a href='/reboot' class='btn btn-sm btn-danger'>Roboot</a>  <button type='submit' class='btn btn-sm btn-success'>Save</button>  <a href='/' class='btn btn-sm btn-primary'>Cancel</a></em></td></tr>";
  }
  htmlDataconf+="            </tbody></table>\n";
  htmlDataconf+="          </div></div>\n";
  
  htmlDataconf+=htmlFooter;

  httpServer.send(httpcode, "text/html; charset=UTF-8", htmlDataconf);
  httpServer.client().stop();
}

Aha, und haste dein Browser mal auf Englisch umgestellt und geschaut was dann da steht? Das ist HTML… Ich wette das kann man einstellen, Google zeigt mir als erstes: https://wiki.selfhtml.org/wiki/HTML/Tutorials/Formulare/input/Datei-Upload

Das andere muss ich mal schauen, die Debug Ausgaben habe ich mir nicht mehr angeschaut bzw. gepflegt. Da fehlt aber auch was in deiner Ausgabe. Da muss ein WEB: Connection received - /config … vorher kommen. drück mal bitte auf config und woanders und wieder auf config und poste davon mal die Debug-Ausgabe, komplett.

Ja, hab ich (Firefox), die Ausgabe bleibt aber trotzdem auf Deutsch. Ist aber auch nicht so wichtig, das habe ich mir schon gedacht, dass das vom Browser ist.
Edit: Nach Löschen des Caches ist die Ausgabe nun auf Englisch, wenn Englisch als Browsersprache eingestellt ist.

Hier ist die komplette Ausgabe config - Tonuino - config (ohne save zwischendrin):

16:09:58.949 -> WEB: Connection received - /config
16:09:58.949 -> SPI: mounted file system
16:09:58.949 -> SPI: reading config file
16:09:58.949 -> SPI: opened config file
16:09:58.949 -> JSO: {"hostname":"TTW","ntpserver":"EUROPE.POOL.NTP.ORG","ntpenabled":"1","deenabled":"0"}
16:09:58.949 -> JSO: parsed json
16:09:58.949 -> NTP: Transmit NTP Request
16:09:59.233 -> NTP: EUROPE.POOL.NTP.ORG  IP: 85.254.217.3
16:09:59.267 -> NTP: Receive NTP Response
16:09:59.303 -> WEB: Number of args received: 0
16:09:59.303 -> 
16:09:59.303 -> SPI: save config.json...
16:09:59.303 -> {"hostname":"","ntpserver":"","ntpenabled":"0","deenabled":"0"}
16:10:05.372 -> WEB: Connection received - /tonuino
16:10:05.409 -> WEB: Set new values using control page: 0
16:10:05.409 -> 
16:10:05.409 -> byteToHexStringRange: 00000000
16:10:05.409 -> byteToHexStringRange: 00
16:10:05.409 -> byteToHexStringRange: 00
16:10:05.409 -> byteToHexStringRange: 00
16:10:05.409 -> byteToHexStringRange: 00
16:10:05.409 -> byteToHexStringRange: 00
16:10:05.409 -> byteToHexStringRange: 00
16:10:05.409 -> byteToHexStringRange: 00
16:10:05.409 -> byteToHexStringRange: 00
16:10:05.409 -> byteToHexStringRange: 00
16:10:05.409 -> byteToHexStringRange: 00
16:10:05.443 -> byteToHexStringRange: 00
16:10:05.443 -> byteToHexStringRange: 00
16:10:05.443 -> byteToHexStringRange: 00
16:10:05.443 -> byteToHexStringRange: 00
16:10:05.443 -> byteToHexStringRange: 00
16:10:05.443 -> byteToHexStringRange: 00
16:10:05.443 -> byteToHexStringRange: 0000
16:10:05.443 -> byteToHexStringRange: 0000
16:10:05.443 -> byteToHexStringRange: 0000
16:10:05.443 -> byteToHexStringRange: 0000
16:10:05.443 -> byteToHexStringRange: 0000
16:10:05.443 -> byteToHexStringRange: 0000
16:10:05.443 -> byteToHexStringRange: 0000
16:10:05.443 -> byteToHexStringRange: 0000
16:10:05.479 -> byteToHexStringRange: 00
16:10:05.479 -> byteToHexStringRange: 00
16:10:05.479 -> byteToHexStringRange: 00
16:10:05.479 -> byteToHexStringRange: 00
16:10:05.479 -> byteToHexStringRange: 00
16:10:05.479 -> byteToHexStringRange: 00
16:10:05.479 -> byteToHexStringRange: 00
16:10:05.479 -> byteToHexStringRange: 00
16:10:05.479 -> byteToHexStringRange: 0000
16:10:05.479 -> byteToHexStringRange: 0000
16:10:05.479 -> byteToHexStringRange: 00
16:10:05.479 -> byteToHexStringRange: 00
16:10:05.479 -> byteToHexStringRange: 00
16:10:05.479 -> byteToHexStringRange: 00
16:10:05.479 -> byteToHexStringRange: 00
16:10:05.479 -> byteToHexStringRange: 00
16:10:05.479 -> byteToHexStringRange: 00
16:10:05.515 -> byteToHexStringRange: 00
16:10:05.515 -> byteToHexStringRange: 00
16:10:05.515 -> byteToHexStringRange: 00
16:10:05.515 -> byteToHexStringRange: 00
16:10:05.515 -> byteToHexStringRange: 00
16:10:05.515 -> byteToHexStringRange: 00
16:10:05.515 -> byteToHexStringRange: 00
16:10:05.515 -> byteToHexStringRange: 00
16:10:05.515 -> byteToHexStringRange: 00
16:10:09.387 -> WEB: Connection received - /config
16:10:09.387 -> SPI: mounted file system
16:10:09.387 -> SPI: reading config file
16:10:09.422 -> SPI: opened config file
16:10:09.422 -> JSO: {"hostname":"","ntpserver":"","ntpenabled":"0","deenabled":"0"}
16:10:09.422 -> JSO: parsed json
16:10:09.422 -> WEB: Number of args received: 0
16:10:09.422 -> 
16:10:09.422 -> SPI: save config.json...
16:10:09.422 -> {"hostname":"","ntpserver":"","ntpenabled":"0","deenabled":"0"}

… und jetzt mal mit save:

16:16:27.252 -> WEB: Connection received - /config
16:16:27.252 -> SPI: mounted file system
16:16:27.252 -> SPI: reading config file
16:16:27.252 -> SPI: opened config file
16:16:27.252 -> JSO: {"hostname":"TTW","ntpserver":"EUROPE.POOL.NTP.ORG","ntpenabled":"1","deenabled":"1"}
16:16:27.252 -> JSO: parsed json
16:16:27.252 -> WEB: Number of args received: 0
16:16:27.285 -> 
16:16:27.285 -> SPI: save config.json...
16:16:27.285 -> {"hostname":"","ntpserver":"","ntpenabled":"0","deenabled":"0"}
16:16:33.411 -> WEB: Connection received - /config (save)
16:16:33.411 -> WEB: Number of args received: 4
16:16:33.411 -> Arg 0 –> host_name_conf:TTW
16:16:33.411 -> Arg 1 –> ntpserver_conf:EUROPE.POOL.NTP.ORG
16:16:33.411 -> Arg 2 –> ntpenabled:on
16:16:33.411 -> Arg 3 –> deenabled:on
16:16:33.411 -> 
16:16:33.411 -> SPI: save config.json...
16:16:33.411 -> {"hostname":"TTW","ntpserver":"EUROPE.POOL.NTP.ORG","ntpenabled":"1","deenabled":"1"}
16:16:38.925 -> WEB: Connection received - /tonuino
16:16:38.925 -> WEB: Set new values using control page: 0
16:16:38.925 -> 
16:16:38.925 -> byteToHexStringRange: 00000000
16:16:38.925 -> byteToHexStringRange: 00
16:16:38.925 -> byteToHexStringRange: 00
16:16:38.962 -> byteToHexStringRange: 00
16:16:38.962 -> byteToHexStringRange: 00
16:16:38.962 -> byteToHexStringRange: 00
16:16:38.962 -> byteToHexStringRange: 00
16:16:38.962 -> byteToHexStringRange: 00
16:16:38.962 -> byteToHexStringRange: 00
16:16:38.962 -> byteToHexStringRange: 00
16:16:38.962 -> byteToHexStringRange: 00
16:16:38.962 -> byteToHexStringRange: 00
16:16:38.962 -> byteToHexStringRange: 00
16:16:38.962 -> byteToHexStringRange: 00
16:16:38.962 -> byteToHexStringRange: 00
16:16:38.962 -> byteToHexStringRange: 00
16:16:38.962 -> byteToHexStringRange: 00
16:16:38.962 -> byteToHexStringRange: 0000
16:16:38.962 -> byteToHexStringRange: 0000
16:16:39.000 -> byteToHexStringRange: 0000
16:16:39.000 -> byteToHexStringRange: 0000
16:16:39.000 -> byteToHexStringRange: 0000
16:16:39.000 -> byteToHexStringRange: 0000
16:16:39.000 -> byteToHexStringRange: 0000
16:16:39.000 -> byteToHexStringRange: 0000
16:16:39.000 -> byteToHexStringRange: 00
16:16:39.000 -> byteToHexStringRange: 00
16:16:39.000 -> byteToHexStringRange: 00
16:16:39.000 -> byteToHexStringRange: 00
16:16:39.000 -> byteToHexStringRange: 00
16:16:39.000 -> byteToHexStringRange: 00
16:16:39.000 -> byteToHexStringRange: 00
16:16:39.000 -> byteToHexStringRange: 00
16:16:39.000 -> byteToHexStringRange: 0000
16:16:39.000 -> byteToHexStringRange: 0000
16:16:39.034 -> byteToHexStringRange: 00
16:16:39.034 -> byteToHexStringRange: 00
16:16:39.034 -> byteToHexStringRange: 00
16:16:39.034 -> byteToHexStringRange: 00
16:16:39.034 -> byteToHexStringRange: 00
16:16:39.034 -> byteToHexStringRange: 00
16:16:39.034 -> byteToHexStringRange: 00
16:16:39.034 -> byteToHexStringRange: 00
16:16:39.034 -> byteToHexStringRange: 00
16:16:39.034 -> byteToHexStringRange: 00
16:16:39.034 -> byteToHexStringRange: 00
16:16:39.034 -> byteToHexStringRange: 00
16:16:39.034 -> byteToHexStringRange: 00
16:16:39.034 -> byteToHexStringRange: 00
16:16:39.034 -> byteToHexStringRange: 00
16:16:39.067 -> byteToHexStringRange: 00
16:16:42.917 -> WEB: Connection received - /config
16:16:42.917 -> SPI: mounted file system
16:16:42.917 -> SPI: reading config file
16:16:42.917 -> SPI: opened config file
16:16:42.917 -> JSO: {"hostname":"TTW","ntpserver":"EUROPE.POOL.NTP.ORG","ntpenabled":"1","deenabled":"1"}
16:16:42.951 -> JSO: parsed json
16:16:42.951 -> WEB: Number of args received: 0
16:16:42.951 -> 
16:16:42.951 -> SPI: save config.json...
16:16:42.951 -> {"hostname":"","ntpserver":"","ntpenabled":"0","deenabled":"0"}

OK, also, aus welchen Gründen auch immer macht der trotzdem ein save auch wen du das angeblich gar nicht gedrückt hast. Oder zumindest wird irgendwo der type=1 gesetzt obwohl gar kein HTML POST kam. Der speichert also leere Sachen ab in dem Fall. Daher ist es beim erneuten /config auch weg.

OK, das ist nachvollziehbar. Das muss dann in oder wegen der Zeile if (json.containsKey("deenabled")) de_enabled = json["deenabled"]; passieren in der Routine loadConfig() , siehe Post #52. Wenn ich die nämlich weg lasse, passiert das nicht.

So, das Problem ist gelöst, der Bug ist beseitigt. Das Problem lag in der Plausibilitätsabfrage bei den übergebenen Argumenten und war auch im ursprünglichen Code vorhanden.

    // validate values before saving
    bool validconf = true;
    if (validconf)
    {...}

kann nicht funktionieren, weil dann validconf immer true ist und die if(validconf) immer abgearbeitet wird. Daher wurde jede Konfiguration gespeichert, ganz gleich, ob sie valide war oder nicht. Warum das im Ursprungscode funktioniert hat, ist mir unklar. Ich habe das jetzt durch folgende Zeilen ersetzt:

    // validate values before saving
    bool validconf;
    if (httpServer.args() == 4) {
      validconf = true;
    }
    else {
      validconf = false;
    }
    if (validconf)
    {...}

Jetzt wird nur noch abgespeichert, wenn genau 4 Argumente übergeben wurden.

Das war mal vorgesehen aber wird noch nicht benutzt, daher ist die Eingabeprüfung nicht drin, die fehlt schlicht noch, einfacher Grund :wink: Da muss noch sowas rein wie Prüfen ob der Hostname valide ist (RFC -952) etc. etc. etc. . Ist ist aber falsch, dass das der Grund für dein Problem war was du da eingebaut hast, das hat ja mit dem Verhalten nichts zu tun. Die Aktivierung der Eingabeprüfung sorgt nur dafür, dass bei Fehlerhaften eingabewerten und gewolltem Speichern eben nichts passiert, das war ja bei dir nicht der Fall.