Decoding Power Bar with Energy Monitoring

In the comment section of the first link there’s someone that posted code to get the energy monitoring, using Pin 12 and Pin 0 on the embedded esp8266, at least approximately. Doesn’t seem like anyone took it much further proof-of-concept

Thank you @workingmanrob!

It does appear that 1588NAZ04 is the mystery chip… and still remains a mystery. I did some looking on Google but as others had mentioned in the links you provided, there is no datasheet to be found.

Here is my current code. The wattage is roughly accurate as I calibrated it against a Sonoff S31. The other values are wrong still.

ESPHome YAML

esphome:
  name: esphome-web-08cdda
  friendly_name: WiOn Power Bar
  includes:
    - wionPowerMonitor.h

esp8266:
  board: esp01_1m

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: [REDACTED]

ota:


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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esphome-Web-08Cdda"
    password: ""

captive_portal:
    
light:
  - platform: status_led
    name: "LED"
    id: led
    icon: mdi:led-on
    pin: 
      number: 2
      inverted: true

binary_sensor:
  - platform: gpio
    name: "Button"
    pin: 
      number: 13
      inverted: true
    on_press:
      if:
        condition:
          - switch.is_on: ledEnable
        then:
          if:
            condition:
              switch.is_on: relay
            then:
              - switch.turn_off: relay
              - light.turn_off: led
            else:
              - switch.turn_on: relay
              - light.turn_on: led
        else:
          - switch.toggle: relay

switch:
  - platform: gpio
    name: "Switch"
    id: "relay"
    pin: 15
    icon: mdi:electric-switch
    on_turn_on: 
      then:
        - if: 
            condition:
              - switch.is_on: ledEnable
            then: 
              - light.turn_on: led
    on_turn_off: 
      then:
        - if: 
            condition: 
              - switch.is_on: ledEnable
            then: 
              - light.turn_off: led
  - platform: template
    id: ledEnable
    name: "Enable LED"
    device_class: switch
    optimistic: True
    restore_mode: RESTORE_DEFAULT_ON
    icon: mdi:led-on
    entity_category: config

sensor:
  - platform: custom
    lambda: |-
      auto power_sense = new wionPowerMonitor();
      App.register_component(power_sense);
      return {power_sense->voltage_sensor, power_sense->current_sensor, power_sense->power_sensor, power_sense->powerF_sensor};
    sensors:
      - name: "Voltage"
        unit_of_measurement: V
        accuracy_decimals: 1
      - name: "Current"
        unit_of_measurement: A
        accuracy_decimals: 1
      - name: "Power"
        unit_of_measurement: W
        accuracy_decimals: 1
      - name: "Power Factor"
        unit_of_measurement: PF
        accuracy_decimals: 2

Custom code for getting values. This file is in homeassistant/esphome/wionPowerMonitor.h.

#include "esphome.h"

const uint16_t POLLING_PERIOD = 15000; //milliseconds
float voltage = 0.0;
float current = 0.0;
float power = 0.0;
float powerF = 0.0;

class wionPowerMonitor : public PollingComponent, public Sensor {
 public:
	Sensor *voltage_sensor = new Sensor();
	Sensor *current_sensor = new Sensor();
	Sensor *power_sensor = new Sensor();
	Sensor *powerF_sensor = new Sensor();
	wionPowerMonitor() : PollingComponent(POLLING_PERIOD) {}
	float get_setup_priority() const override { return esphome::setup_priority::BUS; }

  void setup() override {
	pinMode(0, OUTPUT);
	pinMode(12, INPUT);
	digitalWrite(0, HIGH);
  }

  void update() override {

	int numBlocks = 4;
	int bitsPerBlock = 32;

	int data[numBlocks];

	for (int block = 0; block < numBlocks; block++) {
		for (int bitIdx = bitsPerBlock - 1; bitIdx >= 0; bitIdx--) {
			digitalWrite(0, LOW);
			delayMicroseconds(35);
			bitWrite(data[block], bitIdx, digitalRead(12));
			digitalWrite(0, HIGH);
			delayMicroseconds(35);
   		}
	}

	for (int block = 0; block < numBlocks; block++) {
		for (int bit = 24; bit < 32; bit++) {
			bitWrite(data[block], bit, 0);
		}
	}

	ESP_LOGD("raw", "%#08X; %#08X; %#08X; %#08X", data[0], data[1], data[2], data[3]);

	power = data[0];
	voltage = data[1];
	powerF = data[2];
	current = data[3];

	power = power / 3;	// roughly calibrated against Sonoff S31

	if (power < 1 || power > 2000) {	// filter to ignore erraneous data
		power = 0;
	}

	if (data[0] != 0xFFFFFF && current < 3000) { 	// filters to ignore erraneous data
		power_sensor->publish_state(power);
		voltage_sensor->publish_state(voltage);
		powerF_sensor->publish_state(powerF);
		current_sensor->publish_state(current);
	}

  }
};

I’ll continue looking on the web to see if I can find any info on that chip relating to how it works.

Thanks again for finding the chip name!

I also picked up some of these from my local dollarama and setup esphome. Having the energy monitoring would be great

For the WiOn 50055 Archives - wion outdoor wifi login version with 2 USB ports the night light LED seems to be PIN 14

tested by adding this…

light:
  - platform: binary
    name: "LED"
    output: light_output
    restore_mode: ALWAYS_OFF

output:
  - id: light_output
    platform: gpio
    pin: 14

@devWaves Oh that’s awesome. I just assumed it was an always on light and covered the LEDs with electrical tape while I had them opened up for flashing.

Will give this and the power monitoring setup a go today.

Nightlight working perfectly - thanks @devWaves

And similar to @agk1190 my W looks correct but the other values are just all over the place.

I cheesed it by doing some bad math on the V cause it should always be ~120V and then I removed Current and PowerF.

What is that factor for? Mine was always 0… ¯\_(ツ)_/¯

Thanks again guys - much better than before.

@agk1190, Thanks for sharing the trace! I have been looking for something similar online but the last one I found was many years ago and is no longer available for download.

I am unsure if you can do another trace, but this time use a resistive load (e.g. old school 40W light bulb) That way the readings may be less random. Besides the possible minor voltage fluctuation, most values should be pretty constant, and PF should be 1.

Before I arrive this page, I have already made a PoC mod on my own. Long story short, I have bypassed the unmark chip.

Cut the VS, VC, PC pin to the ESP board. Then run a wire to connect VC (CF1), PC (CF) to GPIO 4 & 5*1. For the VS (SEL) is a bit tricky, according to specs Vhi min is 0.8Vdd which is 4V. I have tried 3.3V logic and it is no go for me. So I made a level shifter using 2N3904 and connected that to GPIO16.

Other misc findings…
GPIO0 - Output to the unmarked chip
GPIO2 - Blue WiFi LED
GPIO12 - Input pin receiving data from the unmark chip
GPIO13 - Button 1
GPIO14 - Green (aka night light) LED
GPIO15 - Relay 1

For the Green LED, I configured it as PWM. With a simple script*2, I make it as power usage indicator, it gets brighter when more current/power is being used.

If you want to save an IO pin, you could use GPIO0 or even GPIO2 for VS (SEL) output.

I have ordered some level shifters, once I get them I will replace the 2N3904 and have all 3 signals run through it.

Another observation is the reaction time for current/power detection is kind of slow after a device is unplugged. It may take a few seconds to drop to 0A 0W. I think it may be due to the sampling time (switching SEL HI/LO) frequency.

*1CF1 and CF is 5V logic, it is in serial with 1K before reaching VS & VC pin. Some people (including myself) may not feel comfortable hooking up 5V login directly to ESP8266, but a few articles did some in-depth analysis to indicate ESP8266 logic is 5V tolerant. And to confirm, even the CEO of Espressif - Teo Swee Ann has confirmed.

*2Custom compile Tasmota 13.3.0 ESP8266 using Script instead of rules.

1 Like

I am getting this from the original WiOn when I hook up the serial port (but not connected to AC power)

####WH_Data:48
####WH_Data:0
####WH_Data:0
####WH_Data:0
Hw ret_data:0
[Pow_detect]A ret_data:0 362
W ret_data:0 1723
V ret_data:0 [1089 ~ 6535 ]
#######Today_Hw_Total:0

I am tempted to use a laptop running on battery to hook up a live one to do a bit more data collection. Even better solution would be using some opto-isolator to prevent and grounding potential problem. :thinking:

FYI: N on WiOn is connected to GROUND (and digital ground as well!!) And L is connected with a voltage divider to the same ground.

I’ve been banging my head for a while trying to figure out why I couldn’t get voltage measurement to work… Current measurement was working fine but the voltage reading would rise when the current did. I was tracing the SEL line and was scratching my head because it was connected… Your explanation that the logic level is too low seems plausible.

The 1588NAZ04 chip is connected to the same 3.3V regulator as the ESP8266 but for whatever reason it’s outputs seem to be 3.6V. Potentially that is high enough for the HLW8012?

I am not sure how the 1588NAZ04 chip works, I didn’t spend much time tracing the rest of the pins (1,5,7,8) it seems to connect to some diodes and such. I was guessing it is a PIC of some sort because it is pretty common to have pins 1 & 8 for power. Therefore I was guessing that “chip” is running at 5V to supply SEL signal.

Well am I glad I pinged this thread… yes, yes I am!

Seems like there might be the possibility of figuring out this mystery chip but in the meantime I was able to modify what agk1190 shared to get enough to be happy.

I removed Current and Power Factor. Added platform: total_daily_energy and now I get KWh which shows up on my Energy dashboard.

Commented out what I didn’t want and added a cheesy fix for Voltage…

#include "esphome.h"

const uint16_t POLLING_PERIOD = 15000; //milliseconds
float voltage = 0.0;
//float current = 0.0;
float power = 0.0;
//float powerF = 0.0;

class wionPowerMonitor : public PollingComponent, public Sensor {
 public:
        Sensor *voltage_sensor = new Sensor();
//      Sensor *current_sensor = new Sensor();
        Sensor *power_sensor = new Sensor();
//      Sensor *powerF_sensor = new Sensor();
        wionPowerMonitor() : PollingComponent(POLLING_PERIOD) {}
        float get_setup_priority() const override { return esphome::setup_priority::BUS; }

  void setup() override {
        pinMode(0, OUTPUT);
        pinMode(12, INPUT);
        digitalWrite(0, HIGH);
  }

  void update() override {

        int numBlocks = 4;
        int bitsPerBlock = 32;

        int data[numBlocks];

        for (int block = 0; block < numBlocks; block++) {
                for (int bitIdx = bitsPerBlock - 1; bitIdx >= 0; bitIdx--) {
                        digitalWrite(0, LOW);
                        delayMicroseconds(35);
                        bitWrite(data[block], bitIdx, digitalRead(12));
                        digitalWrite(0, HIGH);
                        delayMicroseconds(35);
                }
        }

        for (int block = 0; block < numBlocks; block++) {
                for (int bit = 24; bit < 32; bit++) {
                        bitWrite(data[block], bit, 0);
                }
        }

//      ESP_LOGD("raw", "%#08X; %#08X; %#08X; %#08X", data[0], data[1], data[2], data[3]);
        ESP_LOGD("raw", "%#08X; %#08X", data[0], data[1]);

        power = data[0];
        voltage = data[1];
//      powerF = data[2];
//      current = data[3];

        power = power / 3;      // roughly calibrated against Sonoff S31
        voltage = voltage / 2455;       // should be 120V so make it so

        if (power < 1 || power > 2000) {        // filter to ignore erraneous data
                power = 0;
        }

        if (voltage > 190 || voltage < 200) {   // filter to ignore erraneous data
                voltage = 122;
        }

//      if (data[0] != 0xFFFFFF && current < 3000) {    // filters to ignore erraneous data
        {
                power_sensor->publish_state(power);
                voltage_sensor->publish_state(voltage);
//              powerF_sensor->publish_state(powerF);
//              current_sensor->publish_state(current);
        }

  }
};

Then the device config looks like this:

substitutions:
  hostname: 'office-oil-heater'
  name: 'Office Oil Heater'
  room: 'Office'

esphome:
  name: $hostname
  includes:
    - includes/wionPowerMonitor.h

esp8266:
  board: esp01_1m

wifi: !include includes/wifi.yaml

captive_portal:

# Enable logging
logger:

# Enable Home Assistant API
api: !include includes/api.yaml

ota: !include includes/ota.yaml

web_server:

binary_sensor:
- platform: gpio
  pin:
    number: GPIO13
  id: button
  on_press:
   - switch.toggle: relay1

status_led:
  pin:
    number: GPIO2

switch:
- platform: gpio
  name: "$name"
  pin: GPIO15
  id: relay1
  icon: "mdi:radiator"

light:
- platform: binary
  name: "$room Nightlight"
  output: light_output
  restore_mode: ALWAYS_OFF

output:
- id: light_output
  platform: gpio
  pin: 14

sensor:
- platform: custom
  lambda: |-
    auto power_sense = new wionPowerMonitor();
    App.register_component(power_sense);
    return {power_sense->voltage_sensor, power_sense->power_sensor};
#    return {power_sense->voltage_sensor, power_sense->power_sensor, power_sense->current_sensor, power_sense->powerF_sensor};
  sensors:
    - name: "$name Voltage"
      unit_of_measurement: V
      accuracy_decimals: 1
    - name: "$name Power"
      unit_of_measurement: W
      accuracy_decimals: 1
      id: my_power
- platform: total_daily_energy
  name: '$name Energy'
  power_id: my_power
  unit_of_measurement: 'kWh'
  state_class: total_increasing
  device_class: energy
  accuracy_decimals: 3
  filters:
    # Multiplication factor from W to kW is 0.001
    - multiply: 0.001

time:
- platform: homeassistant
  id: homeassistant_time

Thanks again guys.

If anyone cracks that 1588NAZ04 nut hopefully that info ends up here. :crossed_fingers:

I’ll do some more testing as @KwansResearchLab suggested.

In the meantime, an interesting note on voltage I just discovered…

I had the power bar plugged in to the wall for the last few days and the voltage was relatively steady at around 450,000. I recently moved the power bar to the end of a ~50’ extension cord and found the voltage to be around 290,000.

(Wall)
image

(Extension cord)
image

This suggests that the value is not simply scaled from 120 but maybe indicates some amount change from 120V?

The mystery continues…

Hi @KwansResearchLab,

Here are the results using a 6W LED bulb. I didn’t get a chance to test an incandescent bulb (if I have any).

I may have destroyed GPIO0 during some tests so I may have to do your modification and bypass the ADC but we’ll see.

Yea. I know it is hard to find an incandescent bulb these days. It took me a good half an hour to find a 40W bulb in my storage :sweat_smile:

Another suggestion is your solder iron, if it is not a fancy one, it is just on and off. Anything with PF=1 will make the task of decrypting those numbers easier.

I am sorry to hear you may have toasted GPIO0… :pensive: I hope that’s not the case.

I ended up using a incandescent Christmas decoration. PF was steady at around 183,000. Still not sure how it all correlates.

GPIO0 does seem to be stuck at HIGH when set as an output. It might still work as an input though. I cut the trace and re-wired to GPIO4. Works like before.

Here is my update.

I attempted to decode the serial message compared to a known load before looking into GPIO0 & GPIO12. But somehow bad things happened, not sure why, 2 out of 3 times, when I tried to the socket, it crapped out my USB-TTL serial adapter. The second time it even took out my keyboard and mouse, luckily a reboot fixed it. Afterward, I asked myself why the trouble, since tasmota already had a working driver for HLW8012. Even if I could figure out the unmarked chip, I still have to write code and marge with tasmota.

Meanwhile, the bidirectional level shifter arrived in the mail, as planned previously, I have SEL/CF/CF1 properly level shifted. But then, the result was unexpected, Voltage reading was widely fluctuating, from 120V to 70V!! Since the ESP is counting the pulse to determine voltage, I think the level shifter is not fast enough. So I bypassed the level shifter, and have it connected directly to the ESP8266 (like my original mod)… and ta da, everything works fine.

So long story short, at the end I only do 3.3V to 5V level shifting for SEL. I am using GPIO 16 for SEL, GPIO 4 & 5 for CF1 & CF.

In theory, I could use GPIO 0 (as OUTPUT) for SEL since it is just feeding the unmark chip. But since it works, why fix it :wink:

And for your reference, these are my calibration value (YMMV)

VoltageCal=1380
CurrentCal=1704
PowerCal=4278

I too had good luck bypassing with a level shifter. I wrote about it here:

1 Like

If I’m reading this right, we need another board/component that’s called a level shifter? Any chance you could post an example board we could buy from aliexpress or amazon?

These are the ones I got from Amazon:
KeeYees 10pcs 4 Channels IIC I2C Logic Level Converter Bi-Directional Module 3.3V to 5V Shifter for Arduino (Pack of 10) https://a.co/d/3oC1ulL

You can get them significantly cheaper if you order from AliExpress and wait a month or two for shipping.

Thank you! I assume only 2 of the 4 channels are required right? Just considering getting something smaller

This is the circuit I used for level shifting in my original mod. Resistor values are not too important, as long as they are not too far off. (YMMV)

image

For my initial proof of concept, I used a 74LS00 just to boost the output to 5V…