Hello world from the board
Thirty lines and the board has a URL of its own. The idea worth taking from them is that a web server is a table of paths, and one line in loop() is the difference between answering and hanging.
Ask for a path the board knows, then one it does not, then take
handleClient() away.
server.on registered it and did nothing else; the call in loop() is what found the waiting request and called it. Two separate things, and only one of them is in setup().A server is a table and a pump
Two things make it work, and beginners usually only notice the first.
server.on(path, method, handler)adds a row to a table. Nothing runs.server.handleClient()looks for a waiting request, finds the matching row, and calls it. Take it out ofloop()and the board still accepts the connection — it just never says anything, so the browser spins.
That is why a blocked loop() looks identical to a crashed board from the
outside.
The address is private
WiFi.localIP() prints something like 192.168.1.47. That works from a phone
on the same Wi-Fi, and from nowhere else. It is not a bug and there is no
setting to change it. If you want the readings from outside the house, push
them out to a broker rather than letting the world in — MQTT and a
broker is that page.
The address also moves. The router hands it out on a lease, so it changes after a power cut. mDNS gives the board a name that does not.
Do not build pages out of strings
A one-line <h1> is fine. The moment the page has a stylesheet and a script,
concatenating it in C++ costs RAM you do not have and makes every edit a
recompile. Put the file on flash and serve it from there — that is what
Filesystem on flash is for, and it is the
starting point for every page after this one.
The code
One route and a fallback. `server.on` puts a path in the table; `handleClient()` is the line that actually runs what is in it.
#include <WiFi.h>
#include <WebServer.h>
WebServer server(80); // 80 is what a browser assumes
void setup() {
Serial.begin(115200);
WiFi.begin("your-network", "your-password");
while (WiFi.status() != WL_CONNECTED) delay(250);
Serial.println(WiFi.localIP()); // type this into a browser
server.on("/", HTTP_GET, [] {
server.send(200, "text/html", "<h1>Lonely Binary</h1>");
});
server.onNotFound([] {
server.send(404, "text/plain", "no such page");
});
server.begin();
}
void loop() {
server.handleClient(); // delete this line and nothing is ever answered
}This server answers one request at a time, from loop(). Every millisecond loop() spends elsewhere is a millisecond the browser spends waiting.
There is no WebServer class in the standard build, so the routing table is two branches of an if. That is the whole shape - read the request line, decide, write bytes back.
import network, socket
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect('your-network', 'your-password')
while not wlan.isconnected():
pass
print(wlan.ifconfig()[0]) # type this into a browser
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('0.0.0.0', 80))
s.listen(1)
while True:
conn, _ = s.accept()
req = conn.recv(1024).decode()
path = req.split(' ')[1] if ' ' in req else '/'
if path == '/':
status, body = '200 OK', '<h1>Lonely Binary</h1>'
else:
status, body = '404 Not Found', 'no such page'
conn.send('HTTP/1.0 %s\r\nContent-Type: text/html\r\n\r\n' % status)
conn.send(body)
conn.close()SO_REUSEADDR before bind. Without it a soft reset leaves port 80 held and the next run dies with EADDRINUSE.
When it does not work
The board is reachable and nothing is answering. Either handleClient() is missing from loop(), or something in loop() is blocking - a delay, a sensor that waits, a while loop of your own. The connection is accepted by the TCP stack either way, which is why it hangs instead of failing.
Wi-Fi has not finished joining. Wait on WiFi.status() until it returns WL_CONNECTED before reading localIP, rather than printing it once at the top of setup.
No onNotFound handler is registered, so unmatched paths get an empty response and the browser shows a white page. Register one, even if it only says the path is wrong - a blank page tells you nothing about which end is broken.
They are on different networks, or the router has client isolation on for the guest band. The board only has a private address, so both devices have to be on the same subnet for it to be reachable at all.
A fixed string proves the plumbing. The next page puts a number on it that changes.
A reading on the page →Edit this page — content/esp32/web-server-hello-world.mdx
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.