How to get data from a (HTML) form?

Here my test-code.. I want to use this in a "news-scroller". I have a page with all inputfields.
How can i fill those fields and read them from my code, to use them, for instance the variable "brightness"
I found a few "solutions" but i couldn't get even one to work.. (to be honest, i didn't even know what i was doing :wink:

#include <SPI.h>
#include <SPIFFS.h>
#include <WiFi.h>
#include <WiFiSettings.h>
#include <EEPROM.h>
#include <Wire.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
#include <esp_sntp.h>

#define PIN_SW3 D6  //Mode

bool AP_or_Client;
uint16_t brightness = 50;
uint32_t PreviousMiliSecondCC = 0;
uint32_t CurrentMiliSecond;
const long intervalCC = 1000;

const char* host = "esp32";
const char* loginIndex =
  "<form name='loginForm' method='get'>"
  "<table width='40%' bgcolor='BBBBEE' align='center'>"
  "<tr>"
  "<td colspan=2>"
  "<center><font size=4><b>News Scroller data</b></font></center>"
  "<br>"
  "</td>"
  "</tr>"
  "<tr>"
  "<td>NTP Server:</td>"
  "<td><input type='text' size=20 name='NTPSERVER' value='de.pool.ntp.org'><br></td>"
  "</tr>"
  "<tr>"
  "<td>RSS Feed URL:</td>"
  "<td><input type='text' size=25 name='RSSFEED' value='https://www.tagesschau.de/infoxxxxxx'><br></td>"
  "</tr>"
  "<tr>"
  "<td>WLAN SSID:</td>"
  "<td><input type='text' size= 20 name='WLANSSID' value='MyWiFi'><br></td>"
  "</tr>"
  "<tr>"
  "<td>WLAN PASS:</td>"
  "<td><input type='text' size=20 name='WLANPASS' value='MyPassword'><br></td>"
  "</tr>"
  "<tr>"
  "<td>Headlines:</td>"
  "<td><input type='number' size=10 min=1 max=10 name='HEADLINES' value=10><br></td>"
  "</tr>"
  "<tr>"
  "<td>Brightness:</td>"
  "<td><input type='number' size=10 min=5 max=150 name='BRIGHTNESS' value=brightness ><br></td>"
  "</tr>"
  "<tr>"
  "<td> <input type='submit' onclick='check(this.form)' value='Submit' margin:auto></td>"
  "</tr>"
  "</table>"
  "</form>"

  "<script>"
  "function check(form)"
  "{"
  "if(form.WLANPASS.value=='techgraphix')"
  "{"
  ""
  "}"
  "else"
  "{"
  " alert('Error Password or Username')/*displays error message*/"
  "}"
  "}"
  "</script>";

WebServer server(80);

void setup() {
  Serial.begin(115200);
  pinMode(PIN_SW3, INPUT_PULLUP);
  SPIFFS.begin(true);
  WiFiSettings.onPortal = []() {};

  AP_or_Client = digitalRead(PIN_SW3);
  if (AP_or_Client) {
    WiFiSettings.connect();
  } else {
    WiFiSettings.portal();
  }
  delay(500);

  sntp_init();

  if (!MDNS.begin(host)) {
    Serial.println("Error setting up MDNS responder!");
    while (1) { delay(1000); }
  }

  Serial.println("mDNS responder started");
  server.on("/", HTTP_GET, []() {
    server.sendHeader("Connection", "close");
    server.send(200, "text/html", loginIndex);
  });
  server.begin();
}

void loop() {
  CurrentMiliSecond = millis();
  if (CurrentMiliSecond - PreviousMiliSecondCC >= intervalCC) {
    PreviousMiliSecondCC = CurrentMiliSecond;
    server.handleClient();

    Serial.println(brightness);
  }
}

Very unclear what you want to do

If your code running in the microcontroller is creating the website your code knows exactly what the content is. Why would you need to "read back" from the HTML???

If the HTML is running on a different device; How will you create the "browser-functionality" to read in the data into your microcontroller from this other device?

Ok, let me try to explain: this code is just a part of a much larger code and when it works i can place it in that larger code.
It is a program that displays certain parts from a XML file on a led-array.
Depending on where (country) and network (SSID, password etc) and options (like wanted brightness) you have to fill in this form.
After connecting it to the network, you can acces this form through the IP-address.
That works fine.
But what i want is that the field are filled with the global variables from that program and that, when changed, they are written back to that variables.

Find some Javascript or Active Server Pages tutorials. You can "scrape" data from any source document on the internet and place it in a variable on a web page. You might need an API for getting data off advanced web sites (for example, Google maps).

Not possible to change source documents, but variables can be stored in an active "database" (MySQL for example - and databases need to be "turned on" which have a recurring fee).

I think you're just looking for the form handling code??

So in your HTML, you'd have something like

                <form action='/config' method='GET'>\
                    <input type = 'button' id = 'increase' value= '+'>\
                    <input type = 'button' id = 'decrease' value= '-'>\
                    <input type='submit' value='Change Setpoint'/>\
                </form>\

I don't think your form specifies a handler, but I didn't look that closely.

And a handler function defined in your server setup

	server.on("/config", HTTP_GET, [](AsyncWebServerRequest *request)
	{
		String inputMessage;
		// GET inputString value on <ESP_IP>/get?inputString=<inputMessage>
		int params = request->params();
		for (int i = 0; i < params; i++)
		{
			AsyncWebParameter *p = request->getParam(i);
			Serial.printf("GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
		}

		request->send(200, "text/plain", "Hello, GET: " + inputMessage);
	});

Syntax may be different because my example isn't using the exact server you are and I don't know what their relationship is, but the process is generally the same, so hopefully this shows you what to look for in the documentation.

so in summary:

  • you want your server to generate a web form with pre-populated values
  • when a client connects you present that page
  • if the user on the client modifies some of the fields and submit them back, you want to read the changes and take that into account

That does not feel complicated. On an ESP32 using the AsyncWebServer library for example, it's easily done (with a small bonus using AJAX to populate the fields upon loading the web page so that the webpage could be static and stored in flash)

Which Arduino are you using?

@JML
Yes, that's about what i want: show a pre-populated form and, if changed, use that data again.
i'm using a ESP32C3 (Xiao Seeed)

I don't have that specific board, but assuming the Xiao Seeed ESP32C3 is fine with the ESPAsyncWebServer then there are tons of examples out there to do that.

First, you need to retrieve this information from your web server running on the ESP32, so you should set up an endpoint where the microcontroller will respond with the necessary data, formatted in JSON or another easily parsable encoding for the browser. Once these data are obtained, the script running in the browser will appropriately populate the input fields. After the user has modified/entered the other form data, the ESP32 will also need to handle receiving back the data from the form.

 server.on("/", HTTP_GET, []() {
    // Serve the main HTML page
    server.send(200, "text/html", loginIndex);
  });

  server.on("/getData", HTTP_GET, []() {
    // Send the global variables to the client
    server.send(200, "application/JSON", data);
  });

   server.on("/saveData", HTTP_GET, []() {
    // Save all datas
    server.send(200, "text/html", "OK");
  });

This techique is known as AJAX which is a technique used in web development to enable asynchronous communication between a web page and a server without requiring a full page reload. It allows web applications to send and receive data in the background, improving user experience by making web pages more dynamic and responsive.

Despite the name, AJAX is not limited to XML; it often uses JSON, which is more lightweight and easier to parse. AJAX is typically implemented using the XMLHttpRequest object or the more modern fetch API.

ESPAsyncWebserver has simple template processing, so current values could be inserted without resorting to JavaScript (or :shaking_face: XML), while having a single blob of immutable content

static const char *htmlContent PROGMEM = R"(
<!DOCTYPE html>
<html>
<body>
    <h1>Hello, %USER%</h1>
</body>
</html>
)";

We've now gone full circle: all the cool kids do server-side rendering.

true but costly in processing and memory if you have a large web page.

... and additionally you have to reload the full webpage to show values updated.

If you already know how to do it in JavaScript... or if you want to learn -- and in that case, don't follow something from the aughts, use fetch -- fine.

But ESPAsyncWebserver also supports POST body parameters, so with plain HTML and a tiny bit of server code which you need anyway, no JavaScript, no JSON: you're done.

Here is something hacked together quickly - give it a try

Create a new sketch, add this into the .ino (update ssid an password to match your local environment).

#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include "html.h"  // our webpage

const char* ssid = "xxx";
const char* password = "xxx";
AsyncWebServer server(80);

long values[4] = {0, 0, 0, 0};

void setup() {
  Serial.begin(115200);

  Serial.print("Connecting to WiFi...");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.write(".");
  }
  Serial.println("\tConnected");

  // Root page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(200, "text/html", rootPage);
  });

  // Send values
  server.on("/request", HTTP_GET, [](AsyncWebServerRequest *request) {
    String response = "";
    for (int i = 0; i < 4; i++) {
      response += String(values[i]);
      if (i < 3) response += ",";
    }
    request->send(200, "text/plain", response);
  });

  // Update values
  server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request) {
    if (request->hasParam("v")) {
      String newValues = request->getParam("v")->value();
      int startIdx = 0;
      for (int i = 0; i < 4; i++) {
        int commaIdx = newValues.indexOf(',', startIdx);
        String valStr = (commaIdx == -1) ? newValues.substring(startIdx) : newValues.substring(startIdx, commaIdx);
        values[i] = valStr.toInt();
        startIdx = commaIdx + 1;
      }
      Serial.print("New values: ");
      for (int i = 0; i < 4; i++) {
        Serial.print(values[i]);
        if (i < 3) Serial.print(", ");
      }
      Serial.println();
    }
    request->send(200, "text/plain", "OK");
  });

  server.begin();

  Serial.print("Open your browser at http://");
  Serial.println(WiFi.localIP());
}

void loop() {}

Now in the IDE create a new tab in your project called html.h (exactly this name) and paste in this tab the following code

#ifndef HTML_H
#define HTML_H

const char* rootPage = R"rawliteral(
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Demo</title>
    <script>
      // Function to fetch the initial values
      function loadValues() {
        var request = new XMLHttpRequest();
        request.open('GET', '/request', true);
        request.onload = function() {
          if (request.status == 200) {
            var values = request.responseText.split(',');
            for (var i = 0; i < 4; i++) {
              document.getElementById('value' + i).value = values[i];
            }
          }
        };
        request.send();
      }

      // Function to update the values
      function update() {
        var newValues = '';
        for (var i = 0; i < 4; i++) {
          if (i > 0) newValues += ',';
          newValues += document.getElementById('value' + i).value;
        }
        var request = new XMLHttpRequest();
        request.open('GET', '/update?v=' + encodeURIComponent(newValues), true);
        document.getElementById('status').innerHTML = 'Sending: ' + newValues;
        request.onload = function() {
          if (request.status == 200) {
            document.getElementById('status').innerHTML = ''; // clear
          } else {
            document.getElementById('status').innerHTML = 'Error during update. Status: ' + request.status;
          }
        };
        request.send();
      }

      // Call the function loadValues when the page loads
      window.onload = loadValues;
    </script>
  </head>
  <body>
    <h1>Parameter Management</h1>
    <label for="value0">Value 1:</label>
    <input type="number" id="value0" value="?" />
    <br />
    <label for="value1">Value 2:</label>
    <input type="number" id="value1" value="?" />
    <br />
    <label for="value2">Value 3:</label>
    <input type="number" id="value2" value="?" />
    <br />
    <label for="value3">Value 4:</label>
    <input type="number" id="value3" value="?" />
    <br />
    <button onclick="update()">Update</button>
    <p id="status" style="font-size: 0.8em; color: #555;"></p>
  </body>
</html>
)rawliteral";

#endif

Now compile and upload the code to your ESP32. You should see something like

Connecting to WiFi.....	Connected
Open your browser at http://10.116.1.22

(the IP address will be different)

launch your favorite web browser and navigate to the given URL (http://10.116.1.22 in my case)

you should see something like that

Now change some values , for example

and click the Update button and have a look at the Serial monitor, you should see

New values: 10, -20, 30, 42

➜ the values have been received on the server side and if you connect from a different browser or just refresh the page, you'll see those new values. (the code does not store them as preferences so they will be lost if you reboot the ESP32 - see the Preferences library to handle that)

Does that make sense?

(I hardcoded lots of things and there is no error management, so this code needs improvement to be more generic)

I believe that when approaching a new topic, it’s best to always try to learn how to manage everything and understand how things work, so you can expand your project independently. What you’re suggesting is certainly feasible, but it’s specific to using that particular library. However, if the user learns to adopt AJAX techniques, they will be able to use them with any platform, not just the ESP32 + ESPAsyncWebserver.

After making some minor changes: (As it is a ESP32 WiFi.h is included in ESPAsyncWebSrv.h, resulting in double declarations and ESPAsyncWebServer.h had some other issues)
//#include <WiFi.h> #include "ESPAsyncWebSrv.h"
i got this working.
I have to find information on how this works and i also want to find more about that AJAX (reference, tutorials etc) till now i couldn't find something i understand..
Copy a code is one, understand and write my own code is the next step..

How familiar are you with HTTP, HTML (DOM) and JavaScript ?


The C++ code sets up an ESP32 web server using the AsyncWebServer library to manage four numerical values. When a client accesses the root URL (/), the server responds with an HTML page that is stored in flash memory.

The server knows how to handle a /request endpoint which returns the current values as a comma-separated string. The client sends GET /request and the server just answers with the 4 values something like 10,20,30,42

And the /update endpoint which updates the values in memory based on a received query parameter ➜ the client will send something like GET /update?v=12,34,56,78 and the server code will make sure there is a v parameter in the GET request and if so extract the 4 values that are in the coma separated list 12,34,56,78 and populate the array accordingly.

The HTML page contains four input fields where users can modify the values. When the page loads, it sends an AJAX request to /request to fetch the current values and populate the fields by modifying the DOM.

When the "Update" button is clicked, another AJAX request is sent to /update with the modified values as a URL-encoded string. The server processes this request, updates the values, and confirms the update by responding with "OK".

Be careful about the libraries installed in your IDE.

The development of the original library GitHub - me-no-dev/ESPAsyncWebServer: Async Web Server for ESP8266 and ESP32 had been interrupted due to lack of time and some users created forks adapting the repository structure so that it could be included in the Arduino IDE library manager.

What you included is one of these forks (check the name of file included, which is different in the original library), but as far as I know, are not maintained consistently and above all they are not maintained by the original author/team of the library.

In recent months, the ESPAsyncWebServer project has been "officially" revived, but it has been moved to the repositories that refer to the Github @ESP32Async user as stated also in the Github webpage linked before.

My advice is to remove everything you had previously installed and do a clean installation from the "official" repositories:

In addition, with this third-party library, you can run ESPAsyncWebServer also with the Raspberry MCUs:

a bit, a little bit, none.... i made a few very simple webpages in the far past, but that's it. (at the age of 70, understanding is a thing, i notice ;() Anyway... sometimes i see the light..
ESPAsyncWebServer.h generates loads of errors and doesn't compile: One of the included files is ESPAsyncWebSrv.h and multiple times i see WiFi.h is included if ESP32, which results in duplication errors.. and a few others.
I'll make a backup first and see if i can clean install those and where it leads to.

As @cotestatnt said, the library is undergoing some changes and there are reported compatibility issues between the ESPAsyncWebServer library and ESP32 platform version 3.

I used version 2 for my test.