Implementing HVAC System Monitoring via Mobile Applications
HVAC controllers (Danfoss, Honeywell, Daikin VRV) rarely speak the same protocol. One facility may use Modbus RTU over RS-485 for air handlers, BACnet/IP for chillers, and proprietary protocol for Daikin fan coils. Mobile applications work with normalized data via API gateway, but developers must understand where numbers come from to interpret and display them correctly.
Key Parameters and Sources
Minimal climate monitoring set:
| Parameter | Source | Unit | Frequency |
|---|---|---|---|
| Supply/Return Temperature | PT1000/NTC sensors on pipes | °C | 30 sec |
| Zone Air Temperature | Room sensor or thermostat | °C | 1 min |
| Humidity | SHT31 or HIH6130 in zone | %RH | 1 min |
| Setpoint | Controller | °C | on change |
| Compressor State | Discrete input | on/off | on change |
| COP (Efficiency) | Calculated on backend | — | 5 min |
Critical detail: Modbus temperature often arrives as signed int16 in 0.1°C units. If controller sends 0xFF9C, this isn't 65436°C — it's -100, meaning -10.0°C. Incorrect interpretation is the classic bug: "sensor shows -3200°C".
fun parseModbusTemperature(rawValue: Int): Double {
// Convert unsigned 16-bit to signed
val signed = if (rawValue > 32767) rawValue - 65536 else rawValue
return signed / 10.0
}
Android Implementation: Polling via Retrofit + Coroutines
Gateway (Node-RED or custom Go service) provides REST API. Polling with adaptive interval — aggressive when app is foreground, rare in background:
class HvacPollingService(
private val api: HvacApi,
private val repository: HvacRepository,
) {
private var pollingJob: Job? = null
fun startPolling(scope: CoroutineScope, foreground: Boolean) {
pollingJob?.cancel()
val interval = if (foreground) 15_000L else 60_000L
pollingJob = scope.launch {
while (isActive) {
try {
val data = api.getHvacStatus()
repository.update(data)
} catch (e: IOException) {
// Log, don't crash — gateway connection loss is normal
}
delay(interval)
}
}
}
}
For MQTT-gateway objects, use org.eclipse.paho.client.mqttv3. Topics by zones: hvac/{buildingId}/{unitId}/temperature, hvac/{buildingId}/{unitId}/setpoint.
Trends and History
Temperature graph for a week is mandatory. For long periods, request aggregated data from server (avg/min/max by hourly intervals), don't fetch raw 30-second records. Render via MPAndroidChart (Android) or fl_chart (Flutter):
LineChartData buildTemperatureChart(List<TemperatureReading> history) {
return LineChartData(
lineBarsData: [
LineChartBarData(
spots: history.asMap().entries.map((e) =>
FlSpot(e.key.toDouble(), e.value.temperature)).toList(),
isCurved: true,
color: Colors.blue,
dotData: FlDotData(show: false),
),
],
titlesData: FlTitlesData(
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) =>
Text(formatHour(history[value.toInt()].timestamp)),
),
),
),
);
}
Range Alerts
Temperature exceeds range — needs notification. Configure rules on server (Node-RED or TimescaleDB continuous aggregates); push comes via FCM. In app, store alert history locally in Room/SQLite — user should see when and what happened, even if notification was swiped.
Developing HVAC monitoring app with real-time data, historical trends and alerts: 4–6 weeks. Cost calculated individually after analyzing controller protocols and monitoring point count.







