The simplest is to run a tiny web server on the Raspberry Pi that reads your sensor and serves an HTML page that updates itself (polling or WebSockets). Below are three approaches—pick A for quick wins, B for real-time updates, C if you like visual dashboards with almost no coding.
A) Python + Flask (poll every few seconds)
1) Wire the sensor
Examples:
- DHT22 → 1 GPIO + 3.3 V + GND
- I²C sensors (e.g., BMP280, SHT31) → SDA (GPIO2), SCL (GPIO3), 3.3 V, GND
- 1-Wire (DS18B20) → enable 1-Wire in raspi-config (Interface Options)
2) Install basics
sudo apt update
sudo apt install -y python3-venv python3-pip
python3 -m venv ~/env
source ~/env/bin/activate
pip install flask
# Optional sensor libs (choose yours)
pip install adafruit-circuitpython-dht adafruit-circuitpython-bmp280 RPi.GPIO board
3) Minimal Flask app (save as app.py)
from flask import Flask, jsonify, render_template_string
import time, random
# TODO: replace with your real sensor read (e.g., Adafruit libs)
def read_sensor():
# return a dict; replace with real readings
return {"temp_c": round(20 + random.random()*5, 2),
"humidity": round(40 + random.random()*10, 1),
"ts": int(time.time())}
PAGE = """
<!doctype html>
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Raspberry Pi Sensor</title>
<style>
body{font-family:system-ui,Arial;margin:2rem;color:#0b1320}
.card{max-width:520px;padding:1rem;border:1px solid #e5e7eb;border-radius:12px}
h1{margin:.2rem 0 1rem}
.grid{display:grid;grid-template-columns:1fr 1fr;gap:1rem}
.k{font-size:3rem;font-weight:700}
.muted{color:#64748b}
#spark{width:100%;height:60px}
</style>
<div class="card">
<h1>📟 Sensor Dashboard</h1>
<div class="grid">
<div><div class="muted">Temperature</div><div id="t" class="k">-- °C</div></div>
<div><div class="muted">Humidity</div><div id="h" class="k">-- %</div></div>
</div>
<div class="muted" style="margin-top:.8rem">Updated: <span id="time">--</span></div>
<svg id="spark" viewBox="0 0 100 20" preserveAspectRatio="none"></svg>
</div>
<script>
const tEl = document.getElementById('t');
const hEl = document.getElementById('h');
const timeEl = document.getElementById('time');
const spark = document.getElementById('spark');
let history = [];
function drawSpark() {
if (history.length < 2) return;
const max = Math.max(...history), min = Math.min(...history) || 0;
const xs = history.map((v,i)=>[i*(100/(history.length-1)), 20-( (v-min)/(max-min||1)*20 )]);
const d = "M"+xs.map(([x,y])=>`${x},${y}`).join(" L ");
spark.innerHTML = `<path d="${d}" fill="none" stroke="black" stroke-width="0.8"/>`;
}
async function tick(){
try{
const r = await fetch('/api');
const j = await r.json();
tEl.textContent = j.temp_c.toFixed(2) + " °C";
hEl.textContent = j.humidity.toFixed(1) + " %";
timeEl.textContent = new Date(j.ts*1000).toLocaleString();
history.push(j.temp_c);
if (history.length > 40) history.shift();
drawSpark();
}catch(e){ console.error(e); }
}
setInterval(tick, 2000);
tick();
</script>
"""
app = Flask(__name__)
@app.route("/")
def index():
return render_template_string(PAGE)
@app.route("/api")
def api():
return jsonify(read_sensor())
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)
Hook up your real sensor
Replace read_sensor() with code for your device. Examples:
DHT22 (GPIO4)
import adafruit_dht, board
dht = adafruit_dht.DHT22(board.D4, use_pulseio=False)
def read_sensor():
t = dht.temperature
h = dht.humidity
return {"temp_c": round(t,2), "humidity": round(h,1), "ts": int(time.time())}
BMP280 (I²C)
import board, busio, adafruit_bmp280
i2c = busio.I2C(board.SCL, board.SDA)
bmp = adafruit_bmp280.Adafruit_BMP280_I2C(i2c)
def read_sensor():
return {"temp_c": round(bmp.temperature,2), "humidity": None, "ts": int(time.time())}
4) Run it
source ~/env/bin/activate
python app.py
Browse from your PC: http://:8000/ (or http://raspberrypi.local:8000/)
5) Start on boot (systemd)
Create /etc/systemd/system/sensor-web.service:
[Unit]
Description=Pi Sensor Web
After=network-online.target
[Service]
User=pi
WorkingDirectory=/home/pi/sensor-web
ExecStart=/home/pi/env/bin/python /home/pi/sensor-web/app.py
Restart=always
[Install]
WantedBy=multi-user.target
Then:
sudo systemctl daemon-reload
sudo systemctl enable --now sensor-web
B) Real-time updates (WebSockets)
If you want sub-second updates without polling, use Flask-SocketIO:
pip install flask-socketio eventlet
In Python, emit readings on a timer; in the page, subscribe via Socket.IO and update the DOM. This reduces overhead for frequent updates.
C) Zero-code dashboards (Node-RED)
- sudo apt install -y nodered (or use the official script from nodered.org)
- Start Node-RED: node-red-start → open http://:1880
- Add rpi-gpio / I²C input nodes, process in a function node, and wire to dashboard nodes.
- Open the dashboard at /ui (buttons, gauges, charts with clicks).
This is great for multi-sensor projects and prototyping.
Tips & pitfalls
- Serve JSON + a simple HTML/JS—keeps logic clean and lets you redesign the page later.
- Use 4-bit SDMMC + buffering (if logging) to avoid blocking the web loop during writes.
- Security: keep it on your LAN; if you must expose it, put nginx in front and use basic auth/TLS.
- Performance: don’t read slow sensors more often than they can update; cache the last reading and serve it to the page.

Top comments (0)