EnviroLink

9 July 2026

Project overview

EnviroLink is a connected IoT system that monitors greenhouse conditions in real time using a Raspberry Pi with a SenseHAT, and combines live indoor readings with outdoor weather data from the OpenWeather API.

It provides users with a unified view of both their indoor environment and outdoor forecast, enabling smarter decisions like opening vents when outdoor conditions are favourable, or alerting when frost is forecast.

The system features 24-hour historical trend charts, a real-time updating dashboard, colour-coded alerts based on a greenhouse rules engine, and visual feedback directly on the SenseHAT LED matrix.

Architecture

  • Sensor/Edge Layer: Raspberry Pi and SenseHAT take in Temperature, Humidity and Pressure readings. MQTT publishes these readings over TLS. The RPi also shows LED visual alerts.
  • Network Layer: HiveMQ Cloud MQTT Broker routes messages to backend
  • Processing Layer: Backend service subscribes to the MQTT broker and routes messages through the rules engine. The external weather API supplies current and 24hr forecasts. Readings, weather data, and alerts are all saved to a MongoDB collection.
  • Application Layer: The Web Dashboard has live weather readings and charts, colour-coded alerts and historical trend graphs. The SenseHAT LED Matrix also shows the colour-coded alerts.

Design decisions

Concurrency: MQTT and Flask needed to run in the same process, but loop_forever() blocks the thread indefinitely, which would prevent Flask from ever starting. Using loop_start() instead runs the MQTT network loop in a background thread, freeing the main thread for Flask.

Merging main.py and app.py: These were originally kept separate for clean separation of concerns, but were merged once WebSockets were introduced via Flask-SocketIO, both files needed access to the same socketio instance and shared sensor state, which a shared process makes far simpler.

MongoDB Atlas over local MongoDB: Atlas allows the dashboard to work remotely rather than being limited to the local network. This surfaced an early SSL error caused by whitelisting the local IP instead of the public one; a static IP was set afterwards to stop the whitelist breaking on reconnect.

OpenWeather by coordinates, not city name: Querying by city name initially returned data for “County Wicklow” rather than the actual local area. Switching to latitude/longitude coordinates fixed the mismatch, a reminder that debugging data accuracy issues sometimes means questioning the input, not just the output.

os.environ[] over os.getenv(): os.getenv() returns None silently if a key is missing, while os.environ[] raises a KeyError immediately. The fail-fast approach was chosen deliberately, it’s better to crash on startup with a clear error than fail silently at runtime (Python os docs).

UTC storage for timestamps: All readings are stored in UTC in MongoDB and converted to local time only at the display layer, using pytz and astimezone(). Storing in UTC avoids DST bugs and keeps timezone-dependent queries consistent.

Configurable thresholds in MongoDB: Thresholds are stored in a MongoDB document rather than a static file, allowing runtime changes via the dashboard UI without redeploying or editing code. This uses find_one() / update_one() with $set.

--system-site-packages for the virtual environment: RTIMU, a SenseHAT dependency, can’t be pip-installed. This flag gives the venv access to system packages so the SenseHAT library can import correctly.

← Back to all projects