hlw8012

The HLW8012 IC in the new Sonoff POW

The HLW8012 is single phase energy monitor chip by the chinese manufacturer HLW Technology. It features RMS current, RMS voltage sampling and RMS active power with an internal clock and a PWM interface in a SOP-8 package. You can buy it at Aliexpress for less than a euro a piece and the necessary components are fairly easy to source and quite cheap.

All in all it looks like a great IC to include power monitoring in your projects. I guess that is why Itead Studio chose it for the Sonoff POW, one of their newest home automation products. And of course I have a POW here in my desk and I’ve been playing with it this weekend. The goal is to support it in my Espurna firmware but first I wanted to know more about the HLW8012. I’ll write about the Sonoff POW in a different post later this week.

HWL8012 basics

The HWL8012 is a 5V IC that monitors both voltage and current and output RMS voltage, current and active power encoded as a 50% duty cycle square wave where the frequency is proportional to the magnitude.

HLW8012 pinout

HLW8012 pinout

Inputs

Current is measured by a differential potential measure across a milli-ohm copper-manganese resistor in series with the main current flow. The differential potential is fed into pins VIP and VIN. The resistor must be such than the wave in VIP-VIN peaks at 43.75mV maximum. A 1milliohm resistor  is well suited to measure currents up to ~30A with a dissipation of less than 1W.

Voltage is measured in the same way but since the V2P pin supports up to 495mV RMS signals the potential has to be scaled down. The HLW8012 datasheet recommends a voltage divider of 6 470kOhm resitors upstream and a 1kOhm resistor downstream. That means a scale factor of 2821 that will convert a 230V RMS into 82mV that falls waaaay below the limit.

The product datasheet suggests the circuit below as a typical application and it’s the very same schematic you will find on the Sonoff POW (check it’s wiki page for a schematic). Except for the fact that the voltage divider in the Sonoff POW has only 5 470kOhm resistors.

HLW8012 typical application

HLW8012 typical application

Outputs

On the MCU side of the IC we have two pins that output the square wave. The CF pin pulse frequency increases as the active power increases too. The relation depends on the reference voltage (2.43V), the internal clock frequency (3579kHz typically), the voltage divider in the V2P input and the milliohm resistor. For the suggested application in the datasheet a frequency of 1Hz corresponds to a ~12W active power, 10Hz to ~120W, 100Hz to ~1.2kW and so on.

The CF1 pulse is proportional to the RMS current or voltage, depending on the value in the SEL pin. If SEL is pulled high then the CF1 pin outputs a square wave with a frequency proportional to the RMS current measured. If SEL is pulled down it will ouput the RMS voltage instead. Nominal values (as per datasheet) are 15mA or 0.5V for a 1Hz wave.

A library for the HLW8012

The HLW8012 library for Arduino and ESP8266 is released as free open software and can be checked out at my HLW8012 repository on Bitbucket.

The library is still a work in progress as I still have to integrate it in a real project like adding support for the Sonoff POW to my Espurna firmware. So far I have tested stand-alone. The library (as of now) has two ways to monitor pulses: using Arduino “pulseIn” method or using interrupts.

The interrupt-driven approach is the recommended approach but requires you to wire the interrupts to the library. This is so because I didn’t want to do that from the library since that would mean creating a singleton instance of the library to be able to route the interrupts (at least, that’s the only way I know).

The bare minimum example would be:


(... definitions ...) 

HLW8012 hlw8012;

void hlw8012_cf1_interrupt() {
    hlw8012.cf1_interrupt();
}

void hlw8012_cf_interrupt() {
    hlw8012.cf_interrupt();
}

void setup() {

    hlw8012.begin(CF_PIN, CF1_PIN, SEL_PIN, CURRENT_MODE, true);
    attachInterrupt(CF1_PIN, hlw8012_cf1_interrupt, CHANGE);
    attachInterrupt(CF_PIN, hlw8012_cf_interrupt, CHANGE);

    ( ... other setup code ...)

}

void loop() {

    static unsigned long last = millis();

    // This UPDATE_TIME should be at least twice the interrupt timeout (2 second by default)
    if ((millis() - last) > UPDATE_TIME) {

        last = millis();
        Serial.print("[HLW] Active Power (W)    : "); Serial.println(hlw8012.getActivePower());
        Serial.print("[HLW] Voltage (V)         : "); Serial.println(hlw8012.getVoltage());
        Serial.print("[HLW] Current (A)         : "); Serial.println(hlw8012.getCurrent());
        Serial.print("[HLW] Apparent Power (VA) : "); Serial.println(hlw8012.getApparentPower());
        Serial.print("[HLW] Power Factor (%)    : "); Serial.println((int) (100 * hlw8012.getPowerFactor()));
        Serial.println();

    }

    ( ... other loop code ... )

}

Please check the examples folder for more examples on how to use it all together, or use the non-interrupt approach.

Sensor calibration

Internally the library has 3 calibration factors that will apply to the pulse width reading. The actual magnitude (current, voltage or active power) is measured by dividing these values by the pulse width in microseconds.

The formulae are defined in the HLW8012 datasheet and, like I said, depend amongst other factors on the series resistor with the main line and the resistors that create the voltage divider in the V2P input. The library uses the recommended values for the typical application (also in the datasheet) but chances are your device uses a slightly different values of those, like using a 0.002 Ohm resistor instead of the 0.001 Ohm one. Besides, the real values and the nominal ones might not be 100% accurate.

The library provides two calibration methods to improve accuracy. The first calibration methods lets you specify the real values for the resistors around the HLW8012:

...
    hlw8012.begin(CF_PIN, CF1_PIN, SEL_PIN, CURRENT_MODE, true);
    hlw8012.setResistors(CURRENT_RESISTOR, VOLTAGE_RESISTOR_UPSTREAM, VOLTAGE_RESISTOR_DOWNSTREAM);
...

The second calibration method goes a step further and modifies the calibration values so the output matches the expected values for power, current or voltage. Of course if you use this second method the first one is not necessary. To calibrate the sensor using this method you will need some kind of interface to provide the expected values or start the device with a well know load.

The calibration load should be a pure resistive one or you can use an already calibrated commercially available wattimeter to read the values.

... 
    // Calibrate using a 60W bulb (pure resistive) on a 230V line
    hlw8012.expectedActivePower(60.0);
    hlw8012.expectedVoltage(230.0);
    hlw8012.expectedCurrent(60.0 / 230.0);
...

The library does not remember the calibration values across reboots so you will have to implement some kind of persistence of your own. You can use the get*Multiplier() and set*Multiplier() methods to retrieve a manually set these values.

How does it work?

In the interrupt-driven approach, the library monitors the pulses in the background. When the code calls the getActivePower, getCurrent or getVoltage methods the last sampled value is returned. This value might be up to a few seconds old if they are very low values (a 6W LED lamp will output a ~0.5Hz square wave). This is specially obvious when switching off the load. The new value of 0W or 0mA is ideally represented by infinite-length pulses. That means that the interrupt is not triggered, the value does not get updated and it will only timeout after 2 seconds (configurable through the pulse_timeout parameter in the begin() method). During that time lapse the library will still return the last non-zero value.

On the other hand, when not using interrupts, you have to let some time for the pulses in CF1 to stabilise before reading the value. So after calling setMode or toggleMode leave 2 seconds minimum before calling the get methods. The get method for the mode currently active will measure the pulse width and return the corresponding value, the other one will return the cached value (or 0 if none).

Use non-interrupt approach and a low pulse_timeout (200ms) only if you are deploying a battery powered device and you care more about your device power consumption than about precision. But then you should know the HLW8012 takes about 3mA at 5V…

I’ve put together this library after doing a lot of tests with a Sonoff POW. The HLW8012 datasheet gives some information but I couldn’t find any about this issue in the CF1 line that requires some time for the pulse length to stabilise (apparently). Any help about that will be very welcome.

CC BY-SA 4.0 The HLW8012 IC in the new Sonoff POW by Tinkerman is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

22 thoughts on “The HLW8012 IC in the new Sonoff POW

    1. Xose Pérez Post author

      To be honest I don’t have much experience under Windows… but as far as I know FTDI or Silabs CP210x USB to UART bridges are well supported. I’ve also read the CH340 chips are somewhat troublesome… I have never had an issue with them ob Linux 🙂

      Reply
  1. glaskugelsehen

    I just made the first tests with you library and the SONOFF POW. Great job, well done.
    Next step I will be moving to interrupt driven measurement.
    @gamersware: take care to select a USB/seriell adapter that can be switched to 3.3V (which is recommend for the ESP) and 5V (for other use). On Windows you have to manually install drivers for CP210x and CH340, FTDI should be supported directly.
    For flashing the SONOFFs I don’t take the 3.3V from the USB seriell Adapter, I take a separated 3.3V power supply which will guarantee the maximum current the ESP would need

    Reply
    1. glaskugelsehen

      Hi Xose,
      I just got it working with interrupt driven measurement. I tested different loads after calibrating with a 60W bulb. Measurements are reported every 10s and I can switch the relay on/off with the build-in button. Measurement data is send via network to a nodejs server running on linux just reporting the values in a terminal session. I didn’t have any crashes while testing 7W – 100W light bulbs, 600W toaster, 1800W flat-iron. The measurement was within 5% in power compared to a power meter.
      The software is written with OTA-Update, which makes it very comfortable to make changes and do the tests. Debugging information is also send to the nodejs server. I would not recommend to connect the USB-Seriell adapter while operating with main power !!
      Reinhard
      (Cologne, Germany)

      Reply
      1. Xose Pérez Post author

        Good!!
        Would you mind sharing your code? Some people have had problems with the interrupt approach…

        Reply
        1. glaskugelsehen

          Yes, I will share the code soon. I just found also problems with calibration when working with interrupts. I try to solve this doing the calibration without interrupts enabled.

          Reply
    1. Xose Pérez Post author

      Hi. The same formula the manufacturer explains in the datasheet.

      F(CF) = (V1 * V2 * 48 * Fosc) / (Vref * Vref * 128)

      Where F(CF) is the output frequency in the CF pio, Vref is the internal reference voltage (2.43V nominal), Fosc is the internal oscillator frequency (3.579MHz nominal). And V1 and V2 are the voltages in the inputs. So V1 is mains voltage divided by the voltage divider: a factor of (5*470k+1k) / 1k for the Sonoff Pow. And V2 is measured across the 0.001Ohm resistor, so it’s proportional to the current.

      You can check the actual calculation in the _calculateDefaultMultipliers method of the library.

      Reply
  2. Rajeev

    I am Interested in integrating ESP8266 with HLW8012. (a) Do you have the datasheet in english? (b) Secondly any idea of procuring / buying the milli ohm resistor which you have mentioned. (c) The circuit does not seem to isolate the mains from MCU signals. Do you thin it is achievable b y using transformers for current and voltages? (CT/PT)

    Reply
    1. Xose Pérez Post author

      Hi Rajeev
      a) No, I used google translator service to translate the chinese datasheet
      b) I have recently bought some 2milliOhm manganin resistors at AliExpress. It’s the closest I found.
      c) No it doesn’t. Actually I’m working on a sensor board for common non-invasive current sensors that will work for Sonoff TH devices. Not as accurate but good enough and easier to use.

      Reply
  3. Rajeev

    Thanks Xose,

    Well, using the external CT may solve isolation issue but withe sonoff it will increase the footprint… ;(

    Reply
  4. gaurav gupta

    Is there a limit to the frequency that HLW8012 can produce for power? can it go beyond 1Khz or 3KHz? Can ESP resolve 3Khz power pwm from HLW8012?

    Reply
    1. Xose Pérez Post author

      I can’t really tell. Never gone that far.

      Datasheet is not clear about that. Given that the POW is supposed to support up to [email protected] or 4kW that’s around 300Hz. Taking the maximum rating in the V1P and V1N pins of +-32mV with a recommended 1milliOhm burden resistor gives around 9.5kW that’s still less than 1kHz.

      Reply
    1. Xose Pérez Post author

      Not of the device itself. There is no documentation in the datasheet (at least, nothing I could interpret as a limit). But since the device outputs a square wave that’s inversely proportional to the measure there could be a limit in the measurement process. Even if you are using interrupts there is no way to tell a 0W from an arbitrarily low power measurement. A 1Hz wave means 12W (in the CF pin), 2Hz is 6W, 4Hz is 3W, and so on.

      Reply
  5. Pingback: Eco Plug (ESP8266) hack, finally! | Evan's Techie-Blog

Leave a Reply (all comments are moderated, be patient)