Stage 2 of 6 · Theory · about 30 minutes
The web, quickly
Week 9 taught one way for programs to talk; this page teaches the other, the one the entire web runs on. HTTP is older, simpler and more asked-about in interviews than anything else in this course, and your dashboard needs exactly the slice presented here: the cycle, the addresses, the verbs, the verdicts, and ten tags of HTML.
The other protocol
HTTP, the Hypertext Transfer Protocol, was created by Tim Berners-Lee at CERN around 1989–91 as the fetching half of the web: a client (then and now, usually a browser) opens a TCP connection to a server, sends one request naming a resource, receives one response carrying it, and that is the whole transaction. Three design choices from that birth still shape everything you build today. It is client-initiated: servers never call browsers; they answer. It is request/response: every exchange is a matched pair, an ask and an answer. And it is stateless: each request stands alone, carrying everything the server needs, with no memory of the last one built into the protocol. That last property is why the web scales to billions of clients, and it is also precisely why your dashboard will need the bridge: a stateless server that is asked "what is the light level?" must have the answer already in hand, because the protocol gives it no way to go and wait for one.
One request, anatomized
Reading a URL
:80 (HTTP's default port) the way Week 9 hid nothing: your Pi serves on 5000, so the port must be written. Scheme and host find the server; the path is yours to design.GET, POST, and the verbs' meaning
The method is the request's verb, and the two you need carry a real semantic contract. GET reads: it should change nothing on the server, which lets browsers cache it, prefetch it and repeat it freely. POST submits: it acts, creates or changes, and clients treat it carefully because repeating it repeats the act. Your dashboard respects the contract exactly: reading the light level is a GET, while setting the loop's target is a POST, because it moves a physical LED. If that "safe to repeat" reasoning feels familiar, it should: it is Week 9's idempotency argument from the QoS table, arrived on the other protocol, and the same design instinct answers both.
Status codes: the server's verdicts
| Code | Meaning | On your bench, it means |
|---|---|---|
| 200 OK | Served as asked | The normal case; the body is your answer |
| 404 Not Found | No resource at that path | A route/path mismatch: a typo, or the stray trailing slash |
| 400 Bad Request | The request itself was malformed | Usually the setpoint form sent something float() rejects |
| 500 Internal Server Error | The server's code raised | Your Python has a traceback waiting in the Flask terminal; read it bottom-up, Week 4 style |
Just enough HTML
Responses carrying pages speak HTML, a tree of tagged elements. The dashboard needs about ten tags, and here they are:
| Tag | Job |
|---|---|
<html>, <head>, <body> | The document, its metadata, its visible content |
<h1>, <p> | Heading and paragraph |
<div>, <span> | A block box and an inline run; the hooks JavaScript updates by id |
<input>, <button> | A field to type the setpoint, a button to send it |
<script>, <style> | The page's JavaScript and CSS, inline for a one-file dashboard |
<!-- the whole shape of a page -->
<html>
<head><title>Station s07</title></head>
<body>
<h1>Station s07</h1>
<p>Light: <span id="pct">–</span> %</p>
</body>
</html>
Two patterns, two jobs
Week 9's opening argument was "why not just request the data?", and it buried polling for machine telemetry. This week completes the thought: request/response was never the wrong pattern, only the wrong pattern for that job. Put them side by side and each one's territory is obvious:
| HTTP request/response | MQTT publish/subscribe | |
|---|---|---|
| Who initiates | The client who wants data | The device that has data |
| When | On demand | On events |
| Audience | One asker, one answerer | One publish, any subscribers |
| State | Stateless: each request self-contained | Broker holds retained values and wills |
| Shines at | Human interfaces, documents, APIs, commands wanting a confirmed answer | Machine telemetry, fleet fan-out, loose coupling |
| On your bench | browser ↔ Flask | node ↔ broker ↔ monitors |
One honest edge note: the browser speaks HTTP (and WebSockets), not raw TCP, so it cannot join the broker directly the way paho does; MQTT-over-WebSockets exists for exactly that, and it is a fine thing to know the name of and not build this term. Your bridge does the joining instead, next two pages.
The Pi's three doors
By tonight, sudo ufw status verbose on your Pi reads like a résumé: 22 (SSH, Week 3), 1883 (MQTT, Week 9), 5000 (HTTP, today). One machine, three services, three deliberately opened doors, each one justified in writing at the moment it opened. On the public internet the web answers at 80 and, encrypted, at 443; 5000 is Flask's development convention, and the same production honesty from Week 9 applies: real deployments put a hardened server and TLS in front. The bench stays plain and local, on purpose, and knows it.
Checklist for this stage
Check yourself
Why does HTTP's statelessness force the dashboard to keep a "latest values" structure on the server?
Segment http://192.168.1.42:5000/api/light and say which Week built your knowledge of each part.
http (this week), host 192.168.1.42 (the Pi's address, the hostname -I habit from Week 2), port 5000 (the door concept from Weeks 3 and 9, new number today), path /api/light (this week's route, designed with Week 9's namespace instincts).