2018-08-24 22:44:01 +02:00
HLW8012 Power Sensor
====================
2018-11-14 22:12:27 +01:00
.. seo ::
:description: Instructions for setting up HLW8012 power sensors for the Sonoff Pow R1
2021-11-16 03:19:33 +01:00
:image: hlw8012.svg
2021-05-10 00:01:02 +02:00
:keywords: HLW8012, CSE7759, BL0937, Sonoff Pow R1
2018-11-14 22:12:27 +01:00
2018-08-24 22:44:01 +02:00
The `` hlw8012 `` sensor platform allows you to use your HLW8012 voltage/current and power sensors
(`datasheet <https://github.com/xoseperez/hlw8012/blob/master/docs/HLW8012.pdf> `__ ) sensors with
2021-05-10 00:01:02 +02:00
ESPHome. This sensor is commonly found in Sonoff POWs. CSE7759 and BL0937 are similar to HLW8012
2024-05-07 07:16:56 +02:00
and work with this component. Beware that CSE7759B is different and should be used with the
:doc: `CSE7766 <cse7766>` component.
2018-08-24 22:44:01 +02:00
2021-05-17 17:05:55 +02:00
.. note ::
2024-05-07 07:16:56 +02:00
SAFETY HAZARD: Some devices such as Sonoff POWs/Shelly/etc, have the digital GND connected directly to mains
voltage so **the GPIOs become LIVE during normal operation** . Our advice is to mark these boards to prevent
any use of the dangerous digital pins.
2021-05-17 17:05:55 +02:00
2018-08-24 22:44:01 +02:00
This sensor has two data outputs which both encode values using the frequency of a modulated signal: CF and CF1.
CF's frequency is proportional to the (active) power measured and CF1 is proportional to the current/voltage. Using
the SEL pin, one can choose between which mode to use.
2019-02-16 23:25:23 +01:00
ESPHome will continuously cycle the state of the SEL pin to measure current *and* voltage, though both cannot
2018-08-24 22:44:01 +02:00
be measured at the same exact points in time.
2018-11-19 18:32:16 +01:00
.. code-block :: yaml
2018-08-24 22:44:01 +02:00
# Example configuration entry
sensor:
- platform: hlw8012
2024-05-07 07:40:12 +02:00
sel_pin: GPIOXX
cf_pin: GPIOXX
cf1_pin: GPIOXX
2018-08-24 22:44:01 +02:00
current:
name: "HLW8012 Current"
voltage:
name: "HLW8012 Voltage"
power:
name: "HLW8012 Power"
2021-02-13 09:57:10 +01:00
energy:
name: "HLW8012 Energy"
2019-01-06 18:56:14 +01:00
update_interval: 60s
2018-08-24 22:44:01 +02:00
.. note ::
The configuration above should work for Sonoff POWs (R1).
Configuration variables:
------------------------
- **sel_pin** (**Required** , :ref: `Pin Schema <config-pin_schema>` ): The pin SEL is connected to.
- **cf_pin** (**Required** , :ref: `config-pin` ): The pin CF is connected to.
- **cf1_pin** (**Required** , :ref: `config-pin` ): The pin CF1 is connected to.
- **current** (*Optional* ): Use the current value of the sensor in amperes. All options from
2019-02-17 12:28:17 +01:00
:ref: `Sensor <config-sensor>` .
2018-08-24 22:44:01 +02:00
- **power** (*Optional* ): Use the (active) power value of the sensor in watts. All options from
2019-02-17 12:28:17 +01:00
:ref: `Sensor <config-sensor>` .
2018-08-24 22:44:01 +02:00
- **voltage** (*Optional* ): Use the voltage value of the sensor in V (RMS).
2019-02-17 12:28:17 +01:00
All options from :ref: `Sensor <config-sensor>` .
2021-02-13 09:57:10 +01:00
- **energy** (*Optional* ): Use the total energy value of the sensor in Wh.
All options from :ref: `Sensor <config-sensor>` .
2019-06-06 22:45:18 +02:00
- **update_interval** (*Optional* , :ref: `config-time` ): The interval to check the sensor. Defaults to `` 60s `` .
Advanced Options:
2018-08-24 22:44:01 +02:00
- **current_resistor** (*Optional* , float): The value of the shunt resistor for current measurement.
Defaults to the Sonoff POW's value `` 0.001 ohm `` .
- **voltage_divider** (*Optional* , float): The value of the voltage divider on the board as `` (R_upstream + R_downstream) / R_downstream `` .
Defaults to the Sonoff POW's value `` 2351 `` .
2024-05-07 07:40:12 +02:00
- **model** (*Optional* , string): The sensor model on the board, to set internal constant factors to convert pulses to measurements.
Possible values are `` HLW8012 `` , `` CSE7759 `` , `` BL0937 `` . Defaults to `` HLW8012 `` .
2021-07-05 01:09:16 +02:00
CSE7759 uses same constants and it also works with default. Must be set for BL0937 to be able to calibrate all three measurements at the same time.
2018-08-24 22:44:01 +02:00
- **change_mode_every** (*Optional* , int): After how many updates to cycle between the current/voltage measurement mode.
2024-05-07 07:40:12 +02:00
Note that the first value after switching is discarded because it is often inaccurate. When set to `` "never" `` the measurement mode will stay at the
2023-12-20 11:52:59 +01:00
set `` initial_mode `` . Defaults to `` 8 `` .
2019-06-06 22:45:18 +02:00
- **initial_mode** (*Optional* , string): The initial measurement mode. Defaults to `` VOLTAGE `` .
Possible initial measurement modes are `` VOLTAGE `` or `` CURRENT `` .
Permanent SEL Pin
-----------------
2019-01-06 18:56:14 +01:00
2019-06-06 22:45:18 +02:00
Some devices have the SEL pin permanently pulled high or low. If this is the case, you can configure
the initial measurement mode to match whichever mode the device uses, and disable mode switching.
.. code-block :: yaml
# Example configuration entry for device with fixed measurement mode
sensor:
- platform: hlw8012
2024-05-07 07:40:12 +02:00
sel_pin: GPIOXX
cf_pin: GPIOXX
cf1_pin: GPIOXX
2019-06-06 22:45:18 +02:00
current:
name: "HLW8012 Current"
voltage:
name: "HLW8012 Voltage"
power:
name: "HLW8012 Power"
update_interval: 60s
initial_mode: CURRENT
2023-12-20 11:52:59 +01:00
change_mode_every: "never"
2018-08-24 22:44:01 +02:00
2021-11-29 10:05:18 +01:00
SEL Pin Inversion
-----------------
If using model `` BL0937 `` the function of the SEL pin is inverted compared to default. When SEL=0 current is measured,
when SEL=1 voltage is measured. To accommodate this change use the following configuration:
.. code-block :: yaml
# Example configuration entry for device BL0937 using inverted SEL pin functionality
sensor:
- platform: hlw8012
model: BL0937
2024-05-07 07:40:12 +02:00
sel_pin:
number: GPIOXX
2021-11-29 10:05:18 +01:00
inverted: true
2024-05-07 07:40:12 +02:00
cf_pin: GPIOXX
cf1_pin: GPIOXX
2021-11-29 10:05:18 +01:00
current:
name: "BL0937 Current"
voltage:
name: "BL0937 Voltage"
power:
name: "BL0937 Power"
update_interval: 60s
2023-10-25 16:46:48 +02:00
Calibration
-----------
What you need:
- Your ESPHome power measurement plug
- A calibrated power meter that can measure voltage, current, power and power factor
- A large resistive load like an electric heater or kettle, ideally over 1000W
Steps:
- Find the currently used calibration values for `` current_resistor `` and `` voltage_divider `` in your device's YAML configuration or upload a new firmware with some known calibration values to the ESP.
- Plug the ESP power measurement plug into the calibrated power meter.
- Plug the load into the ESP plug.
- Turn on the load and make sure the power factor (PF) displayed on the power meter is 1.0. If it's not, you need to pick a different load.
- Wait for the readings to settle on both the power meter and the ESP.
- Write down the voltage reading of the power meter and the ESP at the same time.
- Write down the power reading of the power meter and the ESP at the same time.
- Write down the current reading of the power meter and the ESP at the same time.
- Use the calculator below to calculate the new calibration values for the ESP. These values will help it accurately measure power, voltage and current in the future.
- Upload the new values to the ESP.
Calibration values on the ESP:
.. raw :: html
<table>
<tr><td>voltage_divider:</td> <td> <input type="text" inputmode="numeric" value="2351" id="voltage-divider" onchange="calc()" onkeypress="calc()" onpaste="calc()" oninput="calc()" style="width: 100px; max-width: 75vw;"> </td> </tr>
<tr><td>current_resistor:</td> <td> <input type="text" inputmode="numeric" value="0.001" id="current-resistor" onchange="calc()" onkeypress="calc()" onpaste="calc()" oninput="calc()" style="width: 100px; max-width: 75vw;"> </td> </tr>
<tr><td>current_multiply:</td> <td> <input type="text" inputmode="numeric" value="1" id="current-multiply" onchange="calc()" onkeypress="calc()" onpaste="calc()" oninput="calc()" style="width: 100px; max-width: 75vw;"> </td> </tr>
</table>
ESP measurements:
.. raw :: html
<table>
<tr><td>Voltage:</td> <td> <input type="text" inputmode="numeric" id="sensor-voltage" onchange="calc()" onkeypress="calc()" onpaste="calc()" oninput="calc()" style="width: 100px; max-width: 75vw;"> V</td></tr>
<tr><td>Power:</td> <td> <input type="text" inputmode="numeric" id="sensor-power" onchange="calc()" onkeypress="calc()" onpaste="calc()" oninput="calc()" style="width: 100px; max-width: 75vw;"> W</td></tr>
<tr><td>Current:</td> <td> <input type="text" inputmode="numeric" id="sensor-current" onchange="calc()" onkeypress="calc()" onpaste="calc()" oninput="calc()" style="width: 100px; max-width: 75vw;"> A</td></tr>
</table>
Power meter measurements:
.. raw :: html
<table>
<tr><td>Voltage:</td> <td> <input type="text" inputmode="numeric" id="real-voltage" onchange="calc()" onkeypress="calc()" onpaste="calc()" oninput="calc()" style="width: 100px; max-width: 75vw;"> V</td></tr>
<tr><td>Power:</td> <td> <input type="text" inputmode="numeric" id="real-power" onchange="calc()" onkeypress="calc()" onpaste="calc()" oninput="calc()" style="width: 100px; max-width: 75vw;"> W</td></tr>
<tr><td>Current:</td> <td> <input type="text" inputmode="numeric" id="real-current" onchange="calc()" onkeypress="calc()" onpaste="calc()" oninput="calc()" style="width: 100px; max-width: 75vw;"> A</td></tr>
</table>
New calibration values:
.. raw :: html
<table>
<tr><td>voltage_divider:</td> <td> <input type="text" id="voltage-divider-new" onclick="this.focus();this.select()" style="width: 100px; max-width: 75vw;" readonly="readonly"> </td> </tr>
<tr><td>current_resistor:</td> <td> <input type="text" id="current-resistor-new" onclick="this.focus();this.select()" style="width: 100px; max-width: 75vw;" readonly="readonly"> </td> </tr>
<tr><td>current_multiply:</td> <td> <input type="text" id="current-multiply-new" onclick="this.focus();this.select()" style="width: 100px; max-width: 75vw;" readonly="readonly"> </td> </tr>
</table>
New ESP measurements:
.. raw :: html
<table>
<tr><td>Voltage:</td> <td> <input type="text" id="sensor-voltage-new" style="width: 100px; max-width: 75vw;" readonly="readonly"> V</td></tr>
<tr><td>Power:</td> <td> <input type="text" id="sensor-power-new" style="width: 100px; max-width: 75vw;" readonly="readonly"> W</td></tr>
<tr><td>Current:</td> <td> <input type="text" id="sensor-current-new" style="width: 100px; max-width: 75vw;" readonly="readonly"> A</td></tr>
</table>
<script>
function calc() {
let voltage_divider = parseFloat(document.getElementById("voltage-divider").value.replace(",", "."))
let current_resistor = parseFloat(document.getElementById("current-resistor").value.replace(",", "."))
let current_multiply = parseFloat(document.getElementById("current-multiply").value.replace(",", "."))
let real_voltage = parseFloat(document.getElementById("real-voltage").value.replace(",", "."))
let real_power = parseFloat(document.getElementById("real-power").value.replace(",", "."))
let real_current = parseFloat(document.getElementById("real-current").value.replace(",", "."))
let sensor_voltage = parseFloat(document.getElementById("sensor-voltage").value.replace(",", "."))
let sensor_power = parseFloat(document.getElementById("sensor-power").value.replace(",", "."))
let sensor_current = parseFloat(document.getElementById("sensor-current").value.replace(",", "."))
let calc_voltage = (document.getElementById("real-voltage").value !== "" || document.getElementById("sensor-voltage").value !== "")
let calc_power = (document.getElementById("real-power").value !== "" || document.getElementById("sensor-power").value !== "")
let calc_current = (document.getElementById("real-current").value !== "" || document.getElementById("sensor-current").value !== "")
let voltage_divider_new = voltage_divider;
let current_resistor_new = current_resistor;
let current_multiply_new = current_multiply;
if (calc_voltage) {
voltage_divider_new *= real_voltage / sensor_voltage;
}
if (calc_power) {
if (calc_voltage) {
current_resistor_new *= (sensor_power / sensor_voltage) / (real_power / real_voltage);
} else {
current_resistor_new *= sensor_power / real_power;
}
}
if (calc_current) {
if (calc_power) {
current_multiply_new *= (real_current / sensor_current) * (current_resistor_new / current_resistor);
} else {
current_resistor_new *= sensor_current / real_current;
}
}
document.getElementById("voltage-divider-new").value = voltage_divider_new;
document.getElementById("current-resistor-new").value = current_resistor_new;
document.getElementById("current-multiply-new").value = current_multiply_new;
let sensor_voltage_new = sensor_voltage * voltage_divider_new / voltage_divider
let sensor_power_new = sensor_power * (voltage_divider_new / current_resistor_new) / (voltage_divider / current_resistor)
let sensor_current_new = sensor_current * (current_multiply_new / current_resistor_new) / (current_multiply / current_resistor)
document.getElementById("sensor-voltage-new").value = Number(sensor_voltage_new.toFixed(8));
document.getElementById("sensor-power-new").value = Number(sensor_power_new.toFixed(8));
document.getElementById("sensor-current-new").value = Number(sensor_current_new.toFixed(8));
}
</script>
Example Config:
.. code-block :: yaml
substitutions:
voltage_divider: "2351"
current_resistor: "0.001"
current_multiply: "1.0"
sensor:
- platform: hlw8012
current_resistor: ${current_resistor}
voltage_divider: ${voltage_divider}
current:
name: "HLW8012 Current"
filters:
- multiply: ${current_multiply}
voltage:
name: "HLW8012 Voltage"
power:
name: "HLW8012 Power"
energy:
name: "HLW8012 Energy"
update_interval: 60s
2018-08-24 22:44:01 +02:00
See Also
--------
- :ref: `sensor-filters`
2019-05-12 22:44:59 +02:00
- :apiref: `hlw8012/hlw8012.h`
2018-08-24 22:44:01 +02:00
- `HLW8012 Library <https://github.com/xoseperez/hlw8012> `__ by `Xose Pérez <https://github.com/xoseperez> `__
2019-02-07 13:54:45 +01:00
- :ghedit: `Edit`