Building a smart inverter monitor with ESP32 and Modbus addresses one of the most important smart home needs in India — visibility into your backup power system. Power cuts are a reality across most Indian states, and knowing your inverter battery status, charge level, and estimated backup time without walking to a physical display is genuinely valuable. This project connects an ESP32 to your inverter via Modbus RS485, pulling real-time data into Home Assistant for monitoring, alerts, and smart load management during outages.
Table of Contents
- Which Indian Inverters Support Modbus?
- Required Components
- RS485 Modbus Wiring
- Reading Inverter Registers
- ESP32 Modbus Code
- Home Assistant Dashboard
- Load Management Automations
- Frequently Asked Questions
Which Indian Inverters Support Modbus?
Modbus RTU over RS485 is the most common communication protocol for solar inverters and some UPS systems. Popular Indian inverter brands with Modbus support:
- Luminous: Higher-end solar PCU models have RS485 Modbus ports
- Microtek: Solar hybrid inverters with RS485 communication
- Su-Kam: Solar inverters with Modbus RTU
- Genus: Smart solar inverters with communication port
- Growatt: Popular solar inverters with full Modbus register documentation
- Deye/Solis: Chinese solar inverters imported by many Indian installers, excellent Modbus support
Standard battery UPS inverters (Luminous, Microtek home inverters) typically do NOT have Modbus. For those, use a voltage monitoring approach with the PZEM module or a simple battery voltage divider to the ESP32 ADC.
Required Components
- ESP32 DevKit V1
- MAX485 or SP3485 RS485 transceiver module (Rs 30-50 each)
- 2-wire twisted pair cable (for RS485 bus)
- 120-ohm termination resistors
- 5V power supply
- Your inverter’s communication cable/port adapter
Total cost: Rs 300-500 plus your ESP32 and power supply.
RS485 Modbus Wiring
RS485 uses a differential signal on two wires (A and B). Most inverters provide either RJ11, RJ45, or screw terminal RS485 ports.
MAX485 Module to ESP32:
RO (Receive) -> GPIO 16 (RX2)
RE+DE (Direction) -> GPIO 4
DI (Transmit) -> GPIO 17 (TX2)
VCC -> 5V (or 3.3V depending on module)
GND -> GND
MAX485 to Inverter RS485 Port:
A (MAX485) -> A (Inverter)
B (MAX485) -> B (Inverter)
Termination: Add 120-ohm resistor
between A and B at the inverter end
if cable is longer than 10m
Reading Inverter Registers
Every inverter model has a specific Modbus register map in its communication manual. Common registers for solar inverters (Growatt example):
| Register | Parameter | Scale |
|---|---|---|
| 0x0001 | Inverter Status | Bitmap |
| 0x0004 | Grid Voltage (V) | x0.1 |
| 0x0006 | Grid Power (W) | x1 |
| 0x001A | Battery Voltage (V) | x0.1 |
| 0x001B | Battery SoC (%) | x1 |
| 0x001E | Solar Power (W) | x1 |
| 0x0056 | Load Power (W) | x1 |
ESP32 Modbus Code
#include <ModbusMaster.h>
#include <WiFi.h>
#include <PubSubClient.h>
#define MAX485_DE_RE 4
#define INVERTER_ID 1
ModbusMaster node;
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
void preTransmission() { digitalWrite(MAX485_DE_RE, HIGH); }
void postTransmission() { digitalWrite(MAX485_DE_RE, LOW); }
struct InverterData {
float gridVoltage;
float batteryVoltage;
int batterySoC;
int solarPower;
int loadPower;
int gridPower;
bool onGrid;
};
InverterData readInverter() {
InverterData data;
uint8_t result;
// Read input registers starting at 0x0001
result = node.readInputRegisters(0x0001, 20);
if (result == node.ku8MBSuccess) {
data.onGrid = (node.getResponseBuffer(0) == 1);
data.gridVoltage = node.getResponseBuffer(3) * 0.1;
data.gridPower = node.getResponseBuffer(5);
result = node.readInputRegisters(0x001A, 10);
if (result == node.ku8MBSuccess) {
data.batteryVoltage = node.getResponseBuffer(0) * 0.1;
data.batterySoC = node.getResponseBuffer(1);
data.solarPower = node.getResponseBuffer(4);
}
result = node.readInputRegisters(0x0056, 2);
if (result == node.ku8MBSuccess) {
data.loadPower = node.getResponseBuffer(0);
}
}
return data;
}
void publishToMQTT(InverterData& data) {
mqtt.publish("inverter/battery_soc", String(data.batterySoC).c_str());
mqtt.publish("inverter/battery_voltage", String(data.batteryVoltage).c_str());
mqtt.publish("inverter/solar_power", String(data.solarPower).c_str());
mqtt.publish("inverter/load_power", String(data.loadPower).c_str());
mqtt.publish("inverter/grid_voltage", String(data.gridVoltage).c_str());
mqtt.publish("inverter/on_grid", data.onGrid ? "on" : "off");
}
void setup() {
pinMode(MAX485_DE_RE, OUTPUT);
digitalWrite(MAX485_DE_RE, LOW);
Serial2.begin(9600, SERIAL_8N1, 16, 17); // RX, TX
node.begin(INVERTER_ID, Serial2);
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);
WiFi.begin("YOUR_SSID", "YOUR_PASS");
while (WiFi.status() != WL_CONNECTED) delay(500);
mqtt.setServer("192.168.30.100", 1883);
mqtt.connect("inverter-monitor");
}
void loop() {
mqtt.loop();
static unsigned long lastRead = 0;
if (millis() - lastRead > 10000) {
InverterData data = readInverter();
publishToMQTT(data);
lastRead = millis();
}
}
Home Assistant Dashboard
# configuration.yaml MQTT sensors
mqtt:
sensor:
- name: "Battery SoC"
state_topic: "inverter/battery_soc"
unit_of_measurement: "%"
device_class: battery
- name: "Solar Power"
state_topic: "inverter/solar_power"
unit_of_measurement: W
device_class: power
- name: "Load Power"
state_topic: "inverter/load_power"
unit_of_measurement: W
device_class: power
binary_sensor:
- name: "Grid Status"
state_topic: "inverter/on_grid"
payload_on: "on"
payload_off: "off"
device_class: power
Load Management Automations
The real power of inverter monitoring is automatic load management during outages:
alias: Power Cut Load Management
trigger:
- platform: state
entity_id: binary_sensor.grid_status
to: "off"
action:
# Turn off high-load non-essentials
- service: switch.turn_off
entity_id:
- switch.washing_machine
- switch.bedroom_2_ac
- switch.computer_socket
# Notify family
- service: notify.family_group
data:
message: >-
Power cut! Battery at {{ states('sensor.battery_soc') }}%.
Estimated backup: {{ (states('sensor.battery_soc') | int * 1.2) | round }} minutes.
alias: Low Battery Alert
trigger:
- platform: numeric_state
entity_id: sensor.battery_soc
below: 30
action:
- service: notify.mobile_app
data:
message: "Inverter battery at {{ states('sensor.battery_soc') }}%. Reduce loads!"
Frequently Asked Questions
My home inverter does not have RS485 Modbus. How can I monitor it?
For standard home inverters without Modbus, monitor battery voltage using a voltage divider connected to the ESP32 ADC pin. Battery state-of-charge can be estimated from voltage (12.7V = 100%, 12.0V = 50%, 11.8V = 20% for lead-acid). This is less accurate than Modbus but provides useful monitoring without hardware modification.
What is the baud rate for inverter Modbus communication?
Most inverters use 9600 baud, 8N1 (8 data bits, no parity, 1 stop bit). Some older models use 4800 baud. Check your inverter’s communication manual. The Growatt, Deye, and Solis inverters used widely in India all default to 9600 baud.
Can I monitor multiple inverters with one ESP32?
Yes. RS485 is a multi-drop bus supporting up to 32 devices. Configure each inverter with a unique Modbus device ID (address). The ESP32 polls each inverter sequentially using its specific address. This is common in commercial solar installations with multiple string inverters.
Is this setup compatible with Luminous solar inverters popular in India?
Higher-end Luminous solar PCUs (Cruze, NXG series) have RS485 ports. The Modbus register map varies by model – contact Luminous technical support for the register documentation. The ESP32 Modbus implementation described here works with any Modbus RTU inverter once you have the correct register addresses.
Add comment