Power monitoring with BL0937 sensor

I have a device (Arlec PC399HA) that I had previously converted to Tasmota but I’ve been working to move things out of Tasmota and standardise on ESPHome. Calibrating the power metering in Tasmota was pretty easy but I’m having a bit of a struggle with ESPHome.

Without tinkering with the filters and just using the default current_resistor and voltage_divider values (which looking at the board, appears to be correct), the voltage that the device is reporting is 36.6V (should be around 237), current is 2.53a (should be around 0 with nothing plugged in) and power of 395W (again, should be around 0 with nothing plugged in). Looking at the various calibration guides, even without adding any calibration, the values reported back were in the ballpark of where they should be. Mine are well out though.

I have tinkered with the current_resistor and voltage_divider values which changed the reported values but didn’t come close to solving the problem. I’ve also tried adding filters, which did get the voltage reporting mostly correct but the power and current were still well off.

Here is my current config (with some tinkering commented out):

esphome:
  name: plug1

esp8266:
  board: esp01_1m
  

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "<password>"

ota:
  password: "<password>"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Plug1 Fallback Hotspot"
    password: "<password>"

binary_sensor:
  - platform: gpio
    name: "Plug1 Button"
    pin:
      number: GPIO3
      mode:
        input: true
        pullup: true
      inverted: true
    on_press:
      - switch.toggle: relay

switch:
- platform: gpio
  id: blue_led
  pin:
    number: GPIO13
    inverted: true
- platform: gpio
  name: "Plug1 Relay"
  pin: GPIO14
  id: relay
  on_turn_on:
  - switch.turn_on: blue_led
  on_turn_off:
  - switch.turn_off: blue_led

# Use red LED for connectivity status indicator
# status_led:
#   pin:
#     number: GPIO3
#     inverted: true

sensor:
  - platform: hlw8012
    model: BL0937
    sel_pin:
      number: GPIO12
      inverted: true
    cf_pin: GPIO04
    cf1_pin: GPIO05
    # Higher value gives lower watt readout
    #current_resistor: "0.003"
    # Lower value gives lower voltage readout
    #voltage_divider: "10051"
    current:
      name: "Plug1 Current"
      accuracy_decimals: 2
      # filters:
      #   # Map from sensor -> measured value
      #   - calibrate_linear:
      #       - 2.45905 -> 0.0
      #       - 2.25141 -> 5.9
      #       - 2.24197 -> 4.3
      #       - 2.41786 -> 10.0

    voltage:
      name: "Plug1 Voltage"
      # filters:
      #   # Map from sensor -> measured value
      #   - calibrate_linear:
      #       - 33.46 -> 238.02
      #       - 33.11665 -> 235.24
      #       - 33.35223 -> 237.70
      #       - 33.30263 -> 238.82

    power:
      name: "Plug1 Power"
      # filters:
      #   # Map from sensor -> measured value
      #   - calibrate_linear:
      #       - 364.00403 -> 0
      #       - 363.19363 -> 1402.43
      #       - 362.51831 -> 1026.926
      #       - 360.49231 -> 2352.4            
    update_interval: 15s

I have a bunch of power monitoring plugs using BL0937
They are the same model from the same brand.

Non of them could use the default voltage_divider so even tho I used a quality multimeter to measure the resistors and used the exakt values to calculate the voltage_divider, I did’t even get close to real life measurements from the plugs.

My voltage_divider have used these, and its been a trial and error approach, and is close enough for me.
1675
1715
1725
1775

Now my values had to be lowered from the calculated values as it reported a lot higer voltage then real.
Yours appears to be reporting lower.

Now they report correct-ish voltage ± 0.5 volts or even less.
And current seams to be correct, I don’t have a calibrated power meter to test against.
I have not used any other filtering/calibrations then voltage_divider.

So just change voltage_divider until you have the correct reported voltage, then see if you get you need any filters etc on current.

Does the power (watt) read out work correctly for you with the settings you’ve used to get the voltage and current correct?

I believe so, but again I don’t have a power meter to compare to, but I do have a PZEM-004T V3 running with CT clamp on the main power line, comparing that sensor with the outlet, it looks like the outlet load amount matches the amount added on the PZEM-004T.

So my approach was to measure the voltage in the plug, with a good accurate multimeter.
Then checked the value reported in esphome, then change accordingly until i have same or as close as is acceptable.

Here is my config, this is from a common file with the config as I have the same power plug so the same config can be used on all of them. the substitution ${calib} stores the voltage_divider value per device config.
As you can see I don’t have any filters etc.

sensor:
  - platform: hlw8012
    model: BL0937
    sel_pin:
      number: GPIO12
      inverted: true
    cf_pin: GPIO04
    cf1_pin: GPIO05
    voltage_divider: ${calib}
    current:
      name: "${shortname} Current"
    voltage:
      name: "${shortname} Voltage"
    power:
      name: "${shortname} Power"
      id: totalMediaWatts
    energy:
      name: "${shortname} Energy"
    update_interval: 5s

I have poured over the BL0937 datasheet and can’t figure out what is the shortest update interval that it supports, or that Tasmota supports.
Do you know?

I have bought couple of smart plugs based on Tuya bk7231n and BL0937 chips. These plugs reports correct value when we run them with factory firmware + Smart Life App. However after using LibreTiny + esphome the Voltage reported are random and fluctuating. Below given is my complete yaml. Some of the configurations mentioned in this yaml are derived from various topics in this forum, modified for my requirement.

substitutions:
  relay_restore_mode: RESTORE_DEFAULT_OFF 
  devicename: plugvoltage #wpropumpvoltge
  hlw8012_update_interval: 5s
  friendly_name: plugvoltage
  device_description: Wipro Plug with Energy
  current_resistor: "0.0145" "#"0.001" <- Calibrated using this factory value
  voltage_divider: "11350" #"1600" <- Calibrated using this factory value

esphome:
  name: ${devicename}
  friendly_name: ${friendly_name}
  comment: ${device_description}

bk72xx:
  board: generic-bk7231n-qfn32-tuya  

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "yourapikeyhere"

ota:
  password: "yourotapassword"

wifi:
  networks:
  - ssid: !secret wifi_ssid
    password: !secret wifi_password
  
  - ssid: !secret wifi2_ssid
    password: !secret wifi2_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "yourplug hotspot"
    password: "yourpassword"

captive_portal:

web_server:
  port: 80

debug:
  update_interval: 10s

globals:
  - id: total_energy
    type: float
    restore_value: yes
    initial_value: '0.0'
  
sensor:
  - platform: uptime
    name: Uptime
    update_interval: 5min

  - platform: wifi_signal
    name: "WiFi Signal"
    update_interval: 5min
    
  - platform: hlw8012
    model: BL0937
    
    cf_pin: # Power Calculations
      number: GPIO7
      inverted: true

    cf1_pin: # Current or Voltage based on Sel Pin value. 
      number: GPIO8
      inverted: true

    sel_pin:
      number: GPIO24
      inverted: true

    change_mode_every: "4294967295"
    update_interval: 5s    

    current_resistor: ${current_resistor}
    voltage_divider: ${voltage_divider}

    power:
      name: "Plug Power"
      id: PlugPower
      filters:
        - skip_initial: 1
        - multiply: 0.97
        - lambda: if (x < 0.01) {return 0;} else {return x;}

    voltage:
      name: "Plug Voltage"
      id: PlugVoltage
      filters:
        - skip_initial: 1

    energy:
      name: "Plug Energy"
      id: PlugEnergy
      filters:
        - skip_initial: 1

  - platform: template
    id: Actual_Voltage
    name: "Actual Voltage"
    lambda: |-
      if (id(PlugPower).state > 1)
      {
        return id(PlugVoltage).state;
      }
      else
      {
        return (id(PlugVoltage).state * 1.14); #This one is not giving the actual but higher than the actual voltage under no load.
      }
    update_interval: 1s
    unit_of_measurement: V
    filters:      
       - sliding_window_moving_average:
          window_size: 5
          send_every: 5

  - platform: template  
    name: "plug Current"
    id: Plugcurrent
    icon: mdi:current-ac
    unit_of_measurement: A
    accuracy_decimals: 2
    update_interval: "5s"
    lambda: |-
      return (id(PlugPower).state / id(PlugVoltage).state * 0.95);
    filters:  
      - skip_initial: 5        

  # Reports the total Power so-far each day
  - platform: total_daily_energy
    name: Total Daily Energy
    icon: mdi:circle-slice-3
    power_id: PlugPower
    filters:
      # Multiplication factor from W to kW is 0.001
      - multiply: 0.001
    unit_of_measurement: kWh

binary_sensor:
  - platform: gpio
    id: binary_switch_1
    device_class: window
    pin:
      number: P10
      inverted: true
      mode: INPUT_PULLUP
    on_press:
      then:
        - switch.toggle: switch_1

output:
  - platform: libretiny_pwm
    id: output_led_1
    pin:
      number: P23
      inverted: true

light:
  - platform: status_led
    id: plug_led
    output: output_led_1

switch:
  - platform: gpio
    id: switch_1
    name: Dongle Power
    pin: P26
    icon: mdi:power-socket-uk
    on_turn_on:
      - light.turn_on: plug_led
    on_turn_off:
      - light.turn_off: plug_led
    restore_mode: ${relay_restore_mode}

status_led:
  pin:
    number: P6
    inverted: true

button:
  - platform: restart
    name: Restart

time:
  - platform: sntp
    id: sntp_time
    servers:
     - yourntpserverip