every hardware project starts with one device and ends with three. cloud-direct works fine for one or two — that’s week 1’s setup. around the third device you start wanting one local thing to coordinate, buffer, and forward instead of N independent connections to the internet.
this tutorial builds that. a $35 raspberry pi running mosquitto and a 40-line python bridge, two ESP32s publishing temperature over MQTT, everything landing in plexus as separate sources. ~30 minutes.
ESP32 #1 (greenhouse) ─┐
│ MQTT raspberry pi
├─────────▶ mosquitto ─HTTPS─▶ plexus
ESP32 #2 (fridge) ───┘ bridge.py
(each ESP32
lands as
its own source)three boxes, two pipes. each ESP32 publishes temperature over MQTT to a topic that includes its source_id — plexus/esp32-greenhouse/temperature, plexus/esp32-fridge/temperature. mosquitto on the pi relays MQTT locally, no plexus knowledge. bridge.py subscribes to plexus/+/+, parses the topic, and forwards each message to plexus with the right source_id attached.
the result: each ESP32 shows up in plexus as its own source, with the same metric names as week 1. week 1’s threshold monitor and week 2’s anomaly detector work unchanged — they query by source_id, and now there are real per-device source_ids.
this is one valid topology, not the only one. cloud-direct from week 1 is fine for one or two devices and one less thing to maintain. the pi earns its keep when you want local buffering during WAN flakes, a single egress connection instead of N, or somewhere to run edge logic without round-tripping to the cloud.
total new spend on the pi side: ~$50.
raspberry pi imager handles this — we won’t tutorialize it.
plexus-pi.local), enable SSH, set username/password, pre-fill WiFi credentials.ssh pi@plexus-pi.localstuck on headless setup? the pi imager docs cover the screen-and-keyboard path too.
three things on the pi: mosquitto (MQTT broker), plexus-python (the SDK), and bridge.py (next step).
sudo apt update
sudo apt install -y mosquitto mosquitto-clients
python3 -m venv ~/plexus-bridge
source ~/plexus-bridge/bin/activate
pip install plexus-python paho-mqttmosquitto needs to accept LAN connections — drop a small override:
echo "listener 1883
allow_anonymous true" | sudo tee /etc/mosquitto/conf.d/local.conf
sudo systemctl restart mosquittoallow_anonymous true is fine on a home LAN. for production, see going further.
verify mosquitto is listening:
mosquitto_sub -h localhost -t '#' -vleave that running in a second terminal — you’ll watch ESP32 messages arrive there in step 4.
bridge.py subscribes to MQTT, parses topics shaped plexus/<source_id>/<metric>, and forwards each message to plexus with the right source_id attached.
# ~/bridge.py
import json
import os
import paho.mqtt.client as mqtt
from plexus import Plexus
MQTT_HOST = os.environ.get("MQTT_HOST", "localhost")
TOPIC = os.environ.get("MQTT_TOPIC", "plexus/+/+")
clients = {}
def get_client(source_id):
if source_id not in clients:
clients[source_id] = Plexus(source_id=source_id)
return clients[source_id]
def on_message(_c, _u, msg):
parts = msg.topic.split("/")
if len(parts) != 3 or parts[0] != "plexus":
return
_, source_id, metric = parts
payload = msg.payload.decode("utf-8", errors="replace")
try:
value = float(payload)
except ValueError:
try:
value = json.loads(payload)
except ValueError:
value = payload
get_client(source_id).send(metric, value)
c = mqtt.Client()
c.on_message = on_message
c.connect(MQTT_HOST)
c.subscribe(TOPIC)
print(f"bridging {TOPIC} → plexus")
c.loop_forever()systemd unit so it auto-starts on boot:
# /etc/systemd/system/plexus-bridge.service
[Unit]
Description=Plexus MQTT bridge
After=network-online.target mosquitto.service
Wants=mosquitto.service
[Service]
Type=simple
User=pi
Environment="PLEXUS_API_KEY=plx_your_key_here"
ExecStart=/home/pi/plexus-bridge/bin/python /home/pi/bridge.py
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.targetthen:
sudo systemctl daemon-reload
sudo systemctl enable --now plexus-bridge
sudo systemctl status plexus-bridgejournalctl -u plexus-bridge -f tails the logs.
same BME280_Dashboard sketch from week 1, with one extra line uncommented at the top:
#define PLEXUS_TRANSPORT_MQTT // week 3: route through the pi
const char* WIFI_SSID = "YourWiFiSSID";
const char* WIFI_PASSWORD = "YourWiFiPassword";
const char* MQTT_HOST = "192.168.1.42"; // your pi's LAN IP
const char* SOURCE_ID = "esp32-greenhouse"; // unique per deviceinstall one extra library: PubSubClient by Nick O’Leary, via Sketch → Include Library → Manage Libraries.
flash both ESP32s, each with its own SOURCE_ID (e.g., esp32-greenhouse, esp32-fridge). serial monitor shows:
WiFi connected. IP: 192.168.1.78
MQTT connected to 192.168.1.42:1883
PUB plexus/esp32-greenhouse/temperature 22.3your second terminal (running mosquitto_sub) now shows the messages arriving at the pi:
plexus/esp32-greenhouse/temperature 22.3
plexus/esp32-fridge/temperature 4.8
plexus/esp32-greenhouse/humidity 54.1both ESP32s now show up in plexus as their own sources, streaming temperature, humidity, pressure. week 2’s anomaly detector points at any of them by changing one constant.
bridge.py is plain python — add a filter in on_message to drop noisy metrics before they leave the LAN, or downsample 10 Hz traffic to 1 Hz before forwarding. saves bandwidth and storage.next week: time-series storage. how plexus stores all this, and what your options are if you want more of it locally.
bridge.py, plexus-bridge.service, the modified BME280_Dashboard.ino with the MQTT switch, and a README live in the plexus-tutorials repo on GitHub.