Project 8: Local Web Dashboard for Sensor Data
Build a lightweight local web UI that serves live sensor data without blocking the acquisition loop.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Intermediate |
| Time Estimate | 1–2 weekends |
| Main Programming Language | Python (Alternatives: Go, Rust, Node.js) |
| Alternative Programming Languages | Go, Rust, Node.js |
| Coolness Level | Medium |
| Business Potential | Medium |
| Prerequisites | HTTP basics, Linux CLI, sensor loop from P03 |
| Key Topics | HTTP server, data caching, inter-process separation, mDNS |
1. Learning Objectives
By completing this project, you will:
- Build a local HTTP server that serves JSON and HTML.
- Decouple sensor acquisition from web requests.
- Implement caching and rate limiting for stable performance.
- Secure a local web UI with minimal overhead.
2. All Theory Needed (Per-Concept Breakdown)
Concept 1: Lightweight HTTP Services and Data Sharing Patterns
Fundamentals
An embedded web dashboard is a simple HTTP server that returns data in JSON and serves a static page. The challenge is not HTTP itself; it is ensuring that web requests do not block sensor collection or crash the device. To achieve this, you separate the sensor loop from the web server and share data via a cache or IPC mechanism. A lightweight, local UI must be efficient, predictable, and secure enough for a LAN environment. Understanding request/response flow and data caching is the core of this project.
Deep Dive into the concept
HTTP is a request/response protocol over TCP. The browser requests a URL, and the server responds with headers and a body. For embedded dashboards, you typically serve a static HTML page and a /status endpoint that returns JSON. The browser uses JavaScript to fetch JSON periodically and update the UI. This design keeps the server simple: one endpoint for UI, one endpoint for data.
The sensor loop should run on its own schedule, independent of HTTP requests. If a web request triggers a sensor read, then a slow sensor or blocking I/O can make the UI sluggish or even crash the server. The solution is to maintain a cached “latest reading” in memory or in a local file. The acquisition loop updates the cache at a fixed interval, while the web server returns the cached value immediately. This pattern is sometimes called “producer-consumer” or “push-to-cache, pull-from-cache.”
You must consider concurrency. In Python, you can implement the sensor loop in a separate thread or process, or use an async event loop. If you share memory, you need synchronization (a lock or atomic update). A common pattern is to store the latest reading in a global dictionary and update it under a lock; the HTTP handler reads it without blocking. Alternatively, you can write the data to a JSON file and read it from the server, but file I/O adds latency and wear. For a Pi Zero 2 W, a small in-memory cache is simplest and fastest.
Rate limiting is important because browsers can refresh aggressively. A client that polls every 100 ms will waste CPU and network bandwidth. Your server should either enforce a minimum interval or return cache headers that instruct the browser to reuse data. Even in local networks, this matters for battery-powered systems. You should also consider mDNS (e.g., pi-zero2w.local) for discovery. This depends on avahi-daemon; if it is disabled, you need a fallback (IP address or static DNS). Security is minimal but not ignored: if the device is on a shared network, basic auth or a simple token can prevent casual access.
Determinism is also relevant. For reproducible tests, your /status endpoint should accept a “fixed time” environment variable, or the server should allow a simulation mode that serves deterministic data. This makes it possible to write golden-path tests that do not depend on real sensors.
How this fit on projects
This concept is used in §3, §4, and §5.10. It connects to Project 9 (MQTT publishing) and the capstone project.
Definitions & key terms
- HTTP: Hypertext Transfer Protocol.
- JSON: Lightweight data interchange format.
- Cache: Stored data returned without recomputing.
- mDNS: Local network name resolution.
- Rate limiting: Restricting request frequency.
Mental model diagram (ASCII)
Sensor Loop -> Cache -> HTTP /status -> Browser UI
How it works (step-by-step, with invariants and failure modes)
- Sensor loop updates cache on schedule.
- HTTP server serves static UI.
/statusreturns cached JSON.
Failure modes:
- Cache missing -> server returns error.
- Sensor loop blocked -> stale data.
- Excessive polling -> high CPU.
Minimal concrete example
latest = {"temp": 22.4, "humidity": 45.6}
@app.route('/status')
def status():
return jsonify(latest)
Common misconceptions
- “The web server should read the sensor directly.” That blocks and adds jitter.
- “Local networks are always safe.” Basic protection is still needed.
Check-your-understanding questions
- Why should sensor reads be decoupled from HTTP requests?
- How does caching improve reliability?
- What breaks if mDNS is disabled?
Check-your-understanding answers
- It prevents slow sensors from blocking the UI.
- It provides fast, consistent responses.
hostname.localdiscovery fails; you need IP fallback.
Real-world applications
- Local dashboards for thermostats, routers, and lab sensors.
Where you’ll apply it
- This project: §3.2 and §5.10.
- Other projects: Project 9, Project 17.
References
- “Computer Networks” — HTTP basics
- Flask/FastAPI docs
Key insights
Serving data is easy; serving data without blocking the device is the real challenge.
Summary
A local dashboard is a small HTTP server backed by cached sensor data and careful rate limits.
Homework/Exercises to practice the concept
- Build a JSON endpoint that returns fixed data.
- Add a cache and update it from a background loop.
- Test how refresh intervals affect CPU load.
Solutions to the homework/exercises
- Use a minimal HTTP server and return JSON.
- Update the cache in a thread and read in the handler.
- Higher refresh rates increase CPU; choose a balance (1–5 s).
3. Project Specification
3.1 What You Will Build
A local web dashboard with a static UI and a JSON endpoint that serves cached sensor data.
3.2 Functional Requirements
- Serve
/with a simple HTML page. - Serve
/statuswith JSON sensor data. - Update sensor data on a fixed schedule.
- Cache results and avoid blocking HTTP requests.
3.3 Non-Functional Requirements
- Performance:
/statusresponds < 50 ms. - Reliability: No missed sensor updates in 1 hour.
- Usability: UI readable on mobile.
3.4 Example Usage / Output
$ curl http://pi-zero2w.local/status
{"temp_c":22.4,"humidity":45.6,"uptime":"3h14m"}
3.5 Data Formats / Schemas / Protocols
JSON response:
{"temp_c":22.4,"humidity":45.6,"uptime":"3h14m","last_update":"10:21:05"}
3.6 Edge Cases
- Sensor data not available.
- Excessive polling.
- No mDNS resolution.
3.7 Real World Outcome
A phone on the same network shows live sensor data with stable refresh.
3.7.1 How to Run (Copy/Paste)
python3 dashboard.py --host 0.0.0.0 --port 8080
3.7.2 Golden Path Demo (Deterministic)
export FIXED_TIME="2026-01-01T10:50:00Z"
python3 dashboard.py --simulate
Expected output:
[2026-01-01T10:50:00Z] /status -> 200
3.7.3 Failure Demo (Deterministic)
python3 dashboard.py --simulate --cache-missing
Expected output:
[ERROR] Cache unavailable
Exit code: 81
3.7.4 CLI Exit Codes
0: Success80: HTTP server failed81: Cache missing
4. Solution Architecture
4.1 High-Level Design
Sensor Loop -> Cache -> HTTP Server -> Browser UI
4.2 Key Components
| Component | Responsibility | Key Decisions | |—|—|—| | Sensor Loop | Gather data | Interval | | Cache | Store latest | In-memory vs file | | HTTP Server | Serve endpoints | Framework choice | | UI | Display data | Polling interval |
4.3 Data Structures (No Full Code)
latest = {"temp_c": 22.4, "humidity": 45.6, "ts": ts}
4.4 Algorithm Overview
Key Algorithm: Cached Status Response
- Update cache on schedule.
- Serve cache on request.
- Rate-limit polling.
Complexity Analysis:
- Time: O(1) per request
- Space: O(1)
5. Implementation Guide
5.1 Development Environment Setup
pip install flask
5.2 Project Structure
project-root/
├── dashboard.py
├── sensor_loop.py
├── static/
│ └── index.html
└── README.md
5.3 The Core Question You’re Answering
“How do you present live device data without destabilizing the device?”
5.4 Concepts You Must Understand First
- HTTP request/response basics.
- Sensor loop separation and caching.
- mDNS discovery and local networking.
5.5 Questions to Guide Your Design
- How will you keep data fresh without blocking?
- What is a safe polling interval for the UI?
5.6 Thinking Exercise
Draw a data flow diagram from sensor read to browser update.
5.7 The Interview Questions They’ll Ask
- Why separate the sensor loop from web requests?
- How do you reduce server load on small devices?
- How would you secure a local dashboard?
5.8 Hints in Layers
Hint 1: Serve static HTML first.
Hint 2: Add a /status JSON endpoint.
Hint 3: Cache the latest sensor reading.
5.9 Books That Will Help
| Topic | Book | Chapter | |—|—|—| | HTTP basics | Computer Networks | Ch. 2 | | Processes | The Linux Programming Interface | Ch. 6 |
5.10 Implementation Phases
Phase 1: Server bring-up (2 hours)
- Serve static HTML and JSON endpoint.
Phase 2: Sensor loop (4 hours)
- Add sensor cache updated on schedule.
Phase 3: UI polishing (3 hours)
- Add refresh logic and mobile-friendly layout.
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale | |—|—|—|—| | Framework | Flask / FastAPI / bare socket | Flask | Simple and lightweight | | Cache | Memory / File | Memory | Low latency |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples |
|—|—|—|
| Unit Tests | Cache update | JSON correctness |
| Integration Tests | HTTP responses | /status 200 |
| Edge Case Tests | Missing cache | Exit 81 |
6.2 Critical Test Cases
/statusreturns JSON every time.- Sensor loop runs even with no web traffic.
- Cache missing -> error response.
6.3 Test Data
{"temp_c":22.4,"humidity":45.6}
7. Common Pitfalls & Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Solution | |—|—|—| | Sensor reads in handler | Slow responses | Use cache | | Aggressive polling | High CPU | Rate-limit | | No mDNS | Hostname not found | Use IP |
7.2 Debugging Strategies
- Use
curlandabto test response time. - Monitor CPU with
topwhile refreshing.
7.3 Performance Traps
- Rendering templates on every request can add overhead.
8. Extensions & Challenges
8.1 Beginner Extensions
- Add uptime and CPU load display.
8.2 Intermediate Extensions
- Add authentication token for
/status.
8.3 Advanced Extensions
- Add WebSocket push updates.
9. Real-World Connections
9.1 Industry Applications
- Local dashboards for home automation and industrial sensors.
9.2 Related Open Source Projects
- Home Assistant local dashboards.
9.3 Interview Relevance
- Discussing caching and separation of concerns is common in interviews.
10. Resources
10.1 Essential Reading
- Flask documentation (or chosen framework docs).
10.2 Video Resources
- Building lightweight web servers tutorials.
10.3 Tools & Documentation
curlandabfor testing.
10.4 Related Projects in This Series
11. Self-Assessment Checklist
11.1 Understanding
- I can explain HTTP request/response flow.
- I can explain why caching is necessary.
11.2 Implementation
- Dashboard serves data reliably.
- Sensor loop is independent of web traffic.
11.3 Growth
- I can explain the architecture in an interview.
12. Submission / Completion Criteria
Minimum Viable Completion:
/statusendpoint returns JSON.
Full Completion:
- Live UI with caching and rate limiting.
Excellence (Going Above & Beyond):
- WebSocket push updates with authentication.