Sitemap

Building a Real-Time Event-Driven IoT Application with Solace, MQTT and Snowflake

9 min readJul 18, 2026

--

Press enter or click to view image in full size

Objective

I decided to set up a real-time IoT application in my own ‘backyard’, which, in my case, is my balcony. It started out as a simple setup, a moisture sensor, ESP32, connected to Solace. But ended up building a small command-and-control system for my plants.

What it does, end to end:

  • Monitors soil moisture in real time
  • Sends me a mobile alert whenever moisture drops below a threshold
  • Displays live readings in real-time on a dashboard
  • Loads real time events into Snowflake for historical analysis
  • Lets me trigger and clear alerts manually, to simulate real operational scenarios

It turned out to be a fun weekend project, and a demo of how an event-driven architecture works in practice, how IoT telemetry can be captured, distributed, visualised, and stored for analytics, all from a handful of independent, decoupled components.

Although the use case is simple, the architecture behind it is broadly applicable.

At the centre of the demo is Solace PubSub+, acting as the event broker. Sensors and simulators publish moisture readings and alert events using MQTT. Multiple downstream consumers then react to those events independently.

Problem Statement

I have quite a few plants, and each one has its own personality. Some need watering every couple of days, others need to dry out completely between waterings. Overwatering and underwatering are both bad for the plants, and the only way to know is to physically check it.

That doesn’t scale. Checking multiple disparate plants periodically is a bit tedious. And manual checks only tell you the moisture level at the moment you looked, they miss anything that happens between checks, which is exactly when a fast-draining pot dries out.

This is a common pattern outside of houseplants too, any environment where a condition needs to be watched continuously and acted on quickly benefits from automatic detection and event-driven alerting rather than someone periodically checking by hand.

The Solution

The demo uses a simple and relatable scenario, monitoring soil moisture levels for my plants. However, the architecture behind it is far more broadly applicable. At the centre of the demo is Solace PubSub+, acting as the event broker. Solace provides the event distribution layer that allows each component to publish or subscribe independently using topics. Sensors publish moisture readings and alert events using MQTT. Multiple downstream consumers then react to those events independently. The goal was to demonstrate how event-driven architecture can turn raw sensor readings into real-time operational visibility.

For the demo, I used three plants:

  • Peace lily, monitored by a real ESP32 with a capacitive soil moisture sensor
  • Basil and mint, a Python simulator generates real time readings.

The ESP32 is wired to a capacitive soil moisture sensor via its ADC pin. When the moisture level falls below the threshold, the ESP32 sends the low-moisture event in real time to Solace over MQTT.

From there, PubSub+ distributes that event in real time to whoever is interested, a dashboard, a messaging app on my phone, or an automation system, with no direct connection between the sensor and any of them.

The dashboard captures real time events sent by the various sensors. The dashboard includes controls to simulate operational scenarios, to trigger a low-water event and restore the plant back to a normal range. These events are sent to the Solace broker, meaning the dashboard does not directly call the simulator, it receives the invents through Solace.

Press enter or click to view image in full size
Press enter or click to view image in full size

Architecture Overview

Press enter or click to view image in full size

The architecture follows a simple, decoupled event-driven flow:

1.Producers publish whenever an event takes place. But they do not push directly to any specific consumer. The ESP32 and the Python simulator publish moisture readings and threshold-breach events to Solace PubSub+ over MQTT. Neither one knows or cares who’s listening.

2.Solace PubSub+ sits in the middle as the event broker. It receives every published event and routes it based on topic subscriptions. There are no direct connections between producers and consumers anywhere in the system.

3.Multiple consumers subscribe independently, each for their own reason:

  • The dashboard subscribes over MQTT/WebSocket to iot/plant/moisture/# for live visualisation.
  • A Telegram alert subscriber subscribes to iot/plant/moisture/thresholdBreached/# to push mobile notifications.
  • A Snowflake Ingestion subscribes to the full moisture stream and writes readings and alerts into two tables for historical analysis.

4.Historical data flows: The dashboard calls a small FastAPI service (/api/moisture-trend), which queries Snowflake.

5.Control flows: When the button is clicked, it publishes a control event to Solace, just like a sensor would.

Protocols

Solace supports multi-protocol messaging. Solace PubSub+ supports open protocols and APIs including MQTT, AMQP, REST/HTTP, WebSocket and JMS, allowing different types of clients to communicate through the same broker. In this demo, the ESP32 publishes using MQTT, while the browser dashboard consumes events using MQTT over WebSocket.

Several protocols are used in the demo, depending on where the component sits in the architecture.

MQTT over TLS. The ESP32 and Python simulator publish events to Solace using MQTT. The ESP32 uses MQTT over TLS because it is a device-to-broker connection. This is common for IoT devices because MQTT is lightweight and works well for telemetry-style messaging. You will need to install the MQTT library for ESP32, ‘PubSubClient by Nick O’Leary’ . The Eclipse Paho Python client is an MQTT client library for Python and supports MQTT publish/subscribe patterns.

The ESP32 publishes events such as:

iot/plant/moisture/measured/v1/nominal/peace-lily/balcony/home
iot/plant/moisture/thresholdBreached/v1/low/peace-lily/balcony/home
Press enter or click to view image in full size
ESP32 reading moisture levels from the sensor and publishes the event to Solace brokers

MQTT over WebSocket. The dashboard runs in the browser, so it connects to Solace using MQTT over WebSocket.This is important because browser applications cannot typically open raw MQTT/TLS connections in the same way that an ESP32 or Python client can. MQTT over WebSocket allows the dashboard to subscribe to Solace topics directly from the browser. The dashboard subscribes broadly to iot/plant/moisture/#. This allows it to receive all moisture readings, alerts and status updates in real time.

HTTPS / REST. The dashboard also needs historical trend data from Snowflake. Instead of connecting directly from the browser to Snowflake, I used a FastAPI service. FastAPI is a modern, developer-friendly framework for creating HTTP-based REST APIs with Python. In this demo, FastAPI acts as a lightweight API layer that retrieves selected historical records from Snowflake and returns them to the dashboard. The dashboard calls a REST endpoint such as /api/moisture-trend. The API queries Snowflake and returns historical data to the dashboard. This keeps Snowflake credentials out of the browser and gives the backend control over query logic and data shaping.

MQTT topic design

Another key part of this demo is the MQTT topic design. The topic hierarchy is not just a naming convention, it defines how events are routed, filtered, discovered and governed across the architecture.

Get Taresh Mulky’s stories in your inbox

Join Medium for free to get updates from this writer.

The best practice for an event topic taxonomy is based on the following pattern

Domain / Noun / Verb / Version / Properties

For regular moisture telemetry, the topics:

iot/plant/moisture/measured/v1/nominal/peace-lily/balcony/home
iot/plant/moisture/measured/v1/nominal/basil/kitchen/home
iot/plant/moisture/measured/v1/nominal/mint/window/home

When the moisture level drops below the configured threshold, the system publishes a separate threshold-breach event

iot/plant/moisture/thresholdBreached/v1/low/peace-lily/balcony/home
iot/plant/moisture/thresholdBreached/v1/low/basil/kitchen/home
iot/plant/moisture/thresholdBreached/v1/low/mint/window/home

The topic should describe what business object the event is about, what happened to it, and which properties are useful for routing and filtering. The topic clearly communicates that the event is about plant moisture, and that the specific event is a threshold breach. This makes the topic easier to understand, govern and reuse.

The real advantage is how easily a consumer can just filter on a particular topic. The Telegram subscriber, for example, only cares about breaches so it subscribes just to iot/plant/moisture/thresholdBreached/#

Event payload design

Each moisture reading and each alert is published as a JSON event. The topic carries the routing information (which plant, which event type); the payload carries the detail (the actual reading, the threshold that was crossed).

For example, the payload includes details such as the plant ID, plant name, location, moisture percentage, threshold percentage, status and event timestamp. The topic is used for routing and filtering, while the payload carries the data that consumers need to display, alert on, or store for analysis.

{
"plantId": "basil-001",
"plantName": "Basil",
"location": "Kitchen",
"rawValue": 2381,
"moisturePercent": 26,
"thresholdPercent": 35,
"status": "LOW_WATER",
"deviceId": "python-plant-simulator",
"message": "Basil needs watering",
"eventTime": "2026-07-16T04:11:43Z"
}

Writing events into Snowflake

For the sake of the demo, a Python subscriber listens to the moisture event streams from Solace and writes the events into Snowflake. The demo stores data in two main tables, PLANT_MOISTURE_READINGS and PLANT_WATER_ALERTS. Regular moisture readings are written into the readings table. Low-water alerts are written into the alerts table. This allows the system to support both operational and analytical use cases.

PLANT_WATER_ALERTS

Press enter or click to view image in full size

PLANT_MOISTURE_READINGS

Press enter or click to view image in full size

Command and control flow

Plant moisture levels don’t fluctuate that much, hence to simulate low water levels (or faults in other IoT devices) the two buttons provide the control. Trigger Low Water Alert and Restore Normal. When a user clicks Trigger Low Water Alert, the dashboard does not call the simulator directly. Instead, it publishes an event to Solace. A python subscribes to that specific topic. When it receives it, the moisture values are reduced and a new alert event is published.

Press enter or click to view image in full size

Real-world application

Real-time IoT events involve streaming data from connected devices to trigger instantaneous actions. Although this demo uses plant moisture monitoring as the scenario, the architecture represents a broader real-time event-driven pattern. Any sensor, device, or operational system can publish events into Solace. From there, multiple consumers, analytics platforms & automation workflows can react independently.

The same pattern applies directly to:

  • Industrial equipment monitoring and predictive maintenance
  • Cold-chain logistics, flagging temperature spikes in perishable goods
  • Railway operations
  • Healthcare device monitoring
  • many others..

Next Steps

In the next iteration, I would probably add the following:

  • Solace Micro-Integration for Snowflake
  • Solace Agent Mesh

Solace Micro-Integration to Snowflake

This would push events directly from Solace PubSub+ into Snowflake

Solace Agent Mesh

Currently, the demo captures telemetry, detects threshold breaches, sends alerts, updates a dashboard and stores historical data. The next step would be to include event driven AI agents through Solace Agent Mesh. This would evolve the demo from a real-time IoT monitoring application into an intelligent event-driven system that can detect, analyse and respond to operational conditions.

So instead of sending a basic message like:

Basil needs watering. Moisture is 22%.

SAM would receive the plant event and generate a more useful recommendation:

Basil is below its moisture threshold at 22%.
Suggested action:
Water lightly within the next few hours.
Since this is only slightly below the 30% threshold, avoid overwatering.
Check again after 30 minutes

Or if I collect more data over time:

Basil has dropped below threshold three times this week. 
This may indicate that the plant is in a hot area,
the pot is too small, or the soil is drying too quickly

That is where Agentic AI with SAM starts adding value: not just alerting, but interpreting, recommending, and possibly coordinating follow-up actions.

I could have multiple AI agents that SAM orchestrates:

  • Plant Health Agent: Interprets moisture events, for example, “This plant is drying faster than usual.”
  • Notification Agent: Sends an alert only if the low-moisture condition persists for more than 10 minutes.
  • Trend Analysis Agent: Compares recent readings and identifies whether moisture is dropping faster than usual.

Solace enables real-time agentic workflows, where agents can subscribe to events, reason with context, and publish new events back into the system.

Conclusion

This demo started with a simple question: how can I monitor soil moisture levels in real time? In the end, it became a broader demonstration of event-driven architecture. This is one plant I won’t have to worry about :-)

Press enter or click to view image in full size

--

--