IoT Automation

HVAC Monitoring with IoT Sensors and MQTT Alerts

Which HVAC signals are worth measuring, how to publish them over MQTT, and how to turn delta-T, filter pressure and runtime into early fault alerts with a cloud broker.

Rooftop air handling unit fitted with temperature, pressure and current sensors streaming data in an hvac monitoring iot setup
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

HVAC monitoring with IoT sensors gives you a continuous view of how air handlers, fan coils and rooftop units actually behave, not just whether they are switched on. This use case shows which measurements catch the most common faults, how to publish them over MQTT, and how to turn them into alerts and runtime reports with a cloud broker.

What Goes Wrong in HVAC Equipment

Most HVAC problems do not announce themselves with an alarm. They show up as slow drifts: a filter loads up and airflow drops, a valve sticks and coil performance falls, a compressor short-cycles because of a bad thermostat location, or a unit runs all weekend because a schedule was overridden and never reset. Occupants notice only once comfort suffers, and by then energy has been wasted for weeks.

A building management system can detect some of this, but many sites have no BMS at all, or have one that only a contractor can access. Retrofitting a few IoT sensors and publishing to MQTT fills the gap quickly and works the same across a whole portfolio, whether the unit is a rooftop package unit at a retail store or an air handler in an office tower.

The Signals That Matter

You do not need dozens of points per unit. The measurements below cover most practical fault detection:

Signal Sensor What it reveals
Supply air temperature Duct probe (DS18B20, PT1000 with transmitter) Whether the unit is actually heating or cooling
Return air temperature Duct probe Load on the unit, with supply gives delta-T
Filter differential pressure Low-range differential pressure sensor, 0 to 500 Pa Filter loading, missing filter, belt failure
Fan or compressor current Split-core current transformer Runtime, short cycling, mechanical load changes
Outdoor air temperature Shielded probe or weather feed Context for heating and cooling demand
Zone temperature Wall sensor Whether the space is comfortable

The most useful derived value is delta-T, the difference between return and supply air. When a cooling unit runs with a very small delta-T, either the coil is not getting chilled water or refrigerant, the airflow is far too high, or a sensor is in the wrong place. A filter pressure that climbs steadily over weeks is normal; one that suddenly drops toward zero means the filter has collapsed or is missing.

Architecture

There are two common ways to get these signals onto MQTT:

  1. Existing controllers. If the units already have BACnet controllers, a gateway reads the points and publishes them, as shown in our BACnet to MQTT tutorial.
  2. Retrofit sensor nodes. An ESP32 or a small industrial IoT module per unit reads the probes directly and publishes over Wi-Fi or Ethernet. The ESP32 MQTT tutorial covers the connection basics.

In both cases the node publishes outbound to a broker, and every consumer subscribes: the portal topic explorer for commissioning, an analytics script for derived values, and a dashboard for facility staff. Topic structure follows the building hierarchy:

acme/ist01/tower-a/roof/ahu-2/telemetry
acme/ist01/tower-a/roof/ahu-2/derived
acme/ist01/tower-a/roof/ahu-2/status

A single telemetry message per interval keeps traffic small and makes time alignment easy:

{
  "ts": "2026-10-16T10:30:00Z",
  "supply_c": 13.6,
  "return_c": 23.1,
  "outdoor_c": 27.4,
  "filter_dp_pa": 182,
  "fan_a": 6.8,
  "comp_a": 14.2,
  "mode": "cooling"
}

Computing Delta-T and Runtime from MQTT

Derived values are best computed in one place rather than in each sensor node. The script below subscribes to every unit's telemetry, calculates delta-T, accumulates fan and compressor runtime, counts compressor starts per hour to detect short cycling, and publishes the results back to MQTT. Try it on the free public MQTT broker with the demo prefix, then switch to private credentials.

import json
import time
import uuid
from collections import defaultdict, deque

import paho.mqtt.client as mqtt

BROKER, PORT = "broker.connectmq.com", 1883
PREFIX = "connectmq/demo/hvac-q81z"
FAN_ON_A = 1.0          # current above this means the fan runs
COMP_ON_A = 3.0         # current above this means the compressor runs
MAX_STARTS_PER_HOUR = 6

state = defaultdict(lambda: {"last_ts": None, "fan_s": 0.0, "comp_s": 0.0,
                             "comp_on": False, "starts": deque()})


def on_connect(client, userdata, flags, reason_code, properties):
    print("connected:", reason_code)
    client.subscribe(f"{PREFIX}/+/telemetry", qos=0)


def on_message(client, userdata, msg):
    unit = msg.topic.split("/")[-2]
    try:
        t = json.loads(msg.payload)
    except json.JSONDecodeError:
        return
    s = state[unit]
    now = time.time()

    if s["last_ts"] is not None:
        dt = min(now - s["last_ts"], 120)      # cap gaps so outages do not inflate runtime
        if t.get("fan_a", 0) > FAN_ON_A:
            s["fan_s"] += dt
        if s["comp_on"]:
            s["comp_s"] += dt
    s["last_ts"] = now

    comp_on = t.get("comp_a", 0) > COMP_ON_A
    if comp_on and not s["comp_on"]:
        s["starts"].append(now)
    s["comp_on"] = comp_on
    while s["starts"] and now - s["starts"][0] > 3600:
        s["starts"].popleft()

    delta_t = None
    if "return_c" in t and "supply_c" in t:
        delta_t = round(t["return_c"] - t["supply_c"], 2)

    derived = {
        "ts": t.get("ts"),
        "delta_t_c": delta_t,
        "fan_runtime_h": round(s["fan_s"] / 3600, 2),
        "comp_runtime_h": round(s["comp_s"] / 3600, 2),
        "comp_starts_last_hour": len(s["starts"]),
        "short_cycling": len(s["starts"]) > MAX_STARTS_PER_HOUR,
    }
    client.publish(f"{PREFIX}/{unit}/derived", json.dumps(derived), qos=1, retain=True)


client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"hvac-analytics-{uuid.uuid4().hex[:6]}")
client.on_connect = on_connect
client.on_message = on_message
client.connect(BROKER, PORT, keepalive=60)
client.loop_forever()

Simulate a unit from the command line to see it work:

mosquitto_pub -h broker.connectmq.com -p 1883 -i hvac-sim-q81z \
  -t "connectmq/demo/hvac-q81z/ahu-2/telemetry" \
  -m '{"ts":"2026-10-16T10:30:00Z","supply_c":13.6,"return_c":23.1,"filter_dp_pa":182,"fan_a":6.8,"comp_a":14.2}'

The runtime counters here live in memory, so they reset when the script restarts. For billing-grade runtime, write the derived values to a time-series database from the same subscriber, or let a tool like Telegraf subscribe to the topics and store them.

Threshold Alerts for HVAC Faults

ConnectMQ lets you define threshold alert rules on incoming values in the portal, and every triggered alert is kept in the alerts log. Practical starting rules for a cooling air handler:

Rule Condition Probable cause
Low delta-T while cooling delta_t_c below 4 for 30 minutes No chilled water, stuck valve, excessive airflow
Filter loaded filter_dp_pa above the filter's final pressure drop Filter replacement due
Filter missing or belt broken filter_dp_pa below 20 while the fan runs Collapsed filter, slipped belt
Supply air too warm supply_c above 18 in cooling mode Compressor fault, refrigerant loss
Short cycling comp_starts_last_hour above 6 Oversized unit, thermostat placement, control fault
Off-hours operation fan current above threshold outside schedule Overridden schedule

Tune thresholds per unit after a week of observation. Equipment, climate and filter class vary too much for universal numbers, and the manufacturer data sheet gives the right filter pressure limits.

Storing History and Reporting

Live values catch faults; history proves savings and guides maintenance. Because every consumer is just another MQTT client, adding storage does not touch the sensor nodes. Common choices are a Telegraf instance with its MQTT consumer input writing to InfluxDB, a Node-RED flow inserting into PostgreSQL, or a small Python subscriber writing to whatever database your organization already runs. Store raw telemetry at the native interval for a few months and keep hourly aggregates for years.

Useful monthly reports for facility teams include runtime hours per unit compared with scheduled hours, the number of hours each unit spent with a low delta-T, the date each filter crossed its replacement pressure, and units ranked by compressor starts. These numbers turn vague complaints such as "the third floor is always warm" into specific work orders.

Rollout Checklist

  1. Choose three to five representative units, not the whole portfolio.
  2. Mount supply probes far enough downstream of the coil for mixed air, and return probes before the mixing box.
  3. Fit current transformers on a single conductor, not the whole cable.
  4. Commission each sensor by watching live values in the topic explorer while the unit cycles.
  5. Run the analytics subscriber and observe for a week before enabling alerts.
  6. Write a short runbook for each alert so technicians know what to check.
  7. Expand to the rest of the units once false alarms are under control.

Scaling Up

A single retrofit node per unit and one analytics service fit easily into the free Serverless plan for a pilot. As you add buildings, connection count grows linearly with units, and the paid Serverless tier supports up to 1,000 connections with shared subscriptions, so you can run several analytics workers that split the load. Because the broker is a cloud MQTT broker you do not install anything in each building except the sensor nodes.

Air quality and occupancy add valuable context to HVAC data: see indoor air quality monitoring and office occupancy sensors. The broader picture is covered in building automation system MQTT and the IoT automation guide.

Start Monitoring Your HVAC with ConnectMQ

Create a free Serverless broker at app.connectmq.com, no credit card required. Connect a sensor node, watch the values in the live topic explorer, then add threshold alert rules for delta-T and filter pressure. ConnectMQ runs the broker so your team can focus on the equipment.

Frequently Asked Questions

What is the single most useful HVAC measurement?

Supply and return air temperature together, because delta-T shows whether the unit is actually transferring heat. Add filter differential pressure next.

Do I need a BMS for HVAC monitoring with IoT sensors?

No. Retrofit sensor nodes can publish directly to MQTT. If a BMS exists, a gateway can reuse its points instead of installing new sensors.

How often should HVAC telemetry be sent?

Every 30 to 60 seconds is enough for temperatures and pressures. Current measurements for runtime and short cycling benefit from 10 to 15 second intervals.

Can alerts be sent from the broker itself?

ConnectMQ evaluates threshold alert rules and records them in the alerts log. For SMS, email or ticketing, run a subscriber such as Node-RED that reacts to the relevant topics.

Related articles

Zigbee2MQTT Setup Guide: From Coordinator to Dashboard

Install Zigbee2MQTT with Docker, configure the coordinator and MQTT connection, pair and rename devices, control them over MQTT and view their states on a dashboard, testing first on a free public broker.

7 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.