279 lines
13 KiB
ReStructuredText
279 lines
13 KiB
ReStructuredText
HLW8012 Power Sensor
|
|
====================
|
|
|
|
.. seo::
|
|
:description: Instructions for setting up HLW8012 power sensors for the Sonoff Pow R1
|
|
:image: hlw8012.svg
|
|
:keywords: HLW8012, CSE7759, BL0937, Sonoff Pow R1
|
|
|
|
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
|
|
ESPHome. This sensor is commonly found in Sonoff POWs. CSE7759 and BL0937 are similar to HLW8012
|
|
and work with this component. Beware that CSE7759B is different and should be used with the
|
|
:doc:`CSE7766 <cse7766>` component.
|
|
|
|
.. note::
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
ESPHome will continuously cycle the state of the SEL pin to measure current *and* voltage, though both cannot
|
|
be measured at the same exact points in time.
|
|
|
|
.. code-block:: yaml
|
|
|
|
# Example configuration entry
|
|
sensor:
|
|
- platform: hlw8012
|
|
sel_pin: GPIOXX
|
|
cf_pin: GPIOXX
|
|
cf1_pin: GPIOXX
|
|
current:
|
|
name: "HLW8012 Current"
|
|
voltage:
|
|
name: "HLW8012 Voltage"
|
|
power:
|
|
name: "HLW8012 Power"
|
|
energy:
|
|
name: "HLW8012 Energy"
|
|
update_interval: 60s
|
|
|
|
.. 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
|
|
:ref:`Sensor <config-sensor>`.
|
|
- **power** (*Optional*): Use the (active) power value of the sensor in watts. All options from
|
|
:ref:`Sensor <config-sensor>`.
|
|
- **voltage** (*Optional*): Use the voltage value of the sensor in V (RMS).
|
|
All options from :ref:`Sensor <config-sensor>`.
|
|
- **energy** (*Optional*): Use the total energy value of the sensor in Wh.
|
|
All options from :ref:`Sensor <config-sensor>`.
|
|
- **update_interval** (*Optional*, :ref:`config-time`): The interval to check the sensor. Defaults to ``60s``.
|
|
|
|
Advanced Options:
|
|
|
|
- **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``.
|
|
- **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``.
|
|
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.
|
|
- **change_mode_every** (*Optional*, int): After how many updates to cycle between the current/voltage measurement mode.
|
|
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
|
|
set ``initial_mode``. Defaults to ``8``.
|
|
- **initial_mode** (*Optional*, string): The initial measurement mode. Defaults to ``VOLTAGE``.
|
|
Possible initial measurement modes are ``VOLTAGE`` or ``CURRENT``.
|
|
|
|
Permanent SEL Pin
|
|
-----------------
|
|
|
|
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
|
|
sel_pin: GPIOXX
|
|
cf_pin: GPIOXX
|
|
cf1_pin: GPIOXX
|
|
current:
|
|
name: "HLW8012 Current"
|
|
voltage:
|
|
name: "HLW8012 Voltage"
|
|
power:
|
|
name: "HLW8012 Power"
|
|
update_interval: 60s
|
|
initial_mode: CURRENT
|
|
change_mode_every: "never"
|
|
|
|
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
|
|
sel_pin:
|
|
number: GPIOXX
|
|
inverted: true
|
|
cf_pin: GPIOXX
|
|
cf1_pin: GPIOXX
|
|
current:
|
|
name: "BL0937 Current"
|
|
voltage:
|
|
name: "BL0937 Voltage"
|
|
power:
|
|
name: "BL0937 Power"
|
|
update_interval: 60s
|
|
|
|
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
|
|
|
|
See Also
|
|
--------
|
|
|
|
- :ref:`sensor-filters`
|
|
- :apiref:`hlw8012/hlw8012.h`
|
|
- `HLW8012 Library <https://github.com/xoseperez/hlw8012>`__ by `Xose Pérez <https://github.com/xoseperez>`__
|
|
- :ghedit:`Edit`
|