Is there an easy way to transform in- and outputs from the serial to a webserver?

Hey guys,
now that my code for controlling a valve works via the serial monitor, I want to control it via a webserver. I am using the Uno R4 Wifi. It shall work as an access point that can be accessed via its ip adress. When accessed, the user can make inputs like setting the pressure and after pressing the start button, the results are shown on the webserver. However, I find it extremely difficult to do this task. Is there a way to transform the serial monitor to the webserver interface? The tutorial about the led that can be switched on and off is helpful to get started, but at some point I dont know what to do anymore. Below you can see my code (which is in german, but maybe you can still help me)

// DAC-Pin-Definition (A0 auf Arduino Due oder einem anderen Board mit DAC)
const int dacPin = A0;
const int istPin = A2;
const int durchflussPin = A4;

// Variablen für Spannung und Steigrate
float zielSpannung = 4.9;  // Maximale Spannung in Volt
float spannungRate = 0.0;    // Steigrate in Volt/Sekunde
unsigned long lastUpdateTime = 0; // Zeitpunkt des letzten Updates in ms

unsigned long startZeit = 0; //Zeitsteuerung für Teil 1 des Versuchs

unsigned long lastMessageTime = 0; // Zeitpunkt der letzten Nachricht
const unsigned long messageInterval = 100; // Intervall in Millisekunden

// Skalierungsfaktoren (hier können Sie die spezifischen Werte eintragen)
const float startIstwertSpannung = 0.935;   // Minimal Istwert-Spannung, ausgegeben vom Ventil
const float voltProBarSoll = 0.773;         // Spannung pro Bar für Sollwert (z. B. 1V = 1 Bar)
const float voltProBarIst = 0.444;          // Spannung pro Bar für Istwert (z. B. 1V = 1 Bar)

// Variablen für Benutzereingaben
float halteDruck = 0.0;
int halteZeit = 0;
float druckSteigrate = 0.0;
float berstDruck = 0.0;


void setup() {
  analogWriteResolution(12); // 12 bit Auflösung (0 - 4095)
  
  Serial.begin(9600);
}

void loop() {

  // Start-Up
  Serial.println("Berstdruckversuch");
  Serial.println("Drücken Sie Enter, um zu starten.");
  while (!Serial.available()); // Warten, bis Benutzer fortfährt
  Serial.read();               // Eingabe leeren

  // Benutzerwerte eingeben
  Serial.println("Geben Sie den Haltedruck in Bar ein:");
  while (!Serial.available());          // Warten auf Eingabe
  halteDruck = Serial.parseFloat();     // Eingabe verarbeiten
  Serial.read();                        // Eingabe-Puffer leeren

  Serial.println("Geben Sie die Haltezeit in Sekunden ein:");
  while (!Serial.available());          // Warten auf Eingabe
  halteZeit = Serial.parseInt();        // Eingabe verarbeiten
  Serial.read();                        // Eingabe-Puffer leeren

  Serial.println("Geben Sie die Druck-Steigrate in bar/s ein:");
  while (!Serial.available());          // Warten auf Eingabe
  druckSteigrate = Serial.parseFloat(); // Eingabe verarbeiten
  Serial.read();                        // Eingabe-Puffer leeren
  
  // Benutzer soll Eingabewerte überprüfen

  Serial.println("Überprüfen Sie die Werte und bestätigen Sie mit Enter.");
  Serial.print("Haltedruck: ");
  Serial.print(halteDruck);
  Serial.println(" bar");
  Serial.print("Haltezeit: ");
  Serial.print(halteZeit);
  Serial.println(" s");
  Serial.print("Steigrate: ");
  Serial.print(druckSteigrate);
  Serial.println(" bar/s");    
  while (!Serial.available());
  Serial.read();

  // Haltedruckphase starten
  Serial.println("Haltephase beginnt...");
  float sollSpannung = halteDruck * voltProBarSoll;
  analogWrite(dacPin, sollSpannung * 4095.0 / 5.0); // Spannung proportional setzen
  startZeit = millis();

  //Ausgabe des Haltedrucks, ermittelt über die Istspannung und den Umrechnungsfaktor
  while (millis() - startZeit < halteZeit * 1000) {  //* 1000 damit Haltezeit als Sekunden gewertet werden und nicht als 
    float istSpannung = analogRead(istPin) * 5.0 / 1023.0;
    float istDruck = istSpannung / voltProBarIst - startIstwertSpannung / voltProBarIst;
    Serial.print("Aktueller Druck: ");
    Serial.print(istDruck);
    Serial.println(" Bar");
    delay(1000); //Ausgabe erfolgt ein mal pro Sekunde
  }
  
  //Information, dass Zeit abgelaufen ist
  Serial.println("Haltezeit abgelaufen.");

  // Entscheidung für Teil 2 oder Wiederholung
  Serial.println("Teil 1 mit (n) wiederholen.");
  Serial.println("Teil 2 starten. Bauteil in Prüfbox legen und mit (y) bestätigen.");
  while (!Serial.available());
  char choice = Serial.read();
  if (choice == 'n' || choice == 'N') {
    Serial.println("Wiederholung von Teil 1...");
    sollSpannung = 0.0;
    analogWrite(dacPin, sollSpannung * 4095.0 / 5.0);
    return; // Zurück zum Anfang des Loops
  }

 	unsigned long currentTime = millis();
	unsigned long lastUpdateTime = currentTime; // Initialisieren für die erste Berechnung
	float istSpannung = analogRead(istPin) * 5.0 / 1023.0;
  spannungRate = druckSteigrate * voltProBarSoll;
	if (spannungRate > 0.0) { // Sicherstellen, dass die Steigrate positiv ist
	  while (sollSpannung < zielSpannung) { // Schleife läuft, bis die Spannung 5V erreicht
	    currentTime = millis(); // Aktuelle Zeit ermitteln
	    unsigned long elapsedTime = currentTime - lastUpdateTime; // Zeitdifferenz berechnen
	    float timeInSeconds = elapsedTime / 1000.0; // Umrechnung in Sekunden
	
	    if (timeInSeconds > 0) { // Wenn Zeit vergangen ist, berechnen
	      // Erhöhe die Spannung proportional zur Steigrate und Zeitdifferenz
	      sollSpannung += spannungRate * timeInSeconds;
       
	      // Begrenzen der Spannung auf die Zielspannung
	      if (sollSpannung >= zielSpannung) {
	        sollSpannung = zielSpannung; // Begrenzung auf 5V
        	Serial.println("Berstdruck > 6 bar.");
	      }
	
	      // Spannung in DAC-Wert umrechnen und ausgeben
	      int dacValue = (int)((sollSpannung / 5.0) * 4095);
	      analogWrite(dacPin, dacValue);

        unsigned long aktuelleZeit = millis(); // Aktuelle Zeit ermitteln
        // Prüfen, ob das Intervall seit der letzten Nachricht abgelaufen ist
        if (aktuelleZeit - lastMessageTime >= messageInterval) {
          lastMessageTime = aktuelleZeit; // Zeitpunkt der letzten Nachricht aktualisieren
	        Serial.print("Aktuelle Spannung: ");
	        Serial.print(sollSpannung);
	        Serial.println(" V");
        }

        float durchflussWert = analogRead(durchflussPin) * 5.0 / 1023.0;
        if (durchflussWert > 0.4) {
          Serial.println("Leckage erkannt, beende Versuch.");
          Serial.print("Finaler Berstdruck: ");
          Serial.println(sollSpannung/voltProBarSoll);
          break;
        }

	      lastUpdateTime = currentTime;
	    }
	  }
	
	  // Nach dem Erreichen von 5V zurücksetzen
	  Serial.println("Druck wird zurückgesetzt.");
	  sollSpannung = 0.0; // Spannung zurücksetzen
	  int dacValue = 0; // DAC auf 0 setzen
	  analogWrite(dacPin, dacValue);
	}
}

Yes, but it will take some work. You can achieve this with the Arduino R4 by setting it up as a server. This approach will be much easier than trying to capture the output from the serial monitor.

There are plenty of examples available of Arduinos functioning as servers. You’ll need to:

  1. Add the necessary code to set up the server functionality.
  2. Modify your existing code to replace or supplement the relevant Serial.print statements to output to the server.

It's certainly possible to present a web page interface at an IP address. But "as an access point" implies that you would set the WiFi on your phone/tablet/laptop to pick its SSID name from a list among other APs -- instead of your usual one -- and doing so would automatically pop up the page. This would work like using a hotel's WiFi, where a page will prompt you for your name and room number. This is known as a "captive portal": when you first connect, no matter where you try to go, you get that page. You don't need to know an IP address.

ESP32 and ESP8266 boards have a class required for captive portals: DNSServer. But despite using an ESP32 for WiFi and Bluetooth, you don't program it directly; the R4 board does not have that class. Could you port one of those over? Eventually maybe.

The captive portal behavior is a completely separate add-on to the web page interface. So if you don't need it, your main question: is there an easy way to transform the user interface? No. But it's not that hard either.

What you've got: after setup, the UI runs in loop

  • ask a series of questions
    • print the prompt
    • wait for an answer to be available
    • use parseFloat or parseInt to read a number
  • having gotten all the numbers
    • print the values read
    • wait for Enter to continue. (No way to cancel though.)
  • apply one of those numbers, and then over another number of seconds
    • every second
      • print the current value of something, presumably as it changes
  • prompt for 'N' to go back or anything else to continue
  • in a tight loop, every millisecond
    • apply a slightly changed value
    • every 100 milliseconds
      • print the current value
    • stop if an error is detected
    • or until the value reaches the limit

This is actually kinda tricky to do with a web interface.

  • The first part is easy: instead of prompting for each value separately, present a <form> to get all the values at the same time
  • After submitting the form, validate the values (not blank, valid numbers, etc) and display those values again for confirmation
  • Where it gets tricky is when you want to update from a live value every 1000 millis, and then every 100 millis on the web page; while the R4 is doing actual work
    • especially if the work is happening every millisecond, because that doesn't leave a lot of time to respond to an HTTP request
    • looks like the code supports making changes less frequently
  • You could try having the web page do a fetch with JavaScript as a kind of long poll, and the R4 would check server.available on its own schedule to reply when appropriate, every 100 or 1000 millis. That would take X-number of millis to do; see if that's acceptable
    • The reply would have one or more of
      • current value
      • overage
      • error
      • completion

Does that make sense to you? Do you know HTML and JavaScript? HTTP? That totals to three or four different pages and two API endpoints.

1 Like

Hey kenb4, thank you for the detailed answer. Since I am not very familiar with coding, communication nor networks, I used terms that I picked up somewhere on my road. For clarity, my goal is to do something similar to the example sketch "AP_SimpleWebServer" that comes with the R4 Wifi. In other words, connect my laptop with the Wifi that is sent by the Arduino, type in the IP adress in my browser, give some input parameters for the experiment, start the experiment and get the results shown on that "website". It is not necessary to automatically open a page like it happens with Hotel's WiFi.

Regarding your second paragraph, I have no idea what you are talking about :smiley: . Once again, I just started with all this. Should I send I you some snippets of my code?

Yes, I already managed to control the first part of the experiment. You are absolutely right that I am struggling to continue the test after part one is finished. Unfortunately, HTML and JavaSkript are as unfamiliar to me as everything else. I've come this far but I feels like I have reached my limits now. Do you have a good source to learn more about APs, Webservers via an Arduino and HTML? It seems like there are not many tutorials online that explain the basics since this is a more advanced topic (I guess :smiley: ).

Best regards

So here is my complete code. Unfortunately, I got lost completely during coding which resulted in losing my most current version. However, this is where I am at right now:

#include "WiFiS3.h"
#include "arduino_secrets.h"

// Netzwerkkonfiguration
char ssid[] = SECRET_SSID;
char pass[] = SECRET_PASS;

// Pin-Konfiguration
const int dacPin = A0;       // DAC-Pin zur Drucksteuerung
const int istwertPin = A2;   // Analog-Pin für den Istwert (Sensor)

// Variablen
float haltedruck = 0.0;      // Haltedruck in bar
int haltezeit = 0;           // Haltezeit in Sekunden
float istwertSpannung = 0.0; // Aktuell gemessene Istwert-Spannung
bool notfall = false;        // Notfallstatus

WiFiServer server(80);

// Werte für Druck/Spannung-Konvertierung (Beispiel: 0-6 bar entspricht 0-5 V)
const float maxDruck = 6.1;  // Maximaldruck in bar
const float maxSpannung = 5.0; // Maximalspannung in Volt

void setup() {
  Serial.begin(9600);
  pinMode(dacPin, OUTPUT);
  pinMode(istwertPin, INPUT);

  // WLAN Access Point starten
  Serial.println("Erstelle Access Point...");
  if (WiFi.beginAP(ssid, pass) != WL_AP_LISTENING) {
    Serial.println("Access Point konnte nicht erstellt werden.");
    while (true);
  }
  server.begin();
  Serial.println("Access Point erstellt. Verbindung mit:");
  Serial.print("IP-Adresse: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  if (notfall) {
    analogWrite(dacPin, 0); // Druck auf 0 setzen
    Serial.println("Notfall ausgelöst. Versuch gestoppt.");
    while (true);           // Programm anhalten
  }

  WiFiClient client = server.available(); // Auf Client-Anfragen warten
  if (client) {
    Serial.println("Neuer Client verbunden.");
    handleClient(client);
  }
}

// Funktion zur Verarbeitung der Client-Anfragen
void handleClient(WiFiClient &client) {
  String request = "";
  while (client.connected() && client.available()) {
    char c = client.read();
    request += c;
  }

  // Notfall-Button gedrückt
  if (request.indexOf("/emergency") != -1) {
    notfall = true;
    client.println("HTTP/1.1 200 OK");
    client.println("Content-type:text/html");
    client.println();
    client.println("<h1>Notfall aktiviert. Versuch gestoppt.</h1>");
    client.println();
    client.stop();
    return;
  }

  // Druck und Haltezeit setzen
  if (request.indexOf("/set") != -1) {
    parseInputs(request);
    client.println("HTTP/1.1 200 OK");
    client.println("Content-type:text/html");
    client.println();
    client.println("<h1>Haltedruck und Haltezeit gesetzt.</h1>");
    client.println("<p>Haltedruck: " + String(haltedruck) + " bar</p>");
    client.println("<p>Haltezeit: " + String(haltezeit) + " Sekunden</p>");
    client.println("<a href=\"/start\">Versuch starten</a>");
    client.println();
    client.stop();
    return;
  }

  // Versuch starten
  if (request.indexOf("/start") != -1) {
    startTest(client);
    return;
  }

  // Standard-Seite anzeigen
  sendHomePage(client);
}

// Homepage senden
void sendHomePage(WiFiClient &client) {
  client.println("HTTP/1.1 200 OK");
  client.println("Content-type:text/html");
  client.println();
  client.println("<h1>Erster Versuchsteil</h1>");
  client.println("<form action=\"/set\">");
  client.println("<label for=\"druck\">Haltedruck (bar):</label>");
  client.println("<input type=\"number\" id=\"druck\" name=\"druck\" step=\"0.1\" min=\"0\" max=\"6\"><br><br>");
  client.println("<label for=\"zeit\">Haltezeit (Sekunden):</label>");
  client.println("<input type=\"number\" id=\"zeit\" name=\"zeit\" min=\"1\"><br><br>");
  client.println("<input type=\"submit\" value=\"Einstellungen speichern\">");
  client.println("</form>");
  client.println("<h3>Notfall</h3>");
  client.println("<a href=\"/emergency\">Notfall stoppen</a>");
  client.println();
  client.stop();
}

// Eingaben aus der Anfrage extrahieren
void parseInputs(String request) {
  if (request.indexOf("druck=") != -1) {
    String druckStr = getValue(request, "druck");
    haltedruck = druckStr.toFloat();
  }
  if (request.indexOf("zeit=") != -1) {
    String zeitStr = getValue(request, "zeit");
    haltezeit = zeitStr.toInt();
  }
}

// Ersten Versuchsteil starten
void startTest(WiFiClient &client) {
  client.println("HTTP/1.1 200 OK");
  client.println("Content-type:text/html");
  client.println();
  client.println("<h1>Test läuft...</h1>");
  client.println("<p>Haltedruck: " + String(haltedruck) + " bar</p>");
  client.println("<p>Haltezeit: " + String(haltezeit) + " Sekunden</p>");
  client.println();

  // Sollspannung für Haltedruck berechnen
  float sollSpannung = (haltedruck / maxDruck) * maxSpannung;
  int dacWert = map(sollSpannung * 1000, 0, 5000, 0, 255); // DAC-Wert berechnen
  analogWrite(dacPin, dacWert);

  delay(haltezeit * 1000); // Haltezeit abwarten

  // Druck zurücksetzen
  analogWrite(dacPin, 0);

  // Versuch beendet
  client.println("<h1>Test beendet</h1>");
  client.println("<p>Haltezeit abgelaufen. Druck wurde zurückgesetzt.</p>");
  client.println("<a href=\"/\">Zurück zur Startseite</a>");
  client.println();
  client.stop();
}

// Hilfsfunktion zum Extrahieren von Werten aus GET-Anfragen
String getValue(String data, String key) {
  int startIndex = data.indexOf(key + "=");
  if (startIndex == -1) return "";
  int endIndex = data.indexOf("&", startIndex);
  if (endIndex == -1) endIndex = data.length();
  return data.substring(startIndex + key.length() + 1, endIndex);
}

The good part of hosting the site as an AP is that it is (optionally) password-protected, and it is isolated. But without the captive portal you still need to know/type the arbitrary IP address. And when connected to SECRET_SSID, your phone/tablet/laptop is not connected to the internet (and other devices on the same WiFi/router) to do anything else.

In contrast, if the R4 joins your normal WiFi network, it will be assigned an IP (10.x.x.x or 192.168.x.x), and as long as it prints that out -- or displays that as a scrolling banner on the built-in LED matrix, etc -- you would use that to connect to it; that's fewer steps. Either way, that's entirely separate from the interesting part.

You've made decent progress by yourself, and added an emergency stop. Obviously you want that to take effect as soon as possible. An HTTP response can easily take 200ms or more. I made some refinements and added stuff.

#include <WiFi.h>
#include "arduino_secrets.h"

// Netzwerkkonfiguration
char ssid[] = SECRET_SSID;
char pass[] = SECRET_PASS;

// Pin-Konfiguration
const int dacPin = A0;       // DAC-Pin zur Drucksteuerung
const int istwertPin = A2;   // Analog-Pin für den Istwert (Sensor)

// Variablen
float haltedruck = 0.0;      // Haltedruck in bar
int haltezeit = 0;           // Haltezeit in Sekunden
float istwertSpannung = 0.0; // Aktuell gemessene Istwert-Spannung
bool notfall = false;        // Notfallstatus
unsigned long startMillis;   // non-zero when test is running

WiFiServer server(80);

// Werte für Druck/Spannung-Konvertierung (Beispiel: 0-6 bar entspricht 0-5 V)
const float maxDruck = 6.1;  // Maximaldruck in bar
const float maxSpannung = 5.0; // Maximalspannung in Volt

void setup() {
  Serial.begin(115200);
  pinMode(dacPin, OUTPUT);
  pinMode(istwertPin, INPUT);

  // WLAN Access Point starten
  Serial.println("Erstelle Access Point...");
  if (WiFi.beginAP(ssid, pass) != WL_AP_LISTENING) {
    Serial.println("Access Point konnte nicht erstellt werden.");
    while (true);
  }
  server.begin();
  Serial.println("Access Point erstellt. Verbindung mit:");
  Serial.print("IP-Adresse: ");
  Serial.println(WiFi.localIP());
}

unsigned long acquireMillis;

void loop() {
  if (notfall) {
    analogWrite(dacPin, 0); // Druck auf 0 setzen
    Serial.println("Notfall ausgelöst. Versuch gestoppt.");
    while (true);           // Programm anhalten
  }

  testLoop();

  WiFiClient client = server.available(); // Auf Client-Anfragen warten
  if (client) {
    acquireMillis = millis();
    Serial.println("Neuer Client verbunden.");
    handleClient(client);
  }
}

void doneWith(WiFiClient &client, const char *p) {
  client.stop();
  Serial.print(p);
  Serial.print(", client elapsed: ");
  Serial.println(millis() - acquireMillis);
}

void html200(WiFiClient &client) {
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html; charset=utf-8");
  client.println();
  // so it doesn't look tiny on a phone
  client.println(R"~(<head><meta name="viewport" content="width=device-width, initial-scale=1.0"></head>)~");
  // HTML body follows
  // Response does not require a blank line at the end
}

void json200(WiFiClient &client, float value, bool done) {
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: application/json");
  client.println();
  client.print(R"({"value":)");
  client.print(value);
  client.print(R"(,"done":)");
  client.print(done ? "true" : "false");
  client.println("}");
}

// Funktion zur Verarbeitung der Client-Anfragen
void handleClient(WiFiClient &client) {
  String request;
  request.reserve(400);
  while (client.connected() && client.available()) {
    char c = client.read();
    request += c;
  }
  // just to see if it is exceeding the reserved size
  Serial.print("request length: ");
  Serial.println(request.length());

  // Remove HTTP method from start of request-line. This can be used
  // in the future in case the same path allows both GET and POST,
  // but for now it's to move the URL-path to the start of the string.
  if (request.startsWith("GET ")) {
    request.remove(0, 4);  // remove shuffles bytes in place
    request.remove(request.indexOf('\n'));  // only need URL-path with params
  } else if (request.startsWith("POST ")) {
    request.remove(0, 5);
  } else {
    Serial.println("405");
    Serial.println(request);
    client.println("HTTP/1.1 405 Method Not Allowed");
    client.println();
    doneWith(client, "405");
    return;
  }

  if (request.startsWith("/status")) {
    float value = millis(); // placeholder
    json200(client, value, startMillis == 0);
    doneWith(client, "status");
    return;
  }

  // Notfall-Button gedrückt
  // Instead of indexOf, which will search all the headers for the string,
  // the path is now at the beginning of the string
  if (request.startsWith("/emergency")) {
    notfall = true;
    html200(client);
    client.println("<h1>Notfall aktiviert. Versuch gestoppt.</h1>");
    doneWith(client, "emergency");
    return;
  }

  // Druck und Haltezeit setzen
  if (request.startsWith("/set")) {
    if (!parseInputs(request)) {
      sendHomePage(client);
      return;
    }
    html200(client);
    client.println("<h1>Haltedruck und Haltezeit gesetzt.</h1>");
    // Don't compose string just to print it; print it in pieces.
    // Line breaks in HTML source are coalesced into a single space (unless in <pre> block)
    client.println("<p>Haltedruck:"); client.println(haltedruck); client.println("bar</p>");
    client.println("<p>Haltezeit:"); client.println(haltezeit); client.println("Sekunden</p>");
    // Use raw string to make it easier to use double-quotes in HTML
    client.println(R"~(<a href="/start">Versuch starten</a>)~");
    doneWith(client, "set");
    return;
  }

  // Versuch starten
  if (request.startsWith("/start") || startMillis) {
    startTest(client);
    return;
  }

  // Standard-Seite anzeigen
  sendHomePage(client);
}

// Homepage senden
void sendHomePage(WiFiClient &client) {
  html200(client);
  client.println("<h1>Erster Versuchsteil</h1>");
  client.println("<form action=\"/set\">");
  client.println("<label for=\"druck\">Haltedruck (bar):</label>");
  client.println("<input type=\"number\" id=\"druck\" name=\"druck\" step=\"0.1\" min=\"0\" max=\"6\"><br><br>");
  client.println("<label for=\"zeit\">Haltezeit (Sekunden):</label>");
  client.println("<input type=\"number\" id=\"zeit\" name=\"zeit\" min=\"1\"><br><br>");
  client.println("<input type=\"submit\" value=\"Einstellungen speichern\">");
  client.println("</form>");
  client.println("<h3>Notfall</h3>");
  client.println("<a href=\"/emergency\">Notfall stoppen</a>");
  doneWith(client, "home");
}

// Eingaben aus der Anfrage extrahieren
bool parseInputs(String request) {
  // getValue already checks for key, and returns empty string if not found
  // toFloat and toInt return zero if strings are not valid numbers
  float druck = getValue(request, "druck").toFloat();
  int zeit = getValue(request, "zeit").toInt();

  if (druck && zeit) {
    haltedruck = druck;
    haltezeit = zeit;
    return true;
  }
  return false;
}

// Ersten Versuchsteil starten
void startTest(WiFiClient &client) {
  html200(client);
  client.println("<h1>Test läuft...</h1>");
  client.println("<p>Haltedruck:"); client.println(haltedruck); client.println("bar</p>");
  client.println("<p>Haltezeit:"); client.println(haltezeit); client.println("Sekunden</p>");
  client.println(R"(<p>value: <span id="value"></span></p>)");

  if (!startMillis) {
    // Sollspannung für Haltedruck berechnen
    float sollSpannung = (haltedruck / maxDruck) * maxSpannung;
    int dacWert = map(sollSpannung * 1000, 0, 5000, 0, 255); // DAC-Wert berechnen
    analogWrite(dacPin, dacWert);
    startMillis = millis() | 1;  // in case it's the 49th day and clock has rolled over to exactly zero
  }

  client.println(R"~(
<script>
  async function pollStatus() {
    const last = Date.now();
    const stop = await fetch("/status").then(response => {
      if (response.ok) {
        return response.json();
      }
      throw response;
    }).then(status => {
      document.getElementById("value").innerText = status.value;
      if (status.done) {
        document.getElementById("done").style.display = "block";
        return true;
      }
    }).catch(console.error);
    if (!stop) {
      setTimeout(pollStatus, Math.max(0, 500 - (Date.now() - last)));
    }
  }
  pollStatus();
</script>
)~");

  // Versuch beendet (initially hidden)
  client.println(R"~(
<div id="done" style="display: none">
  <h1>Test beendet</h1>
  <p>Haltezeit abgelaufen. Druck wurde zurückgesetzt.</p>
  <a href="/">Zurück zur Startseite</a>
</div>
)~");
  doneWith(client, "start");
}

void testLoop() {
  if (!startMillis) {
    return;
  }
  unsigned long elapsed = millis() - startMillis;
  if (elapsed >= haltezeit * 1000) {
    Serial.print("complete after ");
    Serial.println(elapsed);
    // Druck zurücksetzen
    analogWrite(dacPin, 0);
    startMillis = 0;
  }
}

// Hilfsfunktion zum Extrahieren von Werten aus GET-Anfragen
String getValue(String data, String key) {
  int startIndex = data.indexOf(key + "=");
  if (startIndex == -1) return "";
  int endIndex = data.indexOf("&", startIndex);
  if (endIndex == -1) endIndex = data.length();
  return data.substring(startIndex + key.length() + 1, endIndex);
}

The key is removing that long delay, so the server can respond.

Hey kenb4, thank you very much for your help, I really appreciate the work you have done. I use your code and expand it by the stuff that is required. :+1:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.