Open chinswain opened 6 years ago
Same problem for me.
Same problem here
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.
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.
Do you have some example code of your method @erniberni ?
@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.
I have been also having the same behaviour, and still looking for a solution.
Has someone find a solution?
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.
Did you try using my code above?
I am going to implemented your idea as part of the library, test it and share the results.
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:
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.
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:
Randomly with relay off:
1 Hour history: