• Witaj na Forum Arduino Polska! Zapraszamy do rejestracji!
  • Znajdziesz tutaj wiele informacji na temat hardware / software.
Witaj! Logowanie Rejestracja


Ocena wątku:
  • 0 głosów - średnia: 0
  • 1
  • 2
  • 3
  • 4
  • 5
ESP8266EX + BL0397, czyli smart gniazdko i problem z pomiarem prądu
#1
Witam, 

zakupiłem z ciekawości budowy, działania, a przede wszystkim dokładności pomiarów gniazdko SmartDGM, made for Biedronka. Na pokładzie jest nic innego jak układ ESP8266EX, a za pomiar prądu odpowiada klon HLW8012, czyli BL0397.
No i właśnie w fakcie, że to jest klon, jest cały szkopuł.

Do obsługi tego, użyłem biblioteki od HLW8012 autorstwa Xose Pérez https://github.com/xoseperez/hlw8012
Dla BL0397 trzeba było nieco tą bibliotekę poprawić, ale to głównie kosmetyka, czyli mnożniki, napięcia wzorcowe, itp.

Mój problem nie dotyczy tyle dokładności pomiaru, co samego pomiaru.

Gdy wgrywam example, ustawiając rzecz jasna odpowiednie piny, w Serial Monitorze, mam odczyt, ale zawsze ten sam. 
Tzn. wykonując manualną kalibrację (jest w kodzie sketcha), np. na żarówce 150W, po ustawieniu multiplerów, restarcie płytki i właczeniu zasilania, widzę ładnie odczyt 230V, 150W. Zmieniam żarówkę na 40W i mam dokładnie ten sam odczyt. Kolejna zmiana na 10W i nadal w Serialu widzę 150W. Oczywiście rekalibracja na inną żarówkę daje tylko tyle że wynik z tej kalibracji mam w serialu.

Mój kod programu, odchudzony o części od wifi czy komunikacji z serwerem:
Kod:
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPClient.h>
#include "BL0397.h"


#define LED                             13
#define RELAY_PIN                       14
#define SEL_PIN                         12
#define CF1_PIN                         5
#define CF_PIN                          4
//int SWITCH =                         3;

#define UPDATE_TIME                     4000

#define CURRENT_MODE                    LOW

#define CURRENT_RESISTOR                0.001
#define VOLTAGE_RESISTOR_UPSTREAM       ( 3 * 599000 ) // Real: 2280k
#define VOLTAGE_RESISTOR_DOWNSTREAM     ( 1000 ) // Real 1.009k

BL0397 hlw8012;



void setup() {
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, HIGH);
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);

   hlw8012.begin(CF_PIN, CF1_PIN, SEL_PIN, CURRENT_MODE, false, 500000);

//   hlw8012.setResistors(CURRENT_RESISTOR, VOLTAGE_RESISTOR_UPSTREAM, VOLTAGE_RESISTOR_DOWNSTREAM);
    hlw8012.setCurrentMultiplier(45.65);
    hlw8012.setVoltageMultiplier(16100.45);
    hlw8012.setPowerMultiplier(12000.88);

}

void unblockingDelay(unsigned long mseconds) {
    unsigned long timeout = millis();
    while ((millis() - timeout) < mseconds) delay(1);
}

//=======================================================================
//                    Main Program Loop
//=======================================================================
void loop() {
//  button();
  server.handleClient();
  delay(500);
    static unsigned long last = millis();

    // This UPDATE_TIME should be at least twice the minimum time for the current or voltage
    // signals to stabilize. Experimentally that's about 1 second.
    if ((millis() - last) > UPDATE_TIME) {

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

    }
Serial.println(countdown);

//=======================================================================



BL0397.h:
Kod:
#ifndef BL0937_h
#define BL0937_h

#include <Arduino.h>

// Internal voltage reference value -- DO NOT CHANGE
#define V_REF               1.218

// The factor of a 1mOhm resistor as per reference circuit in datasheet
// A 1mOhm resistor allows a ~30A max measurement
#define R_CURRENT           0.001

// This is the factor of a voltage divider of 6x 330K upstream and 1k downstream as per reference circuit in datasheet
// e.g. 110mV * (R_VOLTAGE = ~2000) = 220V
#define R_VOLTAGE           (6 * 330) + 1

// Minimum delay between selecting a mode and reading a sample
#define READING_INTERVAL    3000

// Maximum pulse with in microseconds
#define PULSE_TIMEOUT       1000000

// CF1 mode
typedef enum {
    MODE_CURRENT,
    MODE_VOLTAGE
} BL0937_mode_t;

class BL0937 {

    public:

        void cf_interrupt();
        void cf1_interrupt();

        void begin(
            unsigned char cf_pin,
            unsigned char cf1_pin,
            unsigned char sel_pin,
            unsigned char currentWhen = HIGH,
            bool use_interrupts = true,
            unsigned long pulse_timeout = PULSE_TIMEOUT);

        void setMode(BL0937_mode_t mode);
        BL0937_mode_t getMode();
        BL0937_mode_t toggleMode();

        double getCurrent();
        unsigned int getVoltage();
        unsigned int getActivePower();
        unsigned int getApparentPower();
        double getPowerFactor();
        unsigned int getReactivePower();
        unsigned long getEnergy(); //in Ws
        void resetEnergy();

        void setResistors(double current, double voltage_upstream, double voltage_downstream);

        void expectedCurrent(double current);
        void expectedVoltage(unsigned int current);
        void expectedActivePower(unsigned int power);

        double getCurrentMultiplier() { return _current_multiplier; };
        double getVoltageMultiplier() { return _voltage_multiplier; };
        double getPowerMultiplier() { return _power_multiplier; };

        void setCurrentMultiplier(double current_multiplier) { _current_multiplier = current_multiplier; };
        void setVoltageMultiplier(double voltage_multiplier) { _voltage_multiplier = voltage_multiplier; };
        void setPowerMultiplier(double power_multiplier) { _power_multiplier = power_multiplier; };
        void resetMultipliers();

    private:

        unsigned char _cf_pin;
        unsigned char _cf1_pin;
        unsigned char _sel_pin;

        double _current_resistor = R_CURRENT;
        double _voltage_resistor = R_VOLTAGE;

        double _current_multiplier; // Unit: us/A
        double _voltage_multiplier; // Unit: us/V
        double _power_multiplier;   // Unit: us/W

        unsigned long _pulse_timeout = PULSE_TIMEOUT;    // Unit: us
        volatile unsigned long _voltage_pulse_width = 0; // Unit: us
        volatile unsigned long _current_pulse_width = 0; // Unit: us
        volatile unsigned long _power_pulse_width = 0;   // Unit: us
        volatile unsigned long _pulse_count = 0;

        double _current = 0;
        unsigned int _voltage = 0;
        unsigned int _power = 0;

        unsigned char _current_mode = HIGH;
        volatile unsigned char _mode;

        bool _use_interrupts = true;
        volatile unsigned long _last_cf_interrupt = 0;
        volatile unsigned long _last_cf1_interrupt = 0;
        volatile unsigned long _first_cf1_interrupt = 0;

        void _checkCFSignal();
        void _checkCF1Signal();
        void _calculateDefaultMultipliers();

};

#endif

BL0307.cpp
Kod:
#include <Arduino.h>
#include "BL0937.h"

void BL0937::begin(
    unsigned char cf_pin,
    unsigned char cf1_pin,
    unsigned char sel_pin,
    unsigned char currentWhen,
    bool use_interrupts,
    unsigned long pulse_timeout
    ) {

    _cf_pin = cf_pin;
    _cf1_pin = cf1_pin;
    _sel_pin = sel_pin;

    _current_mode = currentWhen;
    _use_interrupts = use_interrupts;
    _pulse_timeout = pulse_timeout;

    pinMode(_cf_pin, INPUT_PULLUP);
    pinMode(_cf1_pin, INPUT_PULLUP);
    pinMode(_sel_pin, OUTPUT);

    _calculateDefaultMultipliers();

    _mode = _current_mode;
    digitalWrite(_sel_pin, _mode);
}

void BL0937::setMode(BL0937_mode_t mode) {
    _mode = (mode == MODE_CURRENT) ? _current_mode : 1 - _current_mode;
    digitalWrite(_sel_pin, _mode);

    if (_use_interrupts) {
        _last_cf1_interrupt = _first_cf1_interrupt = micros();
    }
}

BL0937_mode_t BL0937::getMode() {
    return (_mode == _current_mode) ? MODE_CURRENT : MODE_VOLTAGE;
}

BL0937_mode_t BL0937::toggleMode() {
    BL0937_mode_t new_mode = getMode() == MODE_CURRENT ? MODE_VOLTAGE : MODE_CURRENT;
    setMode(new_mode);
    return new_mode;
}

double BL0937::getCurrent() {

    // Power measurements are more sensitive to switch offs,
    // so we first check if power is 0 to set _current to 0 too
    if (_power == 0) {
        _current_pulse_width = 0;

    } else if (_use_interrupts) {
        _checkCF1Signal();

    } else if (_mode == _current_mode) {
        _current_pulse_width = pulseIn(_cf1_pin, HIGH, _pulse_timeout);

    }

    _current = (_current_pulse_width > 0) ? _current_multiplier / _current_pulse_width / 2 : 0;
    return _current;

}

unsigned int BL0937::getVoltage() {
    if (_use_interrupts) {
        _checkCF1Signal();
    } else if (_mode != _current_mode) {
        _voltage_pulse_width = pulseIn(_cf1_pin, HIGH, _pulse_timeout);
    }

    _voltage = (_voltage_pulse_width > 0) ? _voltage_multiplier / _voltage_pulse_width / 2 : 0;
    return _voltage;
}

unsigned int BL0937::getActivePower() {
    if (_use_interrupts) {
        _checkCFSignal();
    } else {
        _power_pulse_width = pulseIn(_cf_pin, HIGH, _pulse_timeout);
    }

    _power = (_power_pulse_width > 0) ? _power_multiplier / _power_pulse_width / 2 : 0;
    return _power;
}

unsigned int BL0937::getApparentPower() {
    double current = getCurrent();
    unsigned int voltage = getVoltage();
    return voltage * current;
}

unsigned int BL0937::getReactivePower() {
    unsigned int active = getActivePower();
    unsigned int apparent = getApparentPower();
    if (apparent > active) {
        return sqrt(apparent * apparent - active * active);
    } else {
        return 0;
    }
}

double BL0937::getPowerFactor() {
    unsigned int active = getActivePower();
    unsigned int apparent = getApparentPower();
    if (active > apparent) return 1;
    if (apparent == 0) return 0;

    return (double) active / apparent;
}

unsigned long BL0937::getEnergy() {

    // Counting pulses only works in IRQ mode
    if (!_use_interrupts) return 0;

    /*
        Pulse count is directly proportional to energy:
        P = m*f (m=power multiplier, f = Frequency)
        f = N/t (N=pulse count, t = time)
        E = P*t = m*N  (E=energy)
    */
    return _pulse_count * _power_multiplier / 1000000. / 2;

}

void BL0937::resetEnergy() {
    _pulse_count = 0;
}

void BL0937::expectedCurrent(double value) {
    if (_current == 0) getCurrent();
    if (_current > 0) _current_multiplier *= (value / _current);
}

void BL0937::expectedVoltage(unsigned int value) {
    if (_voltage == 0) getVoltage();
    if (_voltage > 0) _voltage_multiplier *= ((double) value / _voltage);
}

void BL0937::expectedActivePower(unsigned int value) {
    if (_power == 0) getActivePower();
    if (_power > 0) _power_multiplier *= ((double) value / _power);
}

void BL0937::resetMultipliers() {
    _calculateDefaultMultipliers();
}

void BL0937::setResistors(double current, double voltage_upstream, double voltage_downstream) {
    if (voltage_downstream > 0) {
        _current_resistor = current;
        _voltage_resistor = (voltage_upstream + voltage_downstream) / voltage_downstream;
        _calculateDefaultMultipliers();
    }
}

void ICACHE_RAM_ATTR BL0937::cf_interrupt() {
    unsigned long now = micros();
    _power_pulse_width = now - _last_cf_interrupt;
    _last_cf_interrupt = now;
    _pulse_count++;
}

void ICACHE_RAM_ATTR BL0937::cf1_interrupt() {

    unsigned long now = micros();
    unsigned long pulse_width;

    if ((now - _first_cf1_interrupt) > _pulse_timeout) {

        if (_last_cf1_interrupt == _first_cf1_interrupt) {
            pulse_width = 0;
        } else {
            pulse_width = now - _last_cf1_interrupt;
        }

        if (_mode == _current_mode) {
            _current_pulse_width = pulse_width;
        } else {
            _voltage_pulse_width = pulse_width;
        }

        _mode = 1 - _mode;
        digitalWrite(_sel_pin, _mode);
        _first_cf1_interrupt = now;

    }

    _last_cf1_interrupt = now;

}

// 1) output pulse width is fixed at 38uS, The frequency is proportional to the power value
// 2) overcurrent indicator. outputs 6.78KHz pulses when overcurrent occurs
void BL0937::_checkCFSignal() {
    if ((micros() - _last_cf_interrupt) > _pulse_timeout) _power_pulse_width = 0;
}

// SEL=0 (MODE_CURRENT), the output current is rms, output pulse width is fixed at 38uS, The frequency is proportional to the current value
// SEL=1 (MODE_VOLTAGE), the output voltage is rms, output pulse width is fixed at 38uS. The frequency is proportional to the voltage value
void BL0937::_checkCF1Signal() {
    if ((micros() - _last_cf1_interrupt) > _pulse_timeout) {
        if (_mode == _current_mode) {
            _current_pulse_width = 0;
        } else {
            _voltage_pulse_width = 0;
        }
        toggleMode();
    }
}

// Active Power Freq = 1721506 * ((VOL_PIN * CUR_PIN) / (V_REF * V_REF))
// Voltage Freq = 15397 * (VOL_PIN / V_REF);
// Current Freq = 94638 * (CUR_PIN / V_REF);
//
void BL0937::_calculateDefaultMultipliers() {
    _power_multiplier   = 1000000.0 * (1721506 / (_voltage_resistor / _current_resistor * (V_REF * V_REF)));
    _voltage_multiplier = 1000000.0 * (15397 / (_voltage_resistor * V_REF));
    _current_multiplier = 1000000.0 * (94638 / (_current_resistor * V_REF));
}

Wskazówki z internetu, to SEL_PIN jest w tym gniazdku INVERTED i żeby pomiar był skuteczny, należy ustawić monitory na FALLING EDGEs. Mnie to za wiele nie rozjaśniło, ale liczę na Waszą pomoc z implementacją tego Smile
 
Odpowiedź
  


Wiadomości w tym wątku
ESP8266EX + BL0397, czyli smart gniazdko i problem z pomiarem prądu - przez Mad_Maxs - 15-11-2019, 20:08

Skocz do:


Przeglądający: 1 gości