Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Random readings when no load\relay off #11

Open
chinswain opened this issue Oct 25, 2018 · 10 comments
Open

Random readings when no load\relay off #11

chinswain opened this issue Oct 25, 2018 · 10 comments
Labels
question Further information is requested

Comments

@chinswain
Copy link

chinswain commented Oct 25, 2018

I've just uploaded the interrupts example to a Sonoff POW, once or twice a minute I get random readings - is that to be expected? (I want to use the POW to determine if a device is switched on and for how long so was triggering on if current > x).

Most of the time with no load:

t1

Randomly with relay off:
t2

1 Hour history:
screenshot_20181025-141739_blynk

#define BLYNK_PRINT Serial
#include <Arduino.h>
#include "HLW8012.h"
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

#define SERIAL_BAUDRATE                 115200

// GPIOs
#define RELAY_PIN                       12
#define SEL_PIN                         5
#define CF1_PIN                         13
#define CF_PIN                          14
#define UPDATE_TIME                     5000
#define CURRENT_MODE                    HIGH

// These are the nominal values for the resistors in the circuit
#define CURRENT_RESISTOR                0.001
#define VOLTAGE_RESISTOR_UPSTREAM       ( 5 * 470000 ) // Real: 2280k
#define VOLTAGE_RESISTOR_DOWNSTREAM     ( 1000 ) // Real 1.009k

char auth[] = "";
char ssid[] = "";
char pass[] = "";

HLW8012 hlw8012;

void ICACHE_RAM_ATTR hlw8012_cf1_interrupt() {
  hlw8012.cf1_interrupt();
}
void ICACHE_RAM_ATTR hlw8012_cf_interrupt() {
  hlw8012.cf_interrupt();
}

void setInterrupts() {
  attachInterrupt(CF1_PIN, hlw8012_cf1_interrupt, CHANGE);
  attachInterrupt(CF_PIN, hlw8012_cf_interrupt, CHANGE);
}

void setup() {

  // Init serial port and clean garbage
  Serial.begin(SERIAL_BAUDRATE);
  Blynk.begin(auth, ssid, pass);
  // Close the relay to switch on the load
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, HIGH);


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

  setInterrupts();

}

char buffer[50];

void loop() {
  Blynk.run();
  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();

    Blynk.virtualWrite(V1, hlw8012.getActivePower());
    Blynk.virtualWrite(V2, hlw8012.getVoltage());
    Blynk.virtualWrite(V3, hlw8012.getCurrent());
    Blynk.virtualWrite(V4, hlw8012.getApparentPower());
    Blynk.virtualWrite(V5, (int) (100 * hlw8012.getPowerFactor()));
    Blynk.virtualWrite(V6, hlw8012.getEnergy());

  }
}
@erniberni
Copy link

Same problem for me.

@SergiuToporjinschi
Copy link

Same problem here

@erniberni
Copy link

I just did some tests with readings of power as fast as possible. To do this I set a boolean in the ISR and every time this is triggered (as this is an edge on the CF line) I print out the power.
When no other device connected (the power should be zero) I saw some pulses with a duration of mostly 200ms to 1000ms but also very seldom pulses with a duration of 1ms (!) with a calculated power of 6000W. But fortunately only single pulses only. To rule out these short pulses I will do 3 measurements in a row and kick out the value with the biggest deviation. And I will only do measurements after triggering the ISR, alll other times the power is 0 anyhow.
Remember that with a power of 5W the time between two pulses will be 1000ms with 60W it will be 100ms, so you can do a lot of measurements in a short time.

@xoseperez
Copy link
Owner

I also noticed these spurious readings with the HLW8012 but couldn't remove them. My approach in ESPurna is the same as yours, @erniberni. I'm not reading so often but I apply a median filter every 3 readings.

@xoseperez xoseperez added the question Further information is requested label Dec 17, 2018
@chinswain
Copy link
Author

Do you have some example code of your method @erniberni ?

@erniberni
Copy link

erniberni commented Jan 6, 2019

@chinswain
not really an example of code, but I try to explain, what I did.
I added to the ISR a boolean flag

void hlw8012_cf_interrupt() {
  hlw8012.cf_interrupt();
  trigd = true;
}

In the main loop, every time when trigd is set I check the power. When the measured power is within a limit bigger or smaller (half or doubled) then the previous value then I trust this value, if not, the value will be ignored. The first trigd set must be ignored. Here is a part of my test sketch.

void loop() {
  yield();
  if (millis() - lasttrigd_time > (PULSE_TIMEOUT / 1000)) {
    timeOutms = PULSE_TIMEOUT / 1000;
    trusted = false;
    firstReading = false;
    firstPulse = true;        // must be true after this if
  }
  if (trigd && millis() - lasttrigd_time > 100) {     // check every trigger or every 100ms
    sinceLastReadingTime = millis() - lasttrigd_time;     // time since last reading ie min 100
    lasttrigd_time = millis();
    if (!firstPulse) {   // not the first pulse after timeout
      actPow = hlw8012.getActivePower();   // measure
      client.print("[HLW] Power(W) trigd      : ");
      client.print(actPow);
      client.print("\tTime ");
      client.print(sinceLastReadingTime);
      client.print("\tlastTime ");
      client.print(sinceLastReadingTimeStored);
      if (!firstReading) {    // timeout was already reduced
        if ((sinceLastReadingTime > sinceLastReadingTimeStored >> 1) && (sinceLastReadingTime < sinceLastReadingTimeStored << 1)) {
          client.println("\ttrusted value\t");
          trusted = true;
        }
        else {
          client.println("\tUNTRUSTED value");    // difference too big
          trusted = false;
        }
      }
      else {
        client.println("\tUNTRUSTED value");  // this is first reading
        trusted = false;
        firstReading = false;
      }
      trigd = false;
      lastmillis = lasttrigd_time;
    }
    else {      // the first pulse
      trigd = false;
      firstReading = true;
      firstPulse = false;
    }
    sinceLastReadingTimeStored = sinceLastReadingTime;
  }
  printStatus();
  manageButton();
}

void printStatus() {
  if (millis() - lastmillis > UPDATE_TIME) {
    if (!client.connected()) {
      if (!client.connect(host, httpPort)) {  // check for reconnection
        Serial.println("connection failed");
      }
      else {
        client.println("[DEBUG] Connection re-opened");
        client.println();
      }
    }
    client.print("[HLW] Voltage (V)         : "); client.print(hlw8012.getVoltage());
    client.print("  timeout: "); client.println(timeOutms);
    if (trusted) {
      client.print("[HLW] Last Active Power (W)    : ");
      client.println(actPow);
      trusted = false;
    }
    lastmillis = millis();
  }
}

The debug messages are printed on a tcp client.
I hope this will help.

@javier-fg
Copy link

javier-fg commented Sep 14, 2023

I have been also having the same behaviour, and still looking for a solution.

Has someone find a solution?

image

In my case Voltage seems stable, but on Current and Power, there are crazy 1 sample spikes with no load, as you can see in the image.

@erniberni
Copy link

Did you try using my code above?

@javier-fg
Copy link

I am going to implemented your idea as part of the library, test it and share the results.

@javier-fg
Copy link

javier-fg commented Sep 15, 2023

It seems that the new filter performs good compared to the original libray code.

In the image, to the left it was the original library code, to the right it is the new filter functionality:
image

I also checked the CF signal with an oscilloscope and there were not signal spikes or noise (while there is no load connected), but the software reported this strange and random values. Perhaps the noise it is related to the manufacturer PCB design. In my case, everthing has been tested on a Delock 11827 smart plug metter.

The filter functionalities are implemented in the ISR for the CF line, therefore this will work only using interrupts.
Basically, once a timeout is detected, a time delay is added where the readings are ignore, after that there is a counter that counts valid readings, and if during that period no timeout it is detected the measurements will be valid.

void ICACHE_RAM_ATTR HLW8012::cf_interrupt() {

    unsigned long now = micros();
    
    _power_pulse_width = now - _last_cf_interrupt;
    _last_cf_interrupt = now;

    // Check if timeout event ocurred
    if (_power_pulse_width > _pulse_timeout){
        _notimeout_cf_cntr = 0;  
        _timeout_interrupt = now;
        _power_pulse_width = 0;
        _power = 0;  
    } 

    // Ignore any values during the PULSE_TIMEOUT microsec after first timeout event
    if( (now - _timeout_interrupt) < PULSE_TIMEOUT ){
        _power_pulse_width = 0;
        _power = 0;
    }else{

        // Wait for valid measurements, meantime ignore measurements
        if(_notimeout_cf_cntr <= TIMEOUT_VALID_PULSES){
            _notimeout_cf_cntr++;
            _power_pulse_width = 0;
            _power = 0;
        }
    }

    // Increment energy values only on valid signal
    if (_notimeout_cf_cntr >= TIMEOUT_VALID_PULSES)
        _pulse_count++;
}

The full source code can be find on the following fork repository https://github.com/javier-fg/hlw8012

If someone could also test it on their hardware, I could request a push to the original repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

5 participants