Build a solar potential sensor for Home Assistant
An inverter tells me what my panels are producing, but not what they could produce while the battery is full, loads are low, or the inverter is clipping. A small solar cell, an INA219, and an ESP32 make a useful reference sensor for that missing number.
This is not a laboratory pyranometer and it does not measure the main array electrically. The small cell acts as a reference: its current rises roughly with the sunlight falling on it. After calibration against the real array, ESPHome turns that current into an estimate of available array power and exposes it to Home Assistant.
Electrical boundary: this project connects only to an isolated low-voltage reference cell. Do not connect the INA219 or ESP32 to the PV string, inverter DC terminals, mains wiring, or protective earth. Those circuits need appropriately rated equipment and a qualified installer.
What the number means
I call the result solar potential: the approximate power the full array could deliver under the sunlight that reaches the reference cell. It can reveal curtailed production, estimate otherwise wasted sunshine, and help Home Assistant decide when flexible loads are worth running.
It is still an estimate. Cell temperature, dirt, snow, partial shade, spectral differences, panel aging, array orientation, inverter efficiency, and clipping all introduce error. “Maximum” here means the best calibrated estimate under current outdoor conditions—not the array's nameplate power under laboratory Standard Test Conditions.
Parts
- an ESP32 development board with USB power;
- a small monocrystalline “5 V” solar cell, approximately 100–200 mA at its rated operating point;
- an INA219 breakout, normally with a 0.1 Ω onboard shunt;
- a 10 Ω, 2 W flameproof or ceramic resistor;
- an IP65 or better UV-resistant enclosure, cable glands, hookup wire, ferrules or soldered joints, and strain relief;
- a regulated 5 V USB supply installed in a dry, code-compliant location.
The resistor is deliberately overrated. At 200 mA it dissipates I²R = 0.4 W, but sunshine, component tolerances, and a larger replacement cell can raise that. Before assembly, calculate I²R with the cell's specified short-circuit current and keep plenty of margin. With a 0.1 Ω INA219 shunt, 200 mA adds only about 20 mV of drop.
A 10 Ω load is a practical starting point for this size of cell, not a universal value. It keeps the cell nearer its current-source region than an open circuit does. Confirm on the bench that the expected current stays below the cell, resistor, wiring, and INA219 breakout ratings.
Mount the reference cell correctly
Placement matters more than the code. Fix the reference cell in the same plane as the main panels: the same azimuth and tilt, with an unobstructed view of the sky. Even a small angle error changes the reading when the sun is low. Avoid shadows from rails, chimneys, antennas, leaves, and the enclosure itself.
The reference cell should experience weather similarly to the array, but the electronics should not bake behind it. Mount the INA219 and ESP32 in the shaded enclosure, use downward-facing drip loops and glands, and keep the hot resistor away from plastic and the temperature-sensitive cell. Do not seal a wet assembly; condensation needs to be prevented with proper glands and a suitable enclosure vent or desiccant strategy.
Many cheap mini panels are only resin-coated, not permanently weatherproof. Seal exposed solder pads and edges with a material suitable for outdoor electronics without covering the active face. Check the panel after the first rain and periodically for haze, corrosion, cracked encapsulation, water ingress, dirt, and insect nests.
Wire the low-voltage circuit
Disconnect USB power and cover the reference cell while wiring. The current path is a closed loop:
reference cell + ── INA219 VIN+ [shunt] VIN− ── 10 Ω resistor ── reference cell −
│
reference cell − ─────────────────────┴── ESP32 GND
ESP32 3V3 ── INA219 VCC ESP32 GPIO21 ── INA219 SDA
ESP32 GND ── INA219 GND ESP32 GPIO22 ── INA219 SCL
- Connect the cell positive lead to
VIN+(sometimes markedV+) on the INA219. - Connect
VIN−orV−to one end of the 10 Ω resistor. - Connect the other end of the resistor to the cell negative lead.
- Connect cell negative, INA219
GND, and ESP32GNDtogether. - Power the INA219 logic from the ESP32
3V3pin—not from the measured cell. - Connect
SDAto GPIO21 andSCLto GPIO22 on a typical ESP32 DevKit. Change the YAML if your board uses other safe I²C pins. - Power the ESP32 independently through USB. The reference cell is a sensor, not the ESP32 power source.
Before exposing the cell to full sun, check polarity and continuity with a multimeter. There should be no short directly across the cell. Measure the actual resistance between INA219 VIN− and cell negative; it should be close to 10 Ω. Then power the ESP32 indoors and confirm that an I²C scan finds address 0x40.
Complete ESPHome configuration
Create a new ESPHome device, substitute the correct board if it is not an ordinary ESP32 DevKit, and keep credentials in secrets.yaml. The current ESPHome INA219 component supports current, power, bus-voltage, and shunt-voltage readings; all four are useful during commissioning.
substitutions:
device_name: solar-potential-sensor
friendly_name: Solar Potential Sensor
# Replace both values after calibration.
calibration_current_a: "0.180"
array_power_at_calibration_kw: "3.40"
esphome:
name: ${device_name}
friendly_name: ${friendly_name}
esp32:
board: esp32dev
framework:
type: esp-idf
logger:
api:
encryption:
key: !secret solar_potential_api_key
ota:
- platform: esphome
password: !secret solar_potential_ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "Solar sensor fallback"
password: !secret solar_potential_fallback_password
captive_portal:
i2c:
sda: GPIO21
scl: GPIO22
scan: true
sensor:
- platform: ina219
address: 0x40
shunt_resistance: 0.1 ohm
max_voltage: 6.0V
max_current: 0.4A
update_interval: 5s
current:
name: "Reference cell current"
id: reference_current
accuracy_decimals: 3
filters:
- median:
window_size: 5
send_every: 3
send_first_at: 1
power:
name: "Reference cell power"
accuracy_decimals: 3
bus_voltage:
name: "Reference cell loaded voltage"
accuracy_decimals: 2
shunt_voltage:
name: "INA219 shunt voltage"
accuracy_decimals: 4
entity_category: diagnostic
- platform: template
name: "Estimated solar potential"
id: estimated_solar_potential
unit_of_measurement: "kW"
device_class: power
state_class: measurement
accuracy_decimals: 2
update_interval: 15s
lambda: |-
if (!id(reference_current).has_state()) return NAN;
const float current = id(reference_current).state;
if (current <= 0.0f) return 0.0f;
return current / ${calibration_current_a}
* ${array_power_at_calibration_kw};
Add the four referenced secret values in the ESPHome Device Builder. Its wizard can generate the 32-byte base64 API key. The encrypted native API sends the entities directly to Home Assistant; MQTT is not required.
The first flash is easiest over USB. Read the logs before installing the box. The INA219 should appear at 0x40, the current should approach zero when the cell is covered, and it should rise smoothly as light increases. A persistent negative value normally means VIN+ and VIN− are reversed. An implausibly high value demands an immediate wiring and shunt-resistance check.
Calibrate against the real array
Do not assume that 0.2 A from the small cell equals 4 kW from a 4 kW array. The label on the reference cell is not a calibration certificate, and the real array may never produce its nameplate rating at your site.
- Clean the reference cell and make sure it has the same orientation and shading conditions as the array.
- Choose a clear day with stable sun around solar noon. The battery and loads must allow the inverter to take all available PV power; avoid battery-full curtailment, export limits, inverter clipping, or known grid restrictions.
- Choose whether the estimate represents inverter PV/DC power or AC output, then always compare the same measurement. DC power is usually the cleaner calibration target.
- At several stable moments, record the reference current and simultaneous real-array power. Ignore cloud-edge spikes and samples where the inverter is limiting production.
- For each sample calculate
factor = array kW / reference A. Use the median of the good factors to reduce noise.
For example, if the reference cell reads 0.180 A while the inverter reports 3.40 kW of unrestricted PV power, the factor is 3.40 / 0.180 = 18.89 kW/A. The YAML above expresses that same relationship as two readable calibration values. Replace both substitutions and upload the firmware.
A better calibration uses readings from more than one clear day and across several sun angles. Plot estimated versus measured power and adjust only after ruling out shade, dirt, clipping, and curtailment. Recheck seasonally and after changing the reference cell or its mounting angle.
Add it to Home Assistant
- Flash and power the ESP32 on the same reachable network as Home Assistant.
- Open Settings → Devices & services. Home Assistant should discover the ESPHome device automatically.
- Select Configure and enter the API encryption key if prompted. If discovery does not appear, add the ESPHome integration manually with
solar-potential-sensor.localor the device's reserved IP address. - Rename entities if desired, but keep clear names for reference current and estimated potential. Confirm that the device becomes unavailable when unplugged and recovers after reboot.
A simple dashboard card needs no extra Home Assistant YAML:
type: entities
title: Solar potential
entities:
- entity: sensor.solar_potential_sensor_estimated_solar_potential
name: Available array power
- entity: sensor.solar_potential_sensor_reference_cell_current
name: Reference current
- entity: sensor.solar_potential_sensor_reference_cell_loaded_voltage
name: Reference voltage
The generated entity IDs depend on the device and entity names, so select the actual entities in the visual card editor instead of blindly copying those examples. If you want “available solar energy” in kWh, create a Riemann sum Integral helper from the estimated-power entity in Settings → Devices & services → Helpers. That value is an estimate of solar opportunity, not a revenue-grade production meter.
Verify before using it for automation
Watch the sensor for several days before letting it control heavy loads. At sunrise and sunset the estimate should approach zero. Under stable sun it should track the shape of the inverter's PV curve. When the battery becomes full and the inverter curtails production, measured PV may fall while solar potential remains high—that difference is the useful signal.
Automations should require a sustained threshold and should fail safe when the entity is unavailable. For example, wait five or ten minutes above the desired potential before starting a discretionary load, use a lower threshold plus a delay to stop it, and still respect battery state of charge, inverter limits, and the appliance's own safety rules.
The finished device cannot tell me exactly what every panel could produce, but it gives Home Assistant an independent view of the sunlight that the inverter alone cannot provide. Careful placement and honest calibration matter far more than pretending the simple sensor is more precise than it is.