16. Interface and application programming¶
Introduction¶
While trying to understand how a browser on a phone can display a webpage stored on a tiny chip, I came back to something my instructor Dani had explained during the course: WiFi is light.
Not metaphorically — it is electromagnetic radiation, the same category of phenomenon as sunlight, visible light, and radio waves, just at a frequency our eyes can’t see. The ESP32 and the phone communicate by modulating this radiation and reading the patterns in it.
This was a useful shift for me. Before this, “the network” felt like an abstract layer of software rules existing somewhere separate from the physical world. After this, it became a natural phenomenon — as natural as sunlight. WiFi isn’t a digital invention layered on top of reality; it’s a part of reality we’ve learned to read and write to.
What follows covers the specific points that clarified my understanding this week, with the prompts and questions that led there, followed by the application I built.
Building a Foundation Before the Code¶
I don’t come from a coding or software background, so before looking at any code I asked Claude to help me build a basic mental model of how devices communicate with each other and what the underlying technologies actually do. I asked for analogies — comparisons to things I already understand from daily life — rather than technical definitions.
A few of these analogies became the foundation for everything else this week:
-
A web server is a café. It sits and waits. When someone walks in and places an order, it responds. When nobody is there, it does nothing. An ESP32 running a web server is doing exactly this, just at a much smaller scale — it waits for a request and responds when one arrives.
-
An IP address is a house number. When a device joins a network, it’s given an address — a number that identifies where it is on that network, the way a house number identifies a location on a street. Without that number, nobody could find it to deliver anything.
-
A handshake is the “hello?” before a phone call. Before two devices exchange any real information, there’s a brief exchange to confirm both sides are present and ready. This happens automatically and invisibly — I don’t write code for it, but it’s happening every time a connection is made.
-
A browser is a window, not the thing itself. The browser on my phone doesn’t know or care whether the page it’s displaying comes from a massive server on the other side of the world or a small chip sitting on my desk. Its only job is to ask “what’s at this address?” and draw whatever comes back. This was important for understanding that my ESP32 isn’t doing anything fundamentally different from a website — it’s just much smaller and much closer.
Working through these analogies first meant that when I did start looking at code, I wasn’t reading unfamiliar syntax in a vacuum — I already had a picture in my head of what each piece was supposed to be doing.
Choosing a Strategy¶
There were two ways to set up the interface:
-
Option A — the ESP32 hosts its own page. The board creates its own WiFi network and serves the webpage itself. Whoever connects to that network and opens a browser sees the page and can control the board directly. Self-contained — one device, one network, no external dependencies.
-
Option B — the ESP32 joins an existing network. The board connects to a WiFi router like any other device, getting an address on that network. Other devices on the same network — or potentially the internet, with the right configuration — could then reach it.
I chose Option A. It was the simpler setup: no router credentials to manage, no dependency on a specific WiFi network being available, and the board’s address is always the same (192.168.4.1), which makes testing predictable. Option B remains interesting for future development. It opens the door to controlling a device remotely — from another room, another building, or in principle another country — This will help bring to life my original idea of trasmiting the sound of waves from a distance.
Writing the Code¶
I worked with Claude to put the code together. Going in, I knew there were three things the code needed to do: define the LED pin and set it as an output, turn on the ESP32’s WiFi, and write the application — the webpage itself — that would be sent to the browser.
In the process, I also understood the difference between the three languages involved. C++ is the language the Arduino code itself is written in — it controls the chip directly: pins, WiFi, logic. HTML is not a programming language in the same sense; it describes the content and structure of a webpage — the circle, the buttons, the text. JavaScript is what makes that webpage interactive — it’s the part that runs in the browser and sends a message back to the chip when a button is pressed. All three exist together in a single file: the HTML and JavaScript are stored as text inside the C++ code, and the ESP32 hands that text to the browser when asked.
Below is the code, explained section by section as I came to understand it.
- Bringing in the tools
#include <WiFi.h>
#include <WebServer.h>
These two lines load the libraries needed — one for WiFi, one for running a web server. Without them, none of the WiFi or server commands used later would exist.
- Setting up names and pins
const char* ssid = "Nadieh_Board";
const char* password = "12345678";
#define LED_PIN 48
WebServer server(80);
This defines the name and password of the network the board will create, which pin the LED is on (48, confirmed by testing), and declares a web server — though it isn’t running yet at this point.
- The webpage itself
const char html[] = R"rawliteral(
<!DOCTYPE html>
<html>
...
<div class="led" id="led"></div>
<button onclick="setLed('on')">Turn LED on</button>
<button onclick="setLed('off')">Turn LED off</button>
<script>
function setLed(action) {
fetch('/led-' + action)...
}
</script>
...
)rawliteral";
This entire block is HTML and JavaScript stored as text inside the C++ code. The HTML describes what’s on the page — a circle representing the LED, and two buttons. The JavaScript function setLed() runs in the browser when a button is pressed, and sends a small request back to the ESP32 using fetch().
- Functions that respond to requests
void handleRoot() {
server.send(200, "text/html", html);
}
void handleLedOn() {
digitalWrite(LED_PIN, HIGH);
server.send(200, "text/plain", "LED ON");
}
void handleLedOff() {
digitalWrite(LED_PIN, LOW);
server.send(200, "text/plain", "LED OFF");
}
Each of these functions matches one of the “addresses” the browser can request. handleRoot sends back the whole webpage. handleLedOn and handleLedOff change the physical pin and send back a short confirmation.
- Setup — runs once
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
WiFi.softAP(ssid, password);
Serial.println("Access Point started");
Serial.print("Open a browser and go to: http://");
Serial.println(WiFi.softAPIP());
server.on("/", handleRoot);
server.on("/led-on", handleLedOn);
server.on("/led-off", handleLedOff);
server.begin();
}
This prepares the LED pin, turns the ESP32 into its own WiFi network, prints its address to the Serial Monitor, connects each address to its matching function, and starts the server.
- Loop — runs continuously
void loop() {
server.handleClient();
}
This single line runs constantly, checking whether a request has come in and, if so, running the matching function. This is what makes pressing a button on the phone actually cause something to happen on the chip.
Full Code
#include <WiFi.h>
#include <WebServer.h>
const char* ssid = "Nadieh_Board";
const char* password = "12345678";
#define LED_PIN 48
WebServer server(80);
const char html[] = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>LED Control</title>
<style>
body { font-family: sans-serif; text-align: center; padding-top: 60px; }
.led { width: 80px; height: 80px; border-radius: 50%; background: #ccc;
margin: 0 auto 30px; border: 4px solid #999; }
.led.on { background: #5DCAA5; border-color: #1D9E75; }
button { font-size: 18px; padding: 12px 24px; margin: 6px; border-radius: 8px;
border: 1px solid #ccc; cursor: pointer; }
button.on { background: #534AB7; color: white; border: none; }
</style>
</head>
<body>
<div class="led" id="led"></div>
<button class="on" onclick="setLed('on')">Turn LED on</button>
<button onclick="setLed('off')">Turn LED off</button>
<script>
function setLed(action) {
fetch('/led-' + action).then(() => {
document.getElementById('led').className =
'led' + (action === 'on' ? ' on' : '');
});
}
</script>
</body>
</html>
)rawliteral";
void handleRoot() {
server.send(200, "text/html", html);
}
void handleLedOn() {
digitalWrite(LED_PIN, HIGH);
server.send(200, "text/plain", "LED ON");
}
void handleLedOff() {
digitalWrite(LED_PIN, LOW);
server.send(200, "text/plain", "LED OFF");
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
WiFi.softAP(ssid, password);
Serial.println("Access Point started");
Serial.print("Open a browser and go to: http://");
Serial.println(WiFi.softAPIP());
server.on("/", handleRoot);
server.on("/led-on", handleLedOn);
server.on("/led-off", handleLedOff);
server.begin();
}
void loop() {
server.handleClient();
}