Browse Source

First power.ino module

fastled
Xose Pérez 7 years ago
parent
commit
17418d85b2
11 changed files with 770 additions and 160 deletions
  1. +0
    -1
      code/espurna/config/arduino.h
  2. +85
    -14
      code/espurna/config/general.h
  3. +8
    -2
      code/espurna/config/hardware.h
  4. +0
    -74
      code/espurna/config/sensors.h
  5. +2
    -2
      code/espurna/emon.ino
  6. +8
    -19
      code/espurna/espurna.ino
  7. +7
    -3
      code/espurna/hlw8012.ino
  8. +111
    -0
      code/espurna/power.h
  9. +526
    -0
      code/espurna/power.ino
  10. +23
    -31
      code/espurna/web.ino
  11. +0
    -14
      code/espurna/wifi.ino

+ 0
- 1
code/espurna/config/arduino.h View File

@ -54,7 +54,6 @@
//#define DHT_SUPPORT 1
//#define DOMOTICZ_SUPPORT 0
//#define DS18B20_SUPPORT 1
//#define EMON_SUPPORT 1
//#define HOMEASSISTANT_SUPPORT 0
//#define I2C_SUPPORT 1
//#define INFLUXDB_SUPPORT 0


+ 85
- 14
code/espurna/config/general.h View File

@ -423,7 +423,16 @@ PROGMEM const char* const custom_reset_string[] = {
#define MQTT_TOPIC_RFIN "rfin"
#define MQTT_TOPIC_RFLEARN "rflearn"
// Lights
// Power module
#define MQTT_TOPIC_POWER "power"
#define MQTT_TOPIC_CURRENT "current"
#define MQTT_TOPIC_VOLTAGE "voltage"
#define MQTT_TOPIC_APPARENT "apower"
#define MQTT_TOPIC_REACTIVE "rpower"
#define MQTT_TOPIC_POWER_FACTOR "pfactor"
#define MQTT_TOPIC_ENERGY "energy"
// Light module
#define MQTT_TOPIC_CHANNEL "channel"
#define MQTT_TOPIC_COLOR "color"
#define MQTT_TOPIC_BRIGHTNESS "brightness"
@ -454,19 +463,6 @@ PROGMEM const char* const custom_reset_string[] = {
#define SETTINGS_AUTOSAVE 1 // Autosave settings o force manual commit
#endif
// -----------------------------------------------------------------------------
// I2C
// -----------------------------------------------------------------------------
#ifndef I2C_SUPPORT
#define I2C_SUPPORT 0 // I2C enabled
#endif
#define I2C_SDA_PIN 4 // SDA GPIO
#define I2C_SCL_PIN 14 // SCL GPIO
#define I2C_CLOCK_STRETCH_TIME 200 // BRZO clock stretch time
#define I2C_SCL_FREQUENCY 1000 // BRZO SCL frequency
// -----------------------------------------------------------------------------
// LIGHT
// -----------------------------------------------------------------------------
@ -495,6 +491,81 @@ PROGMEM const char* const custom_reset_string[] = {
#define LIGHT_USE_WHITE 0 // Use white channel whenever RGB have the same value
#define LIGHT_USE_GAMMA 0 // Use gamma correction for color channels
// -----------------------------------------------------------------------------
// POWER METERING
// -----------------------------------------------------------------------------
// Available power-metering providers
#define POWER_PROVIDER_NONE 0x00
#define POWER_PROVIDER_EMON 0x10
#define POWER_PROVIDER_EMON_ANALOG 0x10
#define POWER_PROVIDER_EMON_ADC121 0x11
#define POWER_PROVIDER_HLW8012 0x20
// Available magnitudes
#define POWER_MAGNITUDE_CURRENT 1
#define POWER_MAGNITUDE_VOLTAGE 2
#define POWER_MAGNITUDE_ACTIVE 4
#define POWER_MAGNITUDE_APPARENT 8
// No power provider defined
#ifndef POWER_PROVIDER
#define POWER_PROVIDER POWER_PROVIDER_NONE
#endif
// Identify available magnitudes
#if POWER_PROVIDER == POWER_PROVIDER_HLW8012
#define POWER_HAS_ACTIVE 1
#else
#define POWER_HAS_ACTIVE 0
#endif
#define POWER_CURRENT_PRECISION 3
#define POWER_VOLTAGE 230
#define POWER_CURRENT_RATIO 30
#define POWER_SAMPLES 1000
#define POWER_INTERVAL 10000
#define POWER_REPORT_EVERY 6
#define POWER_ENERGY_FACTOR (POWER_INTERVAL * POWER_REPORT_EVERY / 1000.0 / 3600.0)
#if POWER_PROVIDER == POWER_PROVIDER_EMON_ANALOG
#define POWER_ADC_BITS 10
#define POWER_REFERENCE_VOLTAGE 1.0
#define POWER_CURRENT_OFFSET 0.25
#undef ADC_VCC_ENABLED
#define ADC_VCC_ENABLED 0
#endif
#if POWER_PROVIDER == POWER_PROVIDER_EMON_ADC121
#define POWER_I2C_ADDRESS 0x50
#define POWER_ADC_BITS 12
#define POWER_REFERENCE_VOLTAGE 3.3
#define POWER_CURRENT_OFFSET 0.10
#undef I2C_SUPPORT
#define I2C_SUPPORT 1
#endif
#if POWER_PROVIDER == POWER_PROVIDER_HLW8012
#define HLW8012_USE_INTERRUPTS 1
#define HLW8012_SEL_CURRENT HIGH
#define HLW8012_CURRENT_R 0.001
#define HLW8012_VOLTAGE_R_UP ( 5 * 470000 ) // Real: 2280k
#define HLW8012_VOLTAGE_R_DOWN ( 1000 ) // Real 1.009k
#endif
// -----------------------------------------------------------------------------
// I2C
// -----------------------------------------------------------------------------
#ifndef I2C_SUPPORT
#define I2C_SUPPORT 0 // I2C enabled
#endif
#define I2C_SDA_PIN 4 // SDA GPIO
#define I2C_SCL_PIN 14 // SCL GPIO
#define I2C_CLOCK_STRETCH_TIME 200 // BRZO clock stretch time
#define I2C_SCL_FREQUENCY 1000 // BRZO SCL frequency
// -----------------------------------------------------------------------------
// DOMOTICZ
// -----------------------------------------------------------------------------


+ 8
- 2
code/espurna/config/hardware.h View File

@ -42,6 +42,12 @@
#define LED1_PIN 2
#define LED1_PIN_INVERSE 1
// HLW8012
#define POWER_PROVIDER POWER_PROVIDER_EMON_ANALOG
#define HLW8012_SEL_PIN 2
#define HLW8012_CF1_PIN 13
#define HLW8012_CF_PIN 14
#elif defined(WEMOS_D1_MINI_RELAYSHIELD)
// Info
@ -83,7 +89,7 @@
#define LED1_PIN_INVERSE 0
// HLW8012
#define HLW8012_SUPPORT 1
#define POWER_PROVIDER POWER_PROVIDER_HLW8012
#define HLW8012_SEL_PIN 2
#define HLW8012_CF1_PIN 13
#define HLW8012_CF_PIN 14
@ -245,7 +251,7 @@
#define LED1_PIN_INVERSE 0
// HLW8012
#define HLW8012_SUPPORT 1
#define POWER_PROVIDER POWER_PROVIDER_HLW8012
#define HLW8012_SEL_PIN 5
#define HLW8012_CF1_PIN 13
#define HLW8012_CF_PIN 14


+ 0
- 74
code/espurna/config/sensors.h View File

@ -81,80 +81,6 @@
#define DS18B20_UPDATE_INTERVAL 60000
#define DS18B20_TEMPERATURE_TOPIC "temperature"
//--------------------------------------------------------------------------------
// Custom current sensor
// Check http://tinkerman.cat/your-laundry-is-done/
// Check http://tinkerman.cat/power-monitoring-sonoff-th-adc121/
// Enable support by passing EMON_SUPPORT=1 build flag
//--------------------------------------------------------------------------------
#ifndef EMON_SUPPORT
#define EMON_SUPPORT 0
#endif
#define EMON_ANALOG_PROVIDER 0
#define EMON_ADC121_PROVIDER 1
// If you select EMON_ADC121_PROVIDER you need to enable and configure I2C in general.h
#define EMON_PROVIDER EMON_ANALOG_PROVIDER
#if EMON_PROVIDER == EMON_ANALOG_PROVIDER
#define EMON_CURRENT_PIN 0
#define EMON_ADC_BITS 10
#define EMON_REFERENCE_VOLTAGE 1.0
#define EMON_CURRENT_OFFSET 0.25
#if EMON_SUPPORT
#undef ADC_VCC_ENABLED
#define ADC_VCC_ENABLED 0
#endif
#endif
#if EMON_PROVIDER == EMON_ADC121_PROVIDER
#define EMON_ADC121_ADDRESS 0x50
#define EMON_ADC_BITS 12
#define EMON_REFERENCE_VOLTAGE 3.3
#define EMON_CURRENT_OFFSET 0.10
#endif
#define EMON_CURRENT_RATIO 30
#define EMON_SAMPLES 1000
#define EMON_INTERVAL 10000
#define EMON_MEASUREMENTS 6
#define EMON_MAINS_VOLTAGE 230
#define EMON_APOWER_TOPIC "apower"
#define EMON_ENERGY_TOPIC "energy"
#define EMON_CURRENT_TOPIC "current"
//--------------------------------------------------------------------------------
// HLW8012 power sensor (Sonoff POW, Espurna H)
// Enable support by passing HLW8012_SUPPORT=1 build flag
//--------------------------------------------------------------------------------
#ifndef HLW8012_SUPPORT
#define HLW8012_SUPPORT 0
#endif
// GPIOs defined in the hardware.h file
#define HLW8012_USE_INTERRUPTS 1
#define HLW8012_SEL_CURRENT HIGH
#define HLW8012_CURRENT_R 0.001
#define HLW8012_VOLTAGE_R_UP ( 5 * 470000 ) // Real: 2280k
#define HLW8012_VOLTAGE_R_DOWN ( 1000 ) // Real 1.009k
#define HLW8012_POWER_TOPIC "power"
#define HLW8012_CURRENT_TOPIC "current"
#define HLW8012_VOLTAGE_TOPIC "voltage"
#define HLW8012_APOWER_TOPIC "apower"
#define HLW8012_RPOWER_TOPIC "rpower"
#define HLW8012_PFACTOR_TOPIC "pfactor"
#define HLW8012_ENERGY_TOPIC "energy"
#define HLW8012_UPDATE_INTERVAL 5000
#define HLW8012_REPORT_EVERY 12
#define HLW8012_MIN_POWER 5
#define HLW8012_MAX_POWER 2500
#define HLW8012_MIN_CURRENT 0.05
#define HLW8012_MAX_CURRENT 10
//--------------------------------------------------------------------------------
// Internal power montior
// Enable support by passing ADC_VCC_ENABLED=1 build flag


+ 2
- 2
code/espurna/emon.ino View File

@ -6,10 +6,10 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
*/
/*
#if EMON_SUPPORT
#include <EmonLiteESP.h>
#include <EEPROM.h>
#if EMON_PROVIDER == EMON_ADC121_PROVIDER
#include "brzo_i2c.h"
#endif
@ -83,7 +83,6 @@ unsigned int getVoltage() {
void powerMonitorSetup() {
// backwards compatibility
String tmp;
moveSetting("pwMainsVoltage", "emonVoltage");
moveSetting("emonMains", "emonVoltage");
moveSetting("pwCurrentRatio", "emonRatio");
@ -215,3 +214,4 @@ void powerMonitorLoop() {
}
#endif
*/

+ 8
- 19
code/espurna/espurna.ino View File

@ -155,12 +155,6 @@ void welcome() {
#if DS18B20_SUPPORT
DEBUG_MSG_P(PSTR(" DS18B20"));
#endif
#if EMON_SUPPORT
DEBUG_MSG_P(PSTR(" EMON"));
#endif
#if HLW8012_SUPPORT
DEBUG_MSG_P(PSTR(" HLW8012"));
#endif
#if HOMEASSISTANT_SUPPORT
DEBUG_MSG_P(PSTR(" HOMEASSISTANT"));
#endif
@ -224,7 +218,6 @@ void setup() {
settingsSetup();
if (getSetting("hostname").length() == 0) {
setSetting("hostname", getIdentifier());
saveSettings();
}
delay(500);
@ -253,6 +246,9 @@ void setup() {
rfbSetup();
#endif
#if POWER_PROVIDER != POWER_PROVIDER_NONE
powerSetup();
#endif
#if NTP_SUPPORT
ntpSetup();
#endif
@ -268,9 +264,6 @@ void setup() {
#if INFLUXDB_SUPPORT
influxDBSetup();
#endif
#if HLW8012_SUPPORT
hlw8012Setup();
#endif
#if DS18B20_SUPPORT
dsSetup();
#endif
@ -286,9 +279,6 @@ void setup() {
#if RF_SUPPORT
rfSetup();
#endif
#if EMON_SUPPORT
powerMonitorSetup();
#endif
#if DOMOTICZ_SUPPORT
domoticzSetup();
#endif
@ -296,6 +286,8 @@ void setup() {
// Prepare configuration for version 2.0
hwUpwardsCompatibility();
saveSettings();
}
void loop() {
@ -317,6 +309,9 @@ void loop() {
rfbLoop();
#endif
#if POWER_PROVIDER != POWER_PROVIDER_NONE
powerLoop();
#endif
#if NTP_SUPPORT
ntpLoop();
#endif
@ -326,9 +321,6 @@ void loop() {
#if NOFUSS_SUPPORT
nofussLoop();
#endif
#if HLW8012_SUPPORT
hlw8012Loop();
#endif
#if DS18B20_SUPPORT
dsLoop();
#endif
@ -344,8 +336,5 @@ void loop() {
#if RF_SUPPORT
rfLoop();
#endif
#if EMON_SUPPORT
powerMonitorLoop();
#endif
}

+ 7
- 3
code/espurna/hlw8012.ino View File

@ -7,6 +7,8 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
*/
/*
#if HLW8012_SUPPORT
#include <HLW8012.h>
@ -124,9 +126,9 @@ double getCurrent() {
return current;
}
unsigned int getVoltage() {
return hlw8012.getVoltage();
}
//unsigned int getVoltage() {
// return hlw8012.getVoltage();
//}
double getPowerFactor() {
return hlw8012.getPowerFactor();
@ -342,3 +344,5 @@ void hlw8012Loop() {
}
#endif
*/

+ 111
- 0
code/espurna/power.h View File

@ -0,0 +1,111 @@
// -----------------------------------------------------------------------------
// Stream Injector
// -----------------------------------------------------------------------------
#pragma once
/*
class SpikesFilter {
public:
SpikesFilter() {
reset();
}
virtual void reset() {
_sum = 0;
_spike = false;
}
virtual void add(double value) {
// add previous value
if (_last > 0) {
_sum += _last;
}
// flag new possible spike
if (value > 0) {
_spike = (_last == 0);
// delete previous spike
} else if (_spike) {
_sum -= _last;
_spike = false;
}
_last = value;
}
virtual double sum() {
return _sum;
}
private:
double _last = 0;
double _sum = 0;
bool _spike = false;
};
*/
class MedianFilter {
public:
MedianFilter(unsigned char size) {
_size = size;
_data = new double[_size+1];
reset();
}
virtual void reset() {
_data[0] = _data[_size];
_pointer = 1;
for (unsigned char i=_pointer; i<=_size; i++) _data[i] = 0;
}
virtual void add(double value) {
if (_pointer <= _size) {
_data[_pointer] = value;
++_pointer;
}
}
virtual double average(bool do_reset = false) {
double sum = 0;
for (unsigned char i = 1; i<_size; i++) {
double previous = _data[i-1];
double current = _data[i];
double next = _data[i+1];
if (previous > current) std::swap(previous, current);
if (current > next) std::swap(current, next);
if (previous > current) std::swap(previous, current);
sum += current;
}
if (do_reset) reset();
return sum / (_size-1);
}
virtual unsigned char count() {
return _pointer - 1;
}
private:
double *_data;
unsigned char _size = 0;
unsigned char _pointer = 0;
};

+ 526
- 0
code/espurna/power.ino View File

@ -0,0 +1,526 @@
/*
POWER MODULE
Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
*/
#if POWER_PROVIDER != POWER_PROVIDER_NONE
// -----------------------------------------------------------------------------
// MODULE GLOBALS AND CACHE
// -----------------------------------------------------------------------------
#include "power.h"
#include <Hash.h>
#include <ArduinoJson.h>
bool _power_enabled = false;
bool _power_ready = false;
double _power_current = 0;
double _power_voltage = 0;
double _power_apparent = 0;
MedianFilter _filter_current = MedianFilter(POWER_REPORT_EVERY);
#if POWER_HAS_ACTIVE
double _power_active = 0;
MedianFilter _filter_voltage = MedianFilter(POWER_REPORT_EVERY);
MedianFilter _filter_active = MedianFilter(POWER_REPORT_EVERY);
MedianFilter _filter_apparent = MedianFilter(POWER_REPORT_EVERY);
#endif
#if POWER_PROVIDER & POWER_PROVIDER_EMON
#include <EmonLiteESP.h>
EmonLiteESP _emon;
#endif
#if POWER_PROVIDER == POWER_PROVIDER_EMON_ADC121
#include "brzo_i2c.h"
// ADC121 Registers
#define ADC121_REG_RESULT 0x00
#define ADC121_REG_ALERT 0x01
#define ADC121_REG_CONFIG 0x02
#define ADC121_REG_LIMITL 0x03
#define ADC121_REG_LIMITH 0x04
#define ADC121_REG_HYST 0x05
#define ADC121_REG_CONVL 0x06
#define ADC121_REG_CONVH 0x07
#endif // POWER_PROVIDER == POWER_PROVIDER_EMON_ADC121
#if POWER_PROVIDER == POWER_PROVIDER_HLW8012
#include <HLW8012.h>
#include <ESP8266WiFi.h>
HLW8012 _hlw8012;
WiFiEventHandler _power_wifi_onconnect;
WiFiEventHandler _power_wifi_ondisconnect;
#endif // POWER_PROVIDER == POWER_PROVIDER_HLW8012
// -----------------------------------------------------------------------------
// PROVIDERS
// -----------------------------------------------------------------------------
#if POWER_PROVIDER & POWER_PROVIDER_EMON
unsigned int currentCallback() {
#if POWER_PROVIDER == POWER_PROVIDER_EMON_ANALOG
return analogRead(0);
#endif // POWER_PROVIDER == POWER_PROVIDER_EMON_ANALOG
#if POWER_PROVIDER == POWER_PROVIDER_EMON_ADC121
uint8_t buffer[2];
brzo_i2c_start_transaction(POWER_I2C_ADDRESS, I2C_SCL_FREQUENCY);
buffer[0] = ADC121_REG_RESULT;
brzo_i2c_write(buffer, 1, false);
brzo_i2c_read(buffer, 2, false);
brzo_i2c_end_transaction();
unsigned int value;
value = (buffer[0] & 0x0F) << 8;
value |= buffer[1];
return value;
#endif // POWER_PROVIDER == POWER_PROVIDER_EMON_ADC121
}
#endif // POWER_PROVIDER & POWER_PROVIDER_EMON
#if POWER_PROVIDER == POWER_PROVIDER_HLW8012
void ICACHE_RAM_ATTR _hlw_cf1_isr() {
_hlw8012.cf1_interrupt();
}
void ICACHE_RAM_ATTR _hlw_cf_isr() {
_hlw8012.cf_interrupt();
}
void _hlwSetCalibration() {
double value;
value = getSetting("powerRatioP", 0).toFloat();
if (value > 0) _hlw8012.setPowerMultiplier(value);
value = getSetting("powerRatioC", 0).toFloat();
if (value > 0) _hlw8012.setCurrentMultiplier(value);
value = getSetting("powerRatioV", 0).toFloat();
if (value > 0) _hlw8012.setVoltageMultiplier(value);
}
void _hlwGetCalibration() {
setSetting("powerRatioP", _hlw8012.getPowerMultiplier());
setSetting("powerRatioC", _hlw8012.getCurrentMultiplier());
setSetting("powerRatioV", _hlw8012.getVoltageMultiplier());
saveSettings();
}
void _hlwResetCalibration() {
_hlw8012.resetMultipliers();
_hlwGetCalibration();
}
void _hlwExpectedPower(unsigned int power) {
if (power > 0) {
_hlw8012.expectedActivePower(power);
_hlwGetCalibration();
}
}
void _hlwExpectedCurrent(double current) {
if (current > 0) {
_hlw8012.expectedCurrent(current);
_hlwGetCalibration();
}
}
void _hlwExpectedVoltage(unsigned int voltage) {
if (voltage > 0) {
_hlw8012.expectedVoltage(voltage);
_hlwGetCalibration();
}
}
#endif
double _powerCurrent() {
#if POWER_PROVIDER & POWER_PROVIDER_EMON
double current = _emon.getCurrent(POWER_SAMPLES);
current -= POWER_CURRENT_OFFSET;
if (current < 0) current = 0;
return current;
#elif POWER_PROVIDER == POWER_PROVIDER_HLW8012
return _hlw8012.getCurrent();
#else
return 0;
#endif
}
double _powerVoltage() {
#if POWER_PROVIDER & POWER_PROVIDER_EMON
return _power_voltage;
#elif POWER_PROVIDER == POWER_PROVIDER_HLW8012
return _hlw8012.getVoltage();
#else
return 0;
#endif
}
#if POWER_HAS_ACTIVE
double _powerActivePower() {
#if POWER_PROVIDER == POWER_PROVIDER_HLW8012
return _hlw8012.getActivePower();
#else
return 0;
#endif
}
#endif
double _powerApparentPower() {
#if POWER_PROVIDER & POWER_PROVIDER_EMON
return _powerCurrent() * _powerVoltage();
#elif POWER_PROVIDER == POWER_PROVIDER_HLW8012
return _hlw8012.getApparentPower();
#else
return 0;
#endif
}
// -----------------------------------------------------------------------------
// PRIVATE METHODS
// -----------------------------------------------------------------------------
#if WEB_SUPPORT
void _powerAPISetup() {
apiRegister(MQTT_TOPIC_CURRENT, MQTT_TOPIC_CURRENT, [](char * buffer, size_t len) {
if (_power_ready) {
dtostrf(getCurrent(), len-1, POWER_CURRENT_PRECISION, buffer);
} else {
buffer = NULL;
}
});
apiRegister(MQTT_TOPIC_VOLTAGE, MQTT_TOPIC_VOLTAGE, [](char * buffer, size_t len) {
if (_power_ready) {
snprintf_P(buffer, len, PSTR("%d"), getVoltage());
} else {
buffer = NULL;
}
});
apiRegister(MQTT_TOPIC_APPARENT, MQTT_TOPIC_APPARENT, [](char * buffer, size_t len) {
if (_power_ready) {
snprintf_P(buffer, len, PSTR("%d"), getApparentPower());
} else {
buffer = NULL;
}
});
#if POWER_HAS_ACTIVE
apiRegister(MQTT_TOPIC_POWER, MQTT_TOPIC_POWER, [](char * buffer, size_t len) {
if (_power_ready) {
snprintf_P(buffer, len, PSTR("%d"), getActivePower());
} else {
buffer = NULL;
}
});
#endif
}
#endif // WEB_SUPPORT
void _powerReset() {
_filter_current.reset();
#if POWER_HAS_ACTIVE
_filter_apparent.reset();
_filter_voltage.reset();
_filter_active.reset();
#endif
}
// -----------------------------------------------------------------------------
// MAGNITUDE API
// -----------------------------------------------------------------------------
bool hasActivePower() {
return POWER_HAS_ACTIVE;
}
double getCurrent() {
return _power_current;
}
double getVoltage() {
return _power_voltage;
}
double getApparentPower() {
return _power_apparent;
}
#if POWER_HAS_ACTIVE
double getActivePower() {
return _power_active;
}
double getReactivePower() {
if (_power_apparent > _power_active) {
return sqrt(_power_apparent * _power_apparent - _power_active * _power_active);
}
return 0;
}
double getPowerFactor() {
if (_power_active > _power_apparent) return 1;
if (_power_apparent == 0) return 0;
return (double) _power_active / _power_apparent;
}
#endif
// -----------------------------------------------------------------------------
// PUBLIC API
// -----------------------------------------------------------------------------
bool powerEnabled() {
return _power_enabled;
}
void powerEnabled(bool enabled) {
_power_enabled = enabled;
#if (POWER_PROVIDER == POWER_PROVIDER_HLW8012) && HLW8012_USE_INTERRUPTS
if (_power_enabled) {
attachInterrupt(HLW8012_CF1_PIN, _hlw_cf1_isr, CHANGE);
attachInterrupt(HLW8012_CF_PIN, _hlw_cf_isr, CHANGE);
} else {
detachInterrupt(HLW8012_CF1_PIN);
detachInterrupt(HLW8012_CF_PIN);
}
#endif
}
void powerConfigure() {
#if POWER_PROVIDER & POWER_PROVIDER_EMON
_emon.setCurrentRatio(getSetting("powerRatioC", POWER_CURRENT_RATIO).toFloat());
_power_voltage = getSetting("powerVoltage", POWER_VOLTAGE).toFloat();
#endif
#if POWER_PROVIDER == POWER_PROVIDER_HLW8012
_hlwSetCalibration();
_hlwGetCalibration();
#endif
}
void powerSetup() {
// backwards compatibility
moveSetting("pwMainsVoltage", "powerVoltage");
moveSetting("emonMains", "powerVoltage");
moveSetting("emonVoltage", "powerVoltage");
moveSetting("pwCurrentRatio", "powerRatioC");
moveSetting("emonRatio", "powerRatioC");
moveSetting("powPowerMult", "powerRatioP");
moveSetting("powCurrentMult", "powerRatioC");
moveSetting("powVoltageMult", "powerRatioV");
#if POWER_PROVIDER == POWER_PROVIDER_HLW8012
// Initialize HLW8012
// void begin(unsigned char cf_pin, unsigned char cf1_pin, unsigned char sel_pin, unsigned char currentWhen = HIGH, bool use_interrupts = false, unsigned long pulse_timeout = PULSE_TIMEOUT);
// * cf_pin, cf1_pin and sel_pin are GPIOs to the HLW8012 IC
// * currentWhen is the value in sel_pin to select current sampling
// * set use_interrupts to true to use interrupts to monitor pulse widths
// * leave pulse_timeout to the default value, recommended when using interrupts
#if HLW8012_USE_INTERRUPTS
_hlw8012.begin(HLW8012_CF_PIN, HLW8012_CF1_PIN, HLW8012_SEL_PIN, HLW8012_SEL_CURRENT, true);
#else
_hlw8012.begin(HLW8012_CF_PIN, HLW8012_CF1_PIN, HLW8012_SEL_PIN, HLW8012_SEL_CURRENT, false, 1000000);
#endif
// These values are used to calculate current, voltage and power factors as per datasheet formula
// These are the nominal values for the Sonoff POW resistors:
// * The CURRENT_RESISTOR is the 1milliOhm copper-manganese resistor in series with the main line
// * The VOLTAGE_RESISTOR_UPSTREAM are the 5 470kOhm resistors in the voltage divider that feeds the V2P pin in the HLW8012
// * The VOLTAGE_RESISTOR_DOWNSTREAM is the 1kOhm resistor in the voltage divider that feeds the V2P pin in the HLW8012
_hlw8012.setResistors(HLW8012_CURRENT_R, HLW8012_VOLTAGE_R_UP, HLW8012_VOLTAGE_R_DOWN);
#endif // POWER_PROVIDER == POWER_PROVIDER_HLW8012
#if POWER_PROVIDER & POWER_PROVIDER_EMON
_emon.initCurrent(currentCallback, POWER_ADC_BITS, POWER_REFERENCE_VOLTAGE, POWER_CURRENT_RATIO);
#endif
#if POWER_PROVIDER == POWER_PROVIDER_EMON_ADC121
uint8_t buffer[2];
buffer[0] = ADC121_REG_CONFIG;
buffer[1] = 0x00;
brzo_i2c_start_transaction(POWER_I2C_ADDRESS, I2C_SCL_FREQUENCY);
brzo_i2c_write(buffer, 2, false);
brzo_i2c_end_transaction();
#endif
powerConfigure();
#if POWER_PROVIDER & POWER_PROVIDER_EMON
_emon.warmup();
#endif
#if POWER_PROVIDER == POWER_PROVIDER_HLW8012
_power_wifi_onconnect = WiFi.onStationModeGotIP([](WiFiEventStationModeGotIP ipInfo) {
powerEnabled(true);
});
_power_wifi_ondisconnect = WiFi.onStationModeDisconnected([](WiFiEventStationModeDisconnected ipInfo) {
powerEnabled(false);
});
#endif
// API
#if WEB_SUPPORT
_powerAPISetup();
#endif
DEBUG_MSG_P(PSTR("[POWER] POWER_PROVIDER = %d\n"), POWER_PROVIDER);
}
void powerLoop() {
static unsigned long last = 0;
static bool was_disabled = false;
if (!_power_enabled) {
was_disabled = true;
return;
}
if (was_disabled) {
was_disabled = false;
last = millis();
_powerReset();
}
if (millis() - last < POWER_INTERVAL) return;
last = millis();
// Get instantaneous values from HAL
double current = _powerCurrent();
double voltage = _powerVoltage();
double apparent = _powerApparentPower();
#if POWER_HAS_ACTIVE
double active = _powerActivePower();
#endif
// Filters
_filter_current.add(current);
#if POWER_HAS_ACTIVE
_filter_apparent.add(apparent);
_filter_voltage.add(voltage);
_filter_active.add(active);
#endif
char current_buffer[10];
dtostrf(current, sizeof(current_buffer)-1, POWER_CURRENT_PRECISION, current_buffer);
DEBUG_MSG_P(PSTR("[POWER] Current: %sA\n"), current_buffer);
DEBUG_MSG_P(PSTR("[POWER] Voltage: %sA\n"), voltage);
DEBUG_MSG_P(PSTR("[POWER] Apparent Power: %dW\n"), apparent);
#if POWER_HAS_ACTIVE
DEBUG_MSG_P(PSTR("[POWER] Active Power: %dW\n"), active);
DEBUG_MSG_P(PSTR("[POWER] Reactive Power: %dW\n"), getReactivePower());
DEBUG_MSG_P(PSTR("[POWER] Power Factor: %d%%\n"), 100 * getPowerFactor());
#endif
// Update websocket clients
#if WEB_SUPPORT
{
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root["powerVisible"] = 1;
root["powerCurrent"] = String(current_buffer);
root["powerVoltage"] = voltage;
root["powerApparentPower"] = apparent;
#if POWER_HAS_ACTIVE
root["powerActivePower"] = active;
root["powerReactivePower"] = getReactivePower();
root["powerPowerfactor"] = int(100 * getPowerFactor());
#endif
String output;
root.printTo(output);
wsSend(output.c_str());
}
#endif
// Send MQTT messages averaged every POWER_REPORT_EVERY measurements
if (_filter_current.count() == POWER_REPORT_EVERY) {
// Get the fitered values
_power_current = _filter_current.average(true);
#if POWER_HAS_ACTIVE
_power_apparent = _filter_apparent.average(true);
_power_voltage = _filter_voltage.average(true);
_power_active = _filter_active.average(true);
double power = _power_active;
#else
_power_apparent = _power_current * _power_voltage;
double power = _power_apparent;
#endif
double delta_energy = power * POWER_ENERGY_FACTOR;
char delta_energy_buffer[10];
dtostrf(delta_energy, sizeof(delta_energy_buffer)-1, POWER_CURRENT_PRECISION, delta_energy_buffer);
_power_ready = true;
// Report values to MQTT broker
{
mqttSend(MQTT_TOPIC_CURRENT, current_buffer);
mqttSend(MQTT_TOPIC_APPARENT, String((int) _power_apparent).c_str());
mqttSend(MQTT_TOPIC_ENERGY, delta_energy_buffer);
#if POWER_HAS_ACTIVE
mqttSend(MQTT_TOPIC_POWER, String((int) _power_active).c_str());
mqttSend(MQTT_TOPIC_VOLTAGE, String((int) _power_voltage).c_str());
#endif
}
// Report values to Domoticz
#if DOMOTICZ_SUPPORT
{
char buffer[20];
snprintf_P(buffer, sizeof(buffer), PSTR("%d;%s"), power, delta_energy_buffer);
domoticzSend("dczPowIdx", 0, buffer);
domoticzSend("dczEnergyIdx", 0, delta_energy_buffer);
domoticzSend("dczCurrentIdx", 0, current_buffer);
#if POWER_HAS_ACTIVE
snprintf_P(buffer, sizeof(buffer), PSTR("%d"), _power_voltage);
domoticzSend("dczVoltIdx", 0, buffer);
#endif
}
#endif
#if INFLUXDB_SUPPORT
{
influxDBSend(MQTT_TOPIC_CURRENT, current_buffer);
influxDBSend(MQTT_TOPIC_APPARENT, String((int) _power_apparent).c_str());
influxDBSend(MQTT_TOPIC_ENERGY, delta_energy_buffer);
#if POWER_HAS_ACTIVE
influxDBSend(MQTT_TOPIC_POWER, String((int) _power_active).c_str());
influxDBSend(MQTT_TOPIC_VOLTAGE, String((int) _power_voltage).c_str());
#endif
}
#endif
}
// Toggle between current and voltage monitoring
#if (POWER_PROVIDER == POWER_PROVIDER_HLW8012) && (HLW8012_USE_INTERRUPTS == 0)
_hlw8012.toggleMode();
#endif // (POWER_PROVIDER == POWER_PROVIDER_HLW8012) && (HLW8012_USE_INTERRUPTS == 0)
}
#endif // POWER_PROVIDER != POWER_PROVIDER_NONE

+ 23
- 31
code/espurna/web.ino View File

@ -10,6 +10,7 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Hash.h>
#include <FS.h>
#include <AsyncJson.h>
#include <ArduinoJson.h>
@ -228,26 +229,26 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
// Skip firmware filename
if (key.equals("filename")) continue;
#if HLW8012_SUPPORT
#if POWER_PROVIDER == POWER_PROVIDER_HLW8012
if (key == "powExpectedPower") {
hlw8012SetExpectedActivePower(value.toInt());
_hlwExpectedPower(value.toInt());
changed = true;
}
if (key == "powExpectedVoltage") {
hlw8012SetExpectedVoltage(value.toInt());
_hlwExpectedVoltage(value.toInt());
changed = true;
}
if (key == "powExpectedCurrent") {
hlw8012SetExpectedCurrent(value.toFloat());
_hlwExpectedCurrent(value.toFloat());
changed = true;
}
if (key == "powExpectedReset") {
if (value.toInt() == 1) {
hlw8012Reset();
_hlwResetCalibration();
changed = true;
}
}
@ -388,8 +389,8 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
#if RF_SUPPORT
rfBuildCodes();
#endif
#if EMON_SUPPORT
setCurrentRatio(getSetting("emonRatio").toFloat());
#if POWER_PROVIDER != POWER_PROVIDER_NONE
powerConfigure();
#endif
#if NTP_SUPPORT
if (changedNTP) ntpConnect();
@ -533,21 +534,17 @@ void _wsStart(uint32_t client_id) {
root["dczTmpIdx"] = getSetting("dczTmpIdx").toInt();
#endif
#if EMON_SUPPORT
root["dczPowIdx"] = getSetting("dczPowIdx").toInt();
root["dczEnergyIdx"] = getSetting("dczEnergyIdx").toInt();
root["dczCurrentIdx"] = getSetting("dczCurrentIdx").toInt();
#endif
#if ANALOG_SUPPORT
root["dczAnaIdx"] = getSetting("dczAnaIdx").toInt();
#endif
#if HLW8012_SUPPORT
#if POWER_PROVIDER != POWER_PROVIDER_NONE
root["dczPowIdx"] = getSetting("dczPowIdx").toInt();
root["dczEnergyIdx"] = getSetting("dczEnergyIdx").toInt();
root["dczVoltIdx"] = getSetting("dczVoltIdx").toInt();
root["dczCurrentIdx"] = getSetting("dczCurrentIdx").toInt();
#if POWER_HAS_ACTIVE
root["dczVoltIdx"] = getSetting("dczVoltIdx").toInt();
#endif
#endif
#endif
@ -583,14 +580,6 @@ void _wsStart(uint32_t client_id) {
root["rfDevice"] = getSetting("rfDevice", RF_DEVICE);
#endif
#if EMON_SUPPORT
root["emonVisible"] = 1;
root["emonApparentPower"] = getApparentPower();
root["emonCurrent"] = getCurrent();
root["emonVoltage"] = getVoltage();
root["emonRatio"] = getSetting("emonRatio", EMON_CURRENT_RATIO);
#endif
#if ANALOG_SUPPORT
root["analogVisible"] = 1;
root["analogValue"] = getAnalog();
@ -601,14 +590,17 @@ void _wsStart(uint32_t client_id) {
root["counterValue"] = getCounter();
#endif
#if HLW8012_SUPPORT
root["powVisible"] = 1;
root["powActivePower"] = getActivePower();
root["powApparentPower"] = getApparentPower();
root["powReactivePower"] = getReactivePower();
root["powVoltage"] = getVoltage();
root["powCurrent"] = String(getCurrent(), 3);
root["powPowerFactor"] = String(getPowerFactor(), 2);
#if POWER_PROVIDER != POWER_PROVIDER_NONE
root["powerVisible"] = 1;
root["powerCurrent"] = getCurrent();
root["powerVoltage"] = getVoltage();
root["powerApparentPower"] = getApparentPower();
#if POWER_HAS_ACTIVE
root["powerActivePower"] = getActivePower();
root["powerReactivePower"] = getReactivePower();
root["powerPowerfactor"] = int(100 * getPowerFactor());
#endif
root["powerRatioC"] = getSetting("powerRatioC", POWER_CURRENT_RATIO);
#endif
#if NOFUSS_SUPPORT


+ 0
- 14
code/espurna/wifi.ino View File

@ -28,9 +28,6 @@ String getNetwork() {
}
void wifiDisconnect() {
#if HLW8012_SUPPORT
hlw8012Enable(false);
#endif
jw.disconnect();
}
@ -263,17 +260,6 @@ void wifiSetup() {
}
#endif
// Manage POW
#if HLW8012_SUPPORT
if (code == MESSAGE_CONNECTED) {
hlw8012Enable(true);
}
if (code == MESSAGE_DISCONNECTED) {
hlw8012Enable(false);
}
#endif
});
}


Loading…
Cancel
Save