xoseperez / hlw8012

HLW8012 library for Arduino and ESP8266 using the Arduino Core for ESP8266.
GNU General Public License v3.0
126 stars 48 forks source link

Multipliers for Gosund SP1? #23

Closed Miq1 closed 3 years ago

Miq1 commented 3 years ago

I am trying to make a sense out of the readings the HLW8012 library gives me for the Gosund SP1 plug.

The raw values I get with the default settings:

#define SEL_PIN 12
#define CF_PIN 4
#define CF1_PIN 5 
#define UPDATE_TIME 2000
#define CURRENT_RESISTOR                0.001
#define VOLTAGE_RESISTOR_UPSTREAM       ( 5 * 470000 ) // Real: 2280k
#define VOLTAGE_RESISTOR_DOWNSTREAM     ( 1000 ) // Real 1.009k
...
setup() {
...
hlw8012.begin(CF_PIN, CF1_PIN, SEL_PIN, LOW, false, 500000);
hlw8012.setResistors(CURRENT_RESISTOR, VOLTAGE_RESISTOR_UPSTREAM, VOLTAGE_RESISTOR_DOWNSTREAM);
...
loop() {
...
    if ((millis() - last) > UPDATE_TIME) {
      char buf[80];

      snprintf(buf, 80, "Power = %d W\n", hlw8012.getActivePower()); TelnetMsg(buf);
      snprintf(buf, 80, "Voltage = %d V\n", hlw8012.getVoltage()); TelnetMsg(buf);
      snprintf(buf, 80, "Current = %f A\n", hlw8012.getCurrent()); TelnetMsg(buf);
      snprintf(buf, 80, "Apparent = %d W\n", hlw8012.getApparentPower()); TelnetMsg(buf);
      snprintf(buf, 80, "Factor = %d %%\n\n", (int) (100 * hlw8012.getPowerFactor())); TelnetMsg(buf);

      // When not using interrupts we have to manually switch to current or voltage monitor
      // This means that every time we get into the conditional we only update one of them
      // while the other will return the cached value.
      hlw8012.toggleMode();

      last = millis();
    }
...

are quite off. These are the values from the lib (with a soldering station attached to the plug):

Power = 129295 W
Voltage = 5675 V
Current = 181.056161 A
Apparent = 1027493 W
Factor = 12 %

Power = 129295 W
Voltage = 5376 V
Current = 181.056161 A
Apparent = 924653 W
Factor = 12 %

Power = 126141 W
Voltage = 5675 V
Current = 181.056161 A
Apparent = 1027493 W
Factor = 11 %

A cheap power meter set in between shows these readings (varying a bit, as the soldering station is levelling the tip temperature):

Power = 5 to ~35 Watts
Voltage = 228 to 231 Volts

What are the multipliers required to correct the values?

Miq1 commented 3 years ago

I examined the PCB and found it is definitely a BL0937 being used here. WIN_20201220_16_25_16_Pro So I assume the reference voltage must be difgferent as will be the multipliers.

Miq1 commented 3 years ago

If someone is interested:

paketecuento commented 3 years ago

@Miq1 can you please give more details for BL0937? I want to measure only RMS current and active power every 200ms. Can you please share how much pulses do you have for a current of 100mA or so, for example? And 16A ?

Miq1 commented 3 years ago

Hi, I have no consumer yet to get up to 16A 😀 What I can do is give you my code - I completely abandoned the hlw8012 lib and am using the bare functions only:

Globally:
...
// Energy monitor GPIOs for Gosund SP1
#define SEL_PIN 12
#define CF_PIN 4
#define CF1_PIN 5 
// Energy monitor settings
#define UPDATE_TIME 5000
#define HIGH_PULSE 38
...
// Time between reads of the energy monitor im milliseconds
constexpr unsigned int update_interval = (UPDATE_TIME >= 2000) ? UPDATE_TIME : 2000;
// Number of intervals since system boot
unsigned long tickCount = 0;
// Spent Watt hours (Wh) since system boot
double accumulatedWatts = 0.0;
// Calculation helpers for hours and minutes since system boot
constexpr unsigned int ticksPerHour = 3600000 / update_interval;
constexpr unsigned int ticksPerMinute = 60000 / update_interval;
...

In setup():
...
    // Configure energy meter GPIOs
    pinMode(CF_PIN, INPUT);
    pinMode(CF1_PIN, INPUT);
    pinMode(SEL_PIN, OUTPUT);

    tickCount = 0;
    accumulatedWatts = 0.0;
...

void updateEnergy() {
  static unsigned long last = millis();    // time of last read
  static bool select = false;              // Toggle for voltage/current
  static double volt = 0.0;                // Last read voltage value
  static double amps = 0.0;                // Last read current value
  static double watt = 0.0;                // Last read power value
  unsigned long int cf;                    // CF read value (power pulse length)
  unsigned long int cf1;                   // CF1 read value (voltage/current pulse length)

  // New read due?
  if ((millis() - last) > update_interval) {
    // Yes. Read pulse lengths. Note: current reading may take a long time (up to 2.5 seconds)
    cf1 = pulseIn(CF1_PIN, LOW, 2500000);   // Read voltage or current
    cf  = pulseIn(CF_PIN, LOW, 200000);     // Read power

    // Calculate watts according the BL 0937 specs
    //              Vref^2       cycle length       v-specs     resistors
    // watt = cf ? (1483524.0 / (cf + HIGH_PULSE) / 1721506.0 * 2001000.0) : 0.0;
    watt = cf ? (1724380.585/(cf + HIGH_PULSE)) : 0.0;
    accumulatedWatts += watt * (millis() - last) / 3600000.0;

    // Did we read current?
    if (select) {
      // Yes. Calculate amps according to specs
      //               Vref         cycle length        v-specs   shunt
      // amps = cf1 ? (1218000.0 / (cf1 + HIGH_PULSE) / 94638.0 * 1000.0) : 0.0;
      amps = cf1 ? (12870.0/(cf1 + HIGH_PULSE)) : 0.0;
      // Toggle SEL pin to read the other value next time around
      digitalWrite(SEL_PIN, HIGH);
      select = false;
    } else {
      // No. Calculate volts according to specs
      //               Vref         cycle length        v-specs   resistors
      // volt = cf1 ? (1218000.0 / (cf1 + HIGH_PULSE) / 15397.0 * 2001.0) : 0.0;
      volt = cf1 ? (158299.656/(cf1 + HIGH_PULSE)) : 0.0;
      // Toggle SEL pin to read the other value next time around
      digitalWrite(SEL_PIN, LOW);
      select = true;
    }
    tickCount++;
    last = millis();
  }
}

I doubt that you will be lucky reading the current every 200ms. The data sheet specifies a measuring time of up to 2s for a single sample. I experienced something similar with low currents, where the pulse length may reach 100ms and more. It may be better with higher currents and shorter pulse lengths.

paketecuento commented 3 years ago

thank you for sharing your code. Anyway, I will try with an oscilloscope to check by myself. You also mentioned that the datasheet specifies 2s for measuring, but I cannot find this info in the v1.4 datasheet in Chinese.. where did you read this? In my case I will only measure current, so will not toggle with voltage.

Miq1 commented 3 years ago

See xoseperez' comment

paketecuento commented 3 years ago

Yes, I already read this. Anyway other users reports that lower times are possible, see https://github.com/xoseperez/hlw8012/issues/13#issue-433756980

Miq1 commented 3 years ago

I would say: just try it! I will appreciate if you could share your results.

Miq1 commented 3 years ago

I am getting many 0A results when using a timeout of 250000 (250ms) with pulseIn(). I need a second at least to read low wattages around 5W.