/*
|
|
|
|
EMON MODULE
|
|
|
|
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
|
|
|
|
// 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
|
|
String tmp;
|
|
tmp = getSetting("pwMainsVoltage", EMON_MAINS_VOLTAGE);
|
|
setSetting("emonVoltage", tmp);
|
|
delSetting("pwMainsVoltage");
|
|
tmp = getSetting("emonMains", EMON_MAINS_VOLTAGE);
|
|
setSetting("emonVoltage", tmp);
|
|
delSetting("emonMains");
|
|
tmp = getSetting("pwCurrentRatio", EMON_CURRENT_RATIO);
|
|
setSetting("emonRatio", tmp);
|
|
delSetting("pwCurrentRatio");
|
|
|
|
emon.initCurrent(
|
|
currentCallback,
|
|
EMON_ADC_BITS,
|
|
EMON_REFERENCE_VOLTAGE,
|
|
getSetting("emonRatio", EMON_CURRENT_RATIO).toFloat()
|
|
);
|
|
emon.setPrecision(EMON_CURRENT_PRECISION);
|
|
|
|
#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
|
|
|
|
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;
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
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 (wsConnected()) {
|
|
char text[100];
|
|
sprintf_P(text, PSTR("{\"emonVisible\": 1, \"emonApparentPower\": %d, \"emonCurrent\": %s}"), int(current * voltage), String(current, 3).c_str());
|
|
wsSend(text);
|
|
}
|
|
|
|
}
|
|
|
|
// 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, strlen(buffer), PSTR("%d;%s"), _emonPower, String(energy_delta, 3).c_str());
|
|
domoticzSend("dczPowIdx", 0, buffer);
|
|
snprintf_P(buffer, strlen(buffer), PSTR("%s"), String(energy_delta, 3).c_str());
|
|
domoticzSend("dczEnergyIdx", 0, buffer);
|
|
snprintf_P(buffer, strlen(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
|