@ -1,217 +0,0 @@ | |||
/* | |||
EMON MODULE | |||
Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com> | |||
*/ | |||
/* | |||
#if EMON_SUPPORT | |||
#include <EmonLiteESP.h> | |||
#if EMON_PROVIDER == EMON_ADC121_PROVIDER | |||
#include "brzo_i2c.h" | |||
#endif | |||
// 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 | |||
EmonLiteESP emon; | |||
bool _emonReady = false; | |||
double _emonCurrent = 0; | |||
unsigned int _emonPower = 0; | |||
unsigned int _emonVoltage = 0; | |||
// ----------------------------------------------------------------------------- | |||
// Provider | |||
// ----------------------------------------------------------------------------- | |||
unsigned int currentCallback() { | |||
#if EMON_PROVIDER == EMON_ANALOG_PROVIDER | |||
return analogRead(EMON_CURRENT_PIN); | |||
#endif | |||
#if EMON_PROVIDER == EMON_ADC121_PROVIDER | |||
uint8_t buffer[2]; | |||
brzo_i2c_start_transaction(EMON_ADC121_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 | |||
} | |||
// ----------------------------------------------------------------------------- | |||
// HAL | |||
// ----------------------------------------------------------------------------- | |||
void setCurrentRatio(float value) { | |||
emon.setCurrentRatio(value); | |||
} | |||
unsigned int getApparentPower() { | |||
return int(getCurrent() * getVoltage()); | |||
} | |||
double getCurrent() { | |||
double current = emon.getCurrent(EMON_SAMPLES); | |||
current -= EMON_CURRENT_OFFSET; | |||
if (current < 0) current = 0; | |||
return current; | |||
} | |||
unsigned int getVoltage() { | |||
return getSetting("emonVoltage", EMON_MAINS_VOLTAGE).toInt(); | |||
} | |||
// ----------------------------------------------------------------------------- | |||
void powerMonitorSetup() { | |||
// backwards compatibility | |||
moveSetting("pwMainsVoltage", "emonVoltage"); | |||
moveSetting("emonMains", "emonVoltage"); | |||
moveSetting("pwCurrentRatio", "emonRatio"); | |||
emon.initCurrent( | |||
currentCallback, | |||
EMON_ADC_BITS, | |||
EMON_REFERENCE_VOLTAGE, | |||
getSetting("emonRatio", EMON_CURRENT_RATIO).toFloat() | |||
); | |||
#if EMON_PROVIDER == EMON_ADC121_PROVIDER | |||
uint8_t buffer[2]; | |||
buffer[0] = ADC121_REG_CONFIG; | |||
buffer[1] = 0x00; | |||
brzo_i2c_start_transaction(EMON_ADC121_ADDRESS, I2C_SCL_FREQUENCY); | |||
brzo_i2c_write(buffer, 2, false); | |||
brzo_i2c_end_transaction(); | |||
#endif | |||
#if WEB_SUPPORT | |||
apiRegister(EMON_APOWER_TOPIC, EMON_APOWER_TOPIC, [](char * buffer, size_t len) { | |||
if (_emonReady) { | |||
snprintf_P(buffer, len, PSTR("%d"), _emonPower); | |||
} else { | |||
buffer = NULL; | |||
} | |||
}); | |||
apiRegister(EMON_CURRENT_TOPIC, EMON_CURRENT_TOPIC, [](char * buffer, size_t len) { | |||
if (_emonReady) { | |||
dtostrf(_emonCurrent, len-1, 3, buffer); | |||
} else { | |||
buffer = NULL; | |||
} | |||
}); | |||
#endif // WEB_SUPPORT | |||
} | |||
void powerMonitorLoop() { | |||
static unsigned long next_measurement = millis(); | |||
static bool warmup = true; | |||
static byte measurements = 0; | |||
static double max = 0; | |||
static double min = 0; | |||
static double sum = 0; | |||
if (warmup) { | |||
warmup = false; | |||
emon.warmup(); | |||
} | |||
if (millis() > next_measurement) { | |||
int voltage = getVoltage(); | |||
{ | |||
double current = getCurrent(); | |||
if (measurements == 0) { | |||
max = min = current; | |||
} else { | |||
if (_emonCurrent > max) max = current; | |||
if (_emonCurrent < min) min = current; | |||
} | |||
sum += current; | |||
++measurements; | |||
DEBUG_MSG_P(PSTR("[ENERGY] Current: %sA\n"), String(current, 3).c_str()); | |||
DEBUG_MSG_P(PSTR("[ENERGY] Power: %dW\n"), int(current * voltage)); | |||
// Update websocket clients | |||
#if WEB_SUPPORT | |||
char buffer[100]; | |||
snprintf_P(buffer, sizeof(buffer), PSTR("{\"emonVisible\": 1, \"emonApparentPower\": %d, \"emonCurrent\": %s}"), int(current * voltage), String(current, 3).c_str()); | |||
wsSend(buffer); | |||
#endif | |||
} | |||
// Send MQTT messages averaged every EMON_MEASUREMENTS | |||
if (measurements == EMON_MEASUREMENTS) { | |||
// Calculate average current (removing max and min values) | |||
_emonCurrent = (sum - max - min) / (measurements - 2); | |||
_emonPower = (int) (_emonCurrent * voltage); | |||
_emonReady = true; | |||
// Calculate energy increment (ppower times time) | |||
double energy_delta = (double) _emonPower * EMON_INTERVAL * EMON_MEASUREMENTS / 1000.0 / 3600.0; | |||
// Report values to MQTT broker | |||
mqttSend(getSetting("emonPowerTopic", EMON_APOWER_TOPIC).c_str(), String(_emonPower).c_str()); | |||
mqttSend(getSetting("emonCurrTopic", EMON_CURRENT_TOPIC).c_str(), String(_emonCurrent, 3).c_str()); | |||
mqttSend(getSetting("emonEnergyTopic", EMON_ENERGY_TOPIC).c_str(), String(energy_delta, 3).c_str()); | |||
// Report values to Domoticz | |||
#if DOMOTICZ_SUPPORT | |||
{ | |||
char buffer[20]; | |||
snprintf_P(buffer, sizeof(buffer), PSTR("%d;%s"), _emonPower, String(energy_delta, 3).c_str()); | |||
domoticzSend("dczPowIdx", 0, buffer); | |||
snprintf_P(buffer, sizeof(buffer), PSTR("%s"), String(energy_delta, 3).c_str()); | |||
domoticzSend("dczEnergyIdx", 0, buffer); | |||
snprintf_P(buffer, sizeof(buffer), PSTR("%s"), String(_emonCurrent, 3).c_str()); | |||
domoticzSend("dczCurrentIdx", 0, buffer); | |||
} | |||
#endif | |||
#if INFLUXDB_SUPPORT | |||
influxDBSend(getSetting("emonPowerTopic", EMON_APOWER_TOPIC).c_str(), _emonPower); | |||
influxDBSend(getSetting("emonCurrTopic", EMON_CURRENT_TOPIC).c_str(), String(_emonCurrent, 3).c_str()); | |||
influxDBSend(getSetting("emonEnergyTopic", EMON_ENERGY_TOPIC).c_str(), String(energy_delta, 3).c_str()); | |||
#endif | |||
// Reset counters | |||
sum = measurements = 0; | |||
} | |||
next_measurement += EMON_INTERVAL; | |||
} | |||
} | |||
#endif | |||
*/ |
@ -1,348 +0,0 @@ | |||
/* | |||
POW MODULE | |||
Support for Sonoff POW HLW8012-based power monitor | |||
Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com> | |||
*/ | |||
/* | |||
#if HLW8012_SUPPORT | |||
#include <HLW8012.h> | |||
#include <Hash.h> | |||
#include <ArduinoJson.h> | |||
HLW8012 hlw8012; | |||
bool _hlw8012Enabled = false; | |||
bool _hlwReady = false; | |||
int _hlwPower = 0; | |||
double _hlwCurrent = 0; | |||
int _hlwVoltage = 0; | |||
// ----------------------------------------------------------------------------- | |||
// POW | |||
// ----------------------------------------------------------------------------- | |||
// When using interrupts we have to call the library entry point | |||
// whenever an interrupt is triggered | |||
void ICACHE_RAM_ATTR hlw8012_cf1_interrupt() { | |||
hlw8012.cf1_interrupt(); | |||
} | |||
void ICACHE_RAM_ATTR hlw8012_cf_interrupt() { | |||
hlw8012.cf_interrupt(); | |||
} | |||
void hlw8012Enable(bool status) { | |||
_hlw8012Enabled = status; | |||
if (_hlw8012Enabled) { | |||
#if HLW8012_USE_INTERRUPTS == 1 | |||
attachInterrupt(HLW8012_CF1_PIN, hlw8012_cf1_interrupt, CHANGE); | |||
attachInterrupt(HLW8012_CF_PIN, hlw8012_cf_interrupt, CHANGE); | |||
#endif | |||
DEBUG_MSG_P(PSTR("[POW] Enabled\n")); | |||
} else { | |||
#if HLW8012_USE_INTERRUPTS == 1 | |||
detachInterrupt(HLW8012_CF1_PIN); | |||
detachInterrupt(HLW8012_CF_PIN); | |||
#endif | |||
DEBUG_MSG_P(PSTR("[POW] Disabled\n")); | |||
} | |||
} | |||
// ----------------------------------------------------------------------------- | |||
void hlw8012SaveCalibration() { | |||
setSetting("powPowerMult", hlw8012.getPowerMultiplier()); | |||
setSetting("powCurrentMult", hlw8012.getCurrentMultiplier()); | |||
setSetting("powVoltageMult", hlw8012.getVoltageMultiplier()); | |||
saveSettings(); | |||
} | |||
void hlw8012RetrieveCalibration() { | |||
double value; | |||
value = getSetting("powPowerMult", 0).toFloat(); | |||
if (value > 0) hlw8012.setPowerMultiplier(value); | |||
value = getSetting("powCurrentMult", 0).toFloat(); | |||
if (value > 0) hlw8012.setCurrentMultiplier(value); | |||
value = getSetting("powVoltageMult", 0).toFloat(); | |||
if (value > 0) hlw8012.setVoltageMultiplier(value); | |||
} | |||
void hlw8012SetExpectedActivePower(unsigned int power) { | |||
if (power > 0) { | |||
hlw8012.expectedActivePower(power); | |||
hlw8012SaveCalibration(); | |||
} | |||
} | |||
void hlw8012SetExpectedCurrent(double current) { | |||
if (current > 0) { | |||
hlw8012.expectedCurrent(current); | |||
hlw8012SaveCalibration(); | |||
} | |||
} | |||
void hlw8012SetExpectedVoltage(unsigned int voltage) { | |||
if (voltage > 0) { | |||
hlw8012.expectedVoltage(voltage); | |||
hlw8012SaveCalibration(); | |||
} | |||
} | |||
void hlw8012Reset() { | |||
hlw8012.resetMultipliers(); | |||
hlw8012SaveCalibration(); | |||
} | |||
// ----------------------------------------------------------------------------- | |||
// HAL | |||
// ----------------------------------------------------------------------------- | |||
unsigned int getActivePower() { | |||
unsigned int power = hlw8012.getActivePower(); | |||
if (HLW8012_MIN_POWER > power || power > HLW8012_MAX_POWER) power = 0; | |||
return power; | |||
} | |||
unsigned int getApparentPower() { | |||
unsigned int power = hlw8012.getApparentPower(); | |||
if (HLW8012_MIN_POWER > power || power > HLW8012_MAX_POWER) power = 0; | |||
return power; | |||
} | |||
unsigned int getReactivePower() { | |||
unsigned int power = hlw8012.getReactivePower(); | |||
if (HLW8012_MIN_POWER > power || power > HLW8012_MAX_POWER) power = 0; | |||
return power; | |||
} | |||
double getCurrent() { | |||
double current = hlw8012.getCurrent(); | |||
if (HLW8012_MIN_CURRENT > current || current > HLW8012_MAX_CURRENT) current = 0; | |||
return current; | |||
} | |||
//unsigned int getVoltage() { | |||
// return hlw8012.getVoltage(); | |||
//} | |||
double getPowerFactor() { | |||
return hlw8012.getPowerFactor(); | |||
} | |||
// ----------------------------------------------------------------------------- | |||
void hlw8012Setup() { | |||
// 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); | |||
// Retrieve calibration values | |||
hlw8012RetrieveCalibration(); | |||
// API definitions | |||
#if WEB_SUPPORT | |||
apiRegister(HLW8012_POWER_TOPIC, HLW8012_POWER_TOPIC, [](char * buffer, size_t len) { | |||
if (_hlwReady) { | |||
snprintf_P(buffer, len, PSTR("%d"), _hlwPower); | |||
} else { | |||
buffer = NULL; | |||
} | |||
}); | |||
apiRegister(HLW8012_CURRENT_TOPIC, HLW8012_CURRENT_TOPIC, [](char * buffer, size_t len) { | |||
if (_hlwReady) { | |||
dtostrf(_hlwCurrent, len-1, 3, buffer); | |||
} else { | |||
buffer = NULL; | |||
} | |||
}); | |||
apiRegister(HLW8012_VOLTAGE_TOPIC, HLW8012_VOLTAGE_TOPIC, [](char * buffer, size_t len) { | |||
if (_hlwReady) { | |||
snprintf_P(buffer, len, PSTR("%d"), _hlwVoltage); | |||
} else { | |||
buffer = NULL; | |||
} | |||
}); | |||
#endif // WEB_SUPPORT | |||
} | |||
void hlw8012Loop() { | |||
static unsigned long last_update = 0; | |||
static unsigned char report_count = HLW8012_REPORT_EVERY; | |||
static bool power_spike = false; | |||
static unsigned long power_sum = 0; | |||
static unsigned long power_previous = 0; | |||
static bool current_spike = false; | |||
static double current_sum = 0; | |||
static double current_previous = 0; | |||
static bool voltage_spike = false; | |||
static unsigned long voltage_sum = 0; | |||
static unsigned long voltage_previous = 0; | |||
static bool powWasEnabled = false; | |||
// POW is disabled while there is no internet connection | |||
// When the HLW8012 measurements are enabled back we reset the timer | |||
if (!_hlw8012Enabled) { | |||
powWasEnabled = false; | |||
return; | |||
} | |||
if (!powWasEnabled) { | |||
last_update = millis(); | |||
powWasEnabled = true; | |||
} | |||
if (millis() - last_update > HLW8012_UPDATE_INTERVAL) { | |||
last_update = millis(); | |||
unsigned int power = getActivePower(); | |||
unsigned int voltage = getVoltage(); | |||
double current = getCurrent(); | |||
if (power > 0) { | |||
power_spike = (power_previous == 0); | |||
} else if (power_spike) { | |||
power_sum -= power_previous; | |||
power_spike = false; | |||
} | |||
power_previous = power; | |||
if (current > 0) { | |||
current_spike = (current_previous == 0); | |||
} else if (current_spike) { | |||
current_sum -= current_previous; | |||
current_spike = false; | |||
} | |||
current_previous = current; | |||
if (voltage > 0) { | |||
voltage_spike = (voltage_previous == 0); | |||
} else if (voltage_spike) { | |||
voltage_sum -= voltage_previous; | |||
voltage_spike = false; | |||
} | |||
voltage_previous = voltage; | |||
#if WEB_SUPPORT | |||
{ | |||
unsigned int apparent = getApparentPower(); | |||
double factor = getPowerFactor(); | |||
unsigned int reactive = getReactivePower(); | |||
DynamicJsonBuffer jsonBuffer; | |||
JsonObject& root = jsonBuffer.createObject(); | |||
root["powVisible"] = 1; | |||
root["powActivePower"] = power; | |||
root["powCurrent"] = String(current, 3); | |||
root["powVoltage"] = voltage; | |||
root["powApparentPower"] = apparent; | |||
root["powReactivePower"] = reactive; | |||
root["powPowerFactor"] = String(factor, 2); | |||
String output; | |||
root.printTo(output); | |||
wsSend(output.c_str()); | |||
} | |||
#endif | |||
if (--report_count == 0) { | |||
// Update globals | |||
_hlwPower = power_sum / HLW8012_REPORT_EVERY; | |||
_hlwCurrent = current_sum / HLW8012_REPORT_EVERY; | |||
_hlwVoltage = voltage_sum / HLW8012_REPORT_EVERY; | |||
_hlwReady = true; | |||
// Calculate subproducts (apparent and reactive power, power factor and delta energy) | |||
unsigned int apparent = _hlwCurrent * _hlwVoltage; | |||
unsigned int reactive = (apparent > _hlwPower) ? sqrt(apparent * apparent - _hlwPower * _hlwPower) : 0; | |||
double factor = (apparent > 0) ? (double) _hlwPower / apparent : 1; | |||
if (factor > 1) factor = 1; | |||
double energy_delta = (double) _hlwPower * HLW8012_REPORT_EVERY * HLW8012_UPDATE_INTERVAL / 1000.0 / 3600.0; | |||
// Report values to MQTT broker | |||
mqttSend(getSetting("powPowerTopic", HLW8012_POWER_TOPIC).c_str(), String(_hlwPower).c_str()); | |||
mqttSend(getSetting("powCurrentTopic", HLW8012_CURRENT_TOPIC).c_str(), String(_hlwCurrent, 3).c_str()); | |||
mqttSend(getSetting("powVoltageTopic", HLW8012_VOLTAGE_TOPIC).c_str(), String(_hlwVoltage).c_str()); | |||
mqttSend(getSetting("powEnergyTopic", HLW8012_ENERGY_TOPIC).c_str(), String(energy_delta, 3).c_str()); | |||
mqttSend(getSetting("powAPowerTopic", HLW8012_APOWER_TOPIC).c_str(), String(apparent).c_str()); | |||
mqttSend(getSetting("powRPowerTopic", HLW8012_RPOWER_TOPIC).c_str(), String(reactive).c_str()); | |||
mqttSend(getSetting("powPFactorTopic", HLW8012_PFACTOR_TOPIC).c_str(), String(factor, 2).c_str()); | |||
// Report values to Domoticz | |||
#if DOMOTICZ_SUPPORT | |||
{ | |||
char buffer[20]; | |||
snprintf_P(buffer, sizeof(buffer), PSTR("%d;%s"), _hlwPower, String(energy_delta, 3).c_str()); | |||
domoticzSend("dczPowIdx", 0, buffer); | |||
snprintf_P(buffer, sizeof(buffer), PSTR("%s"), String(energy_delta, 3).c_str()); | |||
domoticzSend("dczEnergyIdx", 0, buffer); | |||
snprintf_P(buffer, sizeof(buffer), PSTR("%d"), _hlwVoltage); | |||
domoticzSend("dczVoltIdx", 0, buffer); | |||
snprintf_P(buffer, sizeof(buffer), PSTR("%s"), String(_hlwCurrent).c_str()); | |||
domoticzSend("dczCurrentIdx", 0, buffer); | |||
} | |||
#endif | |||
#if INFLUXDB_SUPPORT | |||
influxDBSend(getSetting("powPowerTopic", HLW8012_POWER_TOPIC).c_str(), String(_hlwPower).c_str()); | |||
influxDBSend(getSetting("powCurrentTopic", HLW8012_CURRENT_TOPIC).c_str(), String(_hlwCurrent, 3).c_str()); | |||
influxDBSend(getSetting("powVoltageTopic", HLW8012_VOLTAGE_TOPIC).c_str(), String(_hlwVoltage).c_str()); | |||
influxDBSend(getSetting("powEnergyTopic", HLW8012_ENERGY_TOPIC).c_str(), String(energy_delta, 3).c_str()); | |||
influxDBSend(getSetting("powAPowerTopic", HLW8012_APOWER_TOPIC).c_str(), String(apparent).c_str()); | |||
influxDBSend(getSetting("powRPowerTopic", HLW8012_RPOWER_TOPIC).c_str(), String(reactive).c_str()); | |||
influxDBSend(getSetting("powPFactorTopic", HLW8012_PFACTOR_TOPIC).c_str(), String(factor, 2).c_str()); | |||
#endif | |||
// Reset counters | |||
power_sum = current_sum = voltage_sum = 0; | |||
report_count = HLW8012_REPORT_EVERY; | |||
} | |||
// Post - Accumulators | |||
power_sum += power_previous; | |||
current_sum += current_previous; | |||
voltage_sum += voltage_previous; | |||
// Toggle between current and voltage monitoring | |||
#if HLW8012_USE_INTERRUPTS == 0 | |||
hlw8012.toggleMode(); | |||
#endif | |||
} | |||
} | |||
#endif | |||
*/ |
@ -1,305 +0,0 @@ | |||
/* | |||
V9261F MODULE | |||
Support for V9261D-based power monitors | |||
Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com> | |||
*/ | |||
/* | |||
#if V9261F_SUPPORT | |||
#include <SoftwareSerial.h> | |||
#include <ArduinoJson.h> | |||
SoftwareSerial * _v9261f_uart; | |||
bool _v9261f_enabled = false; | |||
bool _v9261f_ready = false; | |||
bool _v9261f_newdata = false; | |||
int _v9261f_power = 0; | |||
int _v9261f_rpower = 0; | |||
int _v9261f_voltage = 0; | |||
double _v9261f_current = 0; | |||
unsigned char _v9261f_data[24]; | |||
// ----------------------------------------------------------------------------- | |||
// PRIVATE | |||
// ----------------------------------------------------------------------------- | |||
void v9261fRead() { | |||
static unsigned char state = 0; | |||
static unsigned long last = 0; | |||
static bool found = false; | |||
static unsigned char index = 0; | |||
if (state == 0) { | |||
while (_v9261f_uart->available()) { | |||
_v9261f_uart->flush(); | |||
found = true; | |||
last = millis(); | |||
} | |||
if (found && (millis() - last > V9261F_SYNC_INTERVAL)) { | |||
_v9261f_uart->flush(); | |||
index = 0; | |||
state = 1; | |||
} | |||
} else if (state == 1) { | |||
while (_v9261f_uart->available()) { | |||
_v9261f_uart->read(); | |||
if (index++ >= 7) { | |||
_v9261f_uart->flush(); | |||
index = 0; | |||
state = 2; | |||
} | |||
} | |||
} else if (state == 2) { | |||
while (_v9261f_uart->available()) { | |||
_v9261f_data[index] = _v9261f_uart->read(); | |||
if (index++ >= 19) { | |||
_v9261f_uart->flush(); | |||
last = millis(); | |||
state = 3; | |||
} | |||
} | |||
} else if (state == 3) { | |||
if (checksumOK()) { | |||
_v9261f_power = (double) ( | |||
(_v9261f_data[3]) + | |||
(_v9261f_data[4] << 8) + | |||
(_v9261f_data[5] << 16) + | |||
(_v9261f_data[6] << 24) | |||
) / V9261F_POWER_FACTOR; | |||
_v9261f_rpower = (double) ( | |||
(_v9261f_data[7]) + | |||
(_v9261f_data[8] << 8) + | |||
(_v9261f_data[9] << 16) + | |||
(_v9261f_data[10] << 24) | |||
) / V9261F_RPOWER_FACTOR; | |||
_v9261f_voltage = (double) ( | |||
(_v9261f_data[11]) + | |||
(_v9261f_data[12] << 8) + | |||
(_v9261f_data[13] << 16) + | |||
(_v9261f_data[14] << 24) | |||
) / V9261F_VOLTAGE_FACTOR; | |||
_v9261f_current = (double) ( | |||
(_v9261f_data[15]) + | |||
(_v9261f_data[16] << 8) + | |||
(_v9261f_data[17] << 16) + | |||
(_v9261f_data[18] << 24) | |||
) / V9261F_CURRENT_FACTOR; | |||
_v9261f_newdata = true; | |||
} | |||
last = millis(); | |||
index = 0; | |||
state = 4; | |||
} else if (state == 4) { | |||
while (_v9261f_uart->available()) { | |||
_v9261f_uart->flush(); | |||
last = millis(); | |||
} | |||
if (millis() - last > V9261F_SYNC_INTERVAL) { | |||
state = 1; | |||
} | |||
} | |||
} | |||
boolean checksumOK() { | |||
unsigned char checksum = 0; | |||
for (unsigned char i = 0; i < 19; i++) { | |||
checksum = checksum + _v9261f_data[i]; | |||
} | |||
checksum = ~checksum + 0x33; | |||
return checksum == _v9261f_data[19]; | |||
} | |||
// ----------------------------------------------------------------------------- | |||
// HAL | |||
// ----------------------------------------------------------------------------- | |||
unsigned int getActivePower() { | |||
return _v9261f_power; | |||
} | |||
unsigned int getReactivePower() { | |||
return _v9261f_rpower; | |||
} | |||
unsigned int getApparentPower() { | |||
return sqrt(_v9261f_rpower * _v9261f_rpower + _v9261f_power * _v9261f_power); | |||
} | |||
unsigned int getVoltage() { | |||
return _v9261f_voltage; | |||
} | |||
double getCurrent() { | |||
return _v9261f_current; | |||
} | |||
double getPowerFactor() { | |||
unsigned int apparent = getApparentPower(); | |||
if (apparent > 0) return getActivePower() / getApparentPower(); | |||
return 1; | |||
} | |||
// ----------------------------------------------------------------------------- | |||
void v9261fSetup() { | |||
_v9261f_uart = new SoftwareSerial(V9261F_PIN, SW_SERIAL_UNUSED_PIN, V9261F_PIN_INVERSE, 256); | |||
_v9261f_uart->begin(V9261F_BAUDRATE); | |||
// API definitions | |||
#if WEB_SUPPORT | |||
apiRegister(HLW8012_POWER_TOPIC, HLW8012_POWER_TOPIC, [](char * buffer, size_t len) { | |||
snprintf_P(buffer, len, PSTR("%d"), _v9261f_power); | |||
}); | |||
apiRegister(HLW8012_CURRENT_TOPIC, HLW8012_CURRENT_TOPIC, [](char * buffer, size_t len) { | |||
dtostrf(_v9261f_current, len-1, 3, buffer); | |||
}); | |||
apiRegister(HLW8012_VOLTAGE_TOPIC, HLW8012_VOLTAGE_TOPIC, [](char * buffer, size_t len) { | |||
snprintf_P(buffer, len, PSTR("%d"), _v9261f_voltage); | |||
}); | |||
#endif // WEB_SUPPORT | |||
} | |||
void v9261fLoop() { | |||
static int sum_power = 0; | |||
static int sum_rpower = 0; | |||
static int sum_voltage = 0; | |||
static double sum_current = 0; | |||
static int count = 0; | |||
// Sniff data in the UART interface | |||
v9261fRead(); | |||
// Do we have new data? | |||
if (_v9261f_newdata) { | |||
_v9261f_newdata = false; | |||
sum_power += getActivePower(); | |||
sum_rpower += getReactivePower(); | |||
sum_voltage += getVoltage(); | |||
sum_current += getCurrent(); | |||
count++; | |||
#if WEB_SUPPORT | |||
{ | |||
DynamicJsonBuffer jsonBuffer; | |||
JsonObject& root = jsonBuffer.createObject(); | |||
char buf_current[10]; | |||
dtostrf(getCurrent(), 6, 3, buf_current); | |||
root["powVisible"] = 1; | |||
root["powActivePower"] = getActivePower(); | |||
root["powCurrent"] = String(ltrim(buf_current)); | |||
root["powVoltage"] = getVoltage(); | |||
root["powApparentPower"] = getApparentPower(); | |||
root["powReactivePower"] = getReactivePower(); | |||
root["powPowerFactor"] = 100 * getPowerFactor(); | |||
String output; | |||
root.printTo(output); | |||
wsSend(output.c_str()); | |||
} | |||
#endif | |||
} | |||
// Do we have to report? | |||
static unsigned long last = 0; | |||
if ((count == 0) || (millis() - last < V9261F_REPORT_INTERVAL)) return; | |||
last = millis(); | |||
{ | |||
unsigned int power = sum_power / count; | |||
unsigned int reactive = sum_rpower / count; | |||
unsigned int voltage = sum_voltage / count; | |||
double current = sum_current / count; | |||
char buf_current[10]; | |||
dtostrf(current, 6, 3, buf_current); | |||
unsigned int apparent = sqrt(power * power + reactive * reactive); | |||
double energy_delta = (double) power * V9261F_REPORT_INTERVAL / 1000.0 / 3600.0; | |||
char buf_energy[10]; | |||
dtostrf(energy_delta, 6, 3, buf_energy); | |||
unsigned int factor = 100 * ((double) power / apparent); | |||
// Report values to MQTT broker | |||
mqttSend(HLW8012_POWER_TOPIC, String(power).c_str()); | |||
mqttSend(HLW8012_CURRENT_TOPIC, buf_current); | |||
mqttSend(HLW8012_VOLTAGE_TOPIC, String(voltage).c_str()); | |||
mqttSend(HLW8012_ENERGY_TOPIC, buf_energy); | |||
mqttSend(HLW8012_APOWER_TOPIC, String(apparent).c_str()); | |||
mqttSend(HLW8012_RPOWER_TOPIC, String(reactive).c_str()); | |||
mqttSend(HLW8012_PFACTOR_TOPIC, String(factor).c_str()); | |||
// Report values to Domoticz | |||
#if DOMOTICZ_SUPPORT | |||
{ | |||
char buffer[20]; | |||
snprintf_P(buffer, sizeof(buffer), PSTR("%d;%s"), power, buf_energy); | |||
domoticzSend("dczPowIdx", 0, buffer); | |||
snprintf_P(buffer, sizeof(buffer), PSTR("%s"), buf_energy); | |||
domoticzSend("dczEnergyIdx", 0, buffer); | |||
snprintf_P(buffer, sizeof(buffer), PSTR("%d"), voltage); | |||
domoticzSend("dczVoltIdx", 0, buffer); | |||
snprintf_P(buffer, sizeof(buffer), PSTR("%s"), buf_current); | |||
domoticzSend("dczCurrentIdx", 0, buffer); | |||
} | |||
#endif | |||
#if INFLUXDB_SUPPORT | |||
{ | |||
influxDBSend(HLW8012_POWER_TOPIC, String(power).c_str()); | |||
influxDBSend(HLW8012_CURRENT_TOPIC, buf_current); | |||
influxDBSend(HLW8012_VOLTAGE_TOPIC, String(voltage).c_str()); | |||
influxDBSend(HLW8012_ENERGY_TOPIC, buf_energy); | |||
influxDBSend(HLW8012_APOWER_TOPIC, String(apparent).c_str()); | |||
influxDBSend(HLW8012_RPOWER_TOPIC, String(reactive).c_str()); | |||
influxDBSend(HLW8012_PFACTOR_TOPIC, String(factor).c_str()); | |||
} | |||
#endif | |||
// Reset counters | |||
sum_power = sum_rpower = sum_voltage = sum_current = count = 0; | |||
} | |||
} | |||
#endif | |||
*/ |