Overview
I designed and tested an IoT sensor monitoring system using a Raspberry Pi as the local controller and ESP32 microcontrollers as edge sensor nodes. The system tracks environmental triggers (motion and entry sensors) and streams readings over a private Wi-Fi network with basic security controls.
Goal
Build an integrated hardware-software security monitoring prototype to:
- Collect telemetry and alert events from distributed ESP32 sensor nodes.
- Route sensor data reliably to a central controller using lightweight messaging (MQTT).
- Enforce network and device-level controls to prevent unauthorized nodes from injecting telemetry.
- Maintain a local event audit log on the controller for investigation.
Hardware & Environment
- Controller: Raspberry Pi 4 Model B (4GB RAM) running Raspberry Pi OS (Debian 12).
- Edge Node 1: ESP32 DevKit V1 with PIR motion sensor and DHT11 temperature/humidity module.
- Edge Node 2: ESP32 with magnetic reed switch (door/window state sensor).
- Network: Dedicated 2.4 GHz WPA2-Personal Wi-Fi access point separated from host workstations.
- Broker: Mosquitto MQTT broker 2.0 running locally on the Raspberry Pi controller.
System Architecture
+-------------------+ +--------------------+
| ESP32 Edge Node | | ESP32 Edge Node |
| (PIR / DHT11) | | (Reed Switch) |
+---------+---------+ +---------+----------+
| |
| MQTT over Wi-Fi | MQTT over Wi-Fi
| (Topic: lab/sensors/motion) | (Topic: lab/sensors/door)
v v
+------------------------------------------------------+
| Raspberry Pi Central Controller |
| - Mosquitto MQTT Broker (Auth & ACL enabled) |
| - Python Event Daemon (telemetry processor) |
| - SQLite Event Database (timestamped audit log) |
+---------------------------+--------------------------+
|
v
+----------------------------+
| Local Alert Console / Log |
+----------------------------+
Security Controls Implemented
Rather than leaving IoT defaults intact, I implemented the following controls:
1. MQTT Authentication and Topic Isolation (ACLs)
By default, Mosquitto allows anonymous connections. I disabled anonymous access and created separate user accounts with restricted topic write permissions:
# /etc/mosquitto/conf.d/security.conf
allow_anonymous false
password_file /etc/mosquitto/passwd
acl_file /etc/mosquitto/acl
# ACL rules:
user node_motion
topic write lab/sensors/motion
user node_door
topic write lab/sensors/door
user controller_srv
topic read lab/sensors/#
This ensured that if one ESP32 node was compromised, it could not publish fake door status events or listen to traffic on other nodes' channels.
2. Device Credential Management
Credentials (Wi-Fi SSID/password and MQTT user/secret) were kept out of public git repositories by storing them in a non-committed secrets.py file on the ESP32 filesystem and loading them into memory during boot.
3. Local Subnet Isolation
The IoT devices were connected to a dedicated Wi-Fi SSID with client isolation enabled, preventing lateral communication between sensor nodes.
Event Processing & Logging
The controller runs a Python service that consumes incoming messages, validates the payload structure, and writes to an audit log:
$ tail -f /var/log/iot_security/events.log$ [2024-04-12 14:22:01] [INFO] MQTT client 'node_door' connected from 192.168.12.102$ [2024-04-12 14:22:04] [ALERT] Topic: lab/sensors/door | State: OPEN | SensorID: ESP32-REED-01$ [2024-04-12 14:22:05] [ALERT] Topic: lab/sensors/motion | State: MOTION_DETECTED | SensorID: ESP32-PIR-01$ [2024-04-12 14:22:18] [INFO] Topic: lab/sensors/door | State: CLOSED | SensorID: ESP32-REED-01$ [2024-04-12 14:25:40] [WARN] Unauthorized publish attempt on topic 'lab/admin' from client 'node_motion' - Permission Denied
Problems Encountered & Fixes
1. Wi-Fi Disconnects and MQTT Socket Leaks
- Problem: When the Wi-Fi signal dropped intermittently, the ESP32 MicroPython loop tried to publish without checking connection state, causing memory allocation errors (
ENOMEM) and crashing the microcontroller. - Fix: Wrapped the MQTT publish routine in a connection heartbeat check with exponential backoff reconnection logic. If disconnected, sensor readings are cached in a ring buffer of 10 items until reconnected.
2. Physical Sensor Bouncing
- Problem: The mechanical reed switch generated 4–6 rapid open/close trigger events per single physical door action due to switch bounce.
- Fix: Implemented a 300ms software debounce timer in the ESP32 firmware before triggering state change events.
Limitations
- Home Lab Setup: Tested within a controlled room setup rather than an industrial facility.
- Plaintext MQTT vs TLS: In this test phase, MQTT messages inside the isolated private Wi-Fi used standard TCP (port 1883) rather than MQTTS (TLS 8883) due to memory constraints and self-signed certificate overhead on early ESP32 test firmware. Upgrading to TLS is planned for the next iteration.
What I Learned
- IoT security requires addressing both physical constraints (memory, power, sensor bounce) and network controls (authentication, segmentation, ACLs).
- Isolating message topics at the broker layer is essential to enforce least-privilege on microcontrollers.
- Log aggregation is only as good as the timestamps; synchronizing NTP on both the controller and sensor nodes is required for reliable forensics.