Web server

Makes the board a small web server. What is in its folder is a site: a browser that asks for the board’s address is shown index.html, and any other file by its name. A second page, at /files, lists what is there, takes new files - chosen, or dropped onto it - and deletes them. Routes are addresses it answers with code of your own, which is what a page asks for live values and sends changes to. On a screen it is a card that says where to find it. A design has one. Arduino export, on an ESP32.

Group: Ready-made · Default size: 220 × 86 · Style parts: Main, Title, Address, Status line

A dropped Web server card: Files, the address http://192.168.1.50/files, and 3 files, 812 KB free.
A Web server card as it is dropped. The address and the figures are examples until the board has its own.
The first of the Web server settings: Routes with an Add a route button, Title Files, Show the title, Show the address, The address is of, Show a status line, Status line.
The first of its settings.
SettingDefaultWhat it does
RoutesnoneThe addresses answered by your own functions: Add a route, then its address, whether it is asked (GET), sent to (POST) or either, and - if you want - the function’s name and whether it asks for the password. See Routes below.
TitleFilesThe heading on the card and on the page. Switch Show the title off for a card without one.
Show the address, The address is ofon, the files pageThe line somebody types into a browser: the page that manages the files, or the site itself.
Show a status line, Status lineon, {files} files, {free} freeWords of your own with the server’s figures in them: {files} how many it keeps, {free} the room left, {uploads} how many have arrived since the board started, {last} the name of the last one.
With no networkWaiting for the networkSaid where the address would be until the board has one.
Files are keptOn the board’s flashOr on an SD card, when the design has one under Hardware. The flash is the filesystem the design’s own pictures use, and as big as the partition layout makes it.
In the folder/wwwThe folder that is the site. Give it one of its own: the top folder of the flash is where the design’s pictures live, and they would be listed and could be deleted.
Opens withindex.htmlThe file a folder’s own address shows. Until the folder has one, the board serves a starter page of its own at /.
Starter page titleMy boardThe heading of that starter page.
Files page at/filesThe address of the page that manages the files.
Takes uploads, Lets files be deletedonOff, the page only lists. With both off the board only serves.
Log download page, Log page atoff, /logsPro, Arduino export. A page that lists the files the design’s Stream to card logs have written, with a download for each and, if Lets files be deleted is on, a delete behind a confirm. It needs a log under Hardware > Devices > Stream to card. The page, its three addresses and the code that sets it.
Largest file0In KB. 0 takes whatever there is room for.
Name, Passwordadmin, noneAsked for by the files page, and for every upload and delete. What is in the folder is served to anybody who asks: it is a site. The password is kept in the design and written into the firmware, and is taken out of any copy you publish.
HTTPSoffEncrypts the connection. Needs a board with PSRAM. Always on in a design with a Firmware update.
Port00 is the usual one: 80, or 443 with HTTPS.
Name on the networknoneGiven one - kitchen, say - the board also answers at kitchen.local.

Over plain HTTP a password is sent where anybody on the network can read it. That is what HTTP is, and on a home network it may be all you need. With HTTPS the board makes itself a certificate the first time it starts and keeps it; nobody has vouched for that certificate, so a browser warns before the first visit, and the connection is encrypted all the same. An encrypted connection takes about 40 KB of the board’s memory while it lasts, so two browsers are served at once and no more.

A variable can read what the server says of itself - on the Data tab, under Web server: its address, whether it is being served, how many files it keeps, the room left, how many have arrived and the name of the last, how many times a route has been asked for and which was last. A QR code bound to the address is a link somebody can point a phone at. The card has two events of its own, File received and A route was asked for.

Routes: a site of your own, with live data

A route is an address the board answers by running a function you write - /api/data for a page to ask the board’s values of, /api/set to send it a change, /update/getdata, whatever your pages need. You add one in the inspector, and the export does the rest of the wiring:

// GET /api/data
void route_api_data(ServerRequest & req)
{
    ServerJson j;
    j.add("Level", var_Level);          // the design's variables, by name
    j.add("Room", var_Room);
    req.json(j);                        // {"Level":5,"Room":"Kitchen"}
}

// POST /api/set
void route_api_set(ServerRequest & req)
{
    if (req.hasArg("Level")) var_Level_set((int32_t)req.argInt("Level"));
    req.ok();
}

And from a page of your own:

fetch("/api/data").then(r => r.json()).then(d => { level.textContent = d.Level; });
fetch("/api/set", { method: "POST", body: new URLSearchParams({ Level: 50 }) });
In a functionWhat it does
req.arg("name"), argInt, argFloat, argBool, hasArgA value sent with the request: ?name=value on the address, or a field of a posted form.
req.body(), req.isPost(), req.header("Accept")What was posted, whole; which kind of request it is; one of its headers.
req.json(j), req.text("..."), req.html("..."), req.ok()The answer. ServerJson builds the JSON a name at a time and does the quoting.
req.error(400, "why"), req.redirect("/"), req.send(200, "text/csv", "..."), req.sendHeader(...)Anything else an answer can be.

A function runs on the same thread as the screens, between two refreshes, so it may read and set the design’s variables and use its widgets without any locking - and the screen waits while it runs, so keep it short and never wait in one. A route ticked Password asks for the Web server’s name and password first. A POST is taken from the board’s own pages and from anything that is not a browser; a browser sent by a page on another site is refused.

lw_routes.cpp is written afresh by every export, like the events file: keep your own copy and take only a new route’s function from the new one. A route whose function is missing still builds - the board answers 501 for it and names the function to write. What a route is sent is read whole into memory, so a large upload belongs on the files page, not in a route.

The page is the board’s own, in English, and needs nothing from the internet. A folder inside the folder is served but not listed; an upload that stops part way leaves no file behind. None of this has been run on a board yet: the firmware is compiled, and the pages have been used in a browser against a stand-in for the board, not against one.

The log download page

Pro, Arduino export. A soil logger in the ground, its SD card out of reach: the CSV files come over Wi-Fi instead. Switch Log download page on and the board serves a page, at Log page at (/logs), that lists every file the design’s Stream to card logs have written - the card or the flash, as each log is set - with its size and date, a download link for each, and a Delete button when Lets files be deleted is on. The file a running log is writing is marked “being written”: it can be downloaded, never deleted. A log whose card is out says so. The page asks for the Web server’s Name and Password, and the Problems tab warns if deleting is allowed with no password.

The page is made of three plain addresses under the page’s own, so a page of yours can use them with fetch():

AddressDoes
GET /logs/listThe files, as JSON.
GET /logs/get?log=N&name=FILEOne file as a download: Content-Disposition: attachment, text/csv, sent a chunk at a time so a large file needs no memory.
POST /logs/delete?log=N&name=FILEDeletes it. Needs the header X-LW: 1. Refused with 409 for the file a running log is writing, and for one being downloaded.
{"delete":true,"logs":[{"id":0,"name":"Readings","store":"sd","present":true,
  "files":[{"name":"log_1.csv","size":1234,"time":1760000000,"writing":true}]}]}

id is the log’s number (N), present is false while its card is out, time is 0 when the board does not know the date, and writing marks the file a running log is writing. Only a file that belongs to a log can be read or deleted.

Style it, or replace it. The export carries logs.css and logs.example.html in the site’s folder (data/www/, or sdcard/www/ on a card). The built-in page links logs.css when the file is there: change it and send it back through /files. A file named logs.html in the folder is served at /logs in place of the built-in page and can call the three addresses; logs.example.html is the built-in page, to start from. The names follow the address: at /data they are data.html and data.css. Everything comes from the board - nothing from the internet.

From your own code. lw_logpage.cpp in the export holds logpage_configure(), run each time the server starts, before it answers. What it sets wins over the designer, and every line is a comment to take the comment off:

FieldWhat it sets
c->baseThe page’s address; /list, /get and /delete are under it.
c->page_file, c->css_fileThe page of your own, and the stylesheet the built-in page links, in the site’s folder. An empty page file always uses the built-in page.
c->allow_deleteWhether a file may be deleted.
c->folder, c->every_fileList this folder in place of each log’s own, and every file in it rather than only the log’s own.
c->add_routes, server_handle()Your own function, called with the server’s httpd_handle_t before the catch-all is registered, to add addresses with httpd_register_uri_handler(). server_handle() is the same handle, later. The Arduino export’s server is esp_http_server, not the WebServer class.

lw_logpage.cpp is written afresh by every export: keep your copy. Without the file the board builds just the same.

The server follows Wi-Fi

The Web server has no switch of its own. In a design with the Wi-Fi on or off action it starts once the board has an address and stops, cleanly, when the network goes - a download in progress included - and starts again when Wi-Fi returns. Is being served (Data tab > Web server) says which it is. A battery logger can keep Wi-Fi off, switch it on for a button press, and off again once the files are fetched. It is compiled for the ESP32 and the S3; it has not been run on a board.

Example: two routes for a page of your own

  1. Drag a Web server onto the screen and set its Title to Kitchen board.
  2. Under Routes press Add a route: it suggests GET /api/data. Press it again for POST /api/set. Each is answered by a function of yours in lw_routes.cpp, already written to answer with the design’s variables.
  3. On the Data tab add a variable, Level: the first route will answer with it as JSON.
  4. It is served over a network: add a WiFi Setup, and a Password, as the Problems tab says.
Web server settings with two routes: GET /api/data answered by route_api_data(), POST /api/set answered by route_api_set(), and Title Kitchen board.
Two routes, each with the function that answers it.