Browse Source

Adding current, voltage, apparent and reactive power reports to Sonoff POW

fastled
Xose Pérez 7 years ago
parent
commit
a098614ca6
9 changed files with 173 additions and 47 deletions
  1. +8
    -2
      code/espurna/config/sensors.h
  2. BIN
      code/espurna/data/index.html.gz
  3. BIN
      code/espurna/data/script.js.gz
  4. +0
    -2
      code/espurna/espurna.ino
  5. +78
    -23
      code/espurna/pow.ino
  6. +34
    -16
      code/espurna/web.ino
  7. +3
    -0
      code/html/custom.js
  8. +48
    -2
      code/html/index.html
  9. +2
    -2
      code/platformio.ini

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

@ -61,5 +61,11 @@
#define POW_VOLTAGE_R_UP ( 5 * 470000 ) // Real: 2280k
#define POW_VOLTAGE_R_DOWN ( 1000 ) // Real 1.009k
#define POW_POWER_TOPIC "/power"
#define POW_UPDATE_INTERVAL 10000
#define POW_REPORT_EVERY 6
#define POW_CURRENT_TOPIC "/current"
#define POW_VOLTAGE_TOPIC "/voltage"
#define POW_APOWER_TOPIC "/apower"
#define POW_RPOWER_TOPIC "/rpower"
#define POW_PFACTOR_TOPIC "/pfactor"
#define POW_ENERGY_TOPIC "/energy"
#define POW_UPDATE_INTERVAL 5000
#define POW_REPORT_EVERY 12

BIN
code/espurna/data/index.html.gz View File


BIN
code/espurna/data/script.js.gz View File


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

@ -175,6 +175,4 @@ void loop() {
powerMonitorLoop();
#endif
yield();
}

+ 78
- 23
code/espurna/pow.ino View File

@ -11,6 +11,8 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
#include <HLW8012.h>
#define POW_USE_INTERRUPTS 1
HLW8012 hlw8012;
// -----------------------------------------------------------------------------
@ -28,17 +30,19 @@ void hlw8012_cf_interrupt() {
}
void powAttachInterrupts() {
//attachInterrupt(POW_CF1_PIN, hlw8012_cf1_interrupt, CHANGE);
attachInterrupt(POW_CF1_PIN, hlw8012_cf1_interrupt, CHANGE);
attachInterrupt(POW_CF_PIN, hlw8012_cf_interrupt, CHANGE);
DEBUG_MSG("[POW] Enabled\n");
}
void powDettachInterrupts() {
//detachInterrupt(POW_CF1_PIN);
detachInterrupt(POW_CF1_PIN);
detachInterrupt(POW_CF_PIN);
DEBUG_MSG("[POW] Disabled\n");
}
// -----------------------------------------------------------------------------
void powSaveCalibration() {
setSetting("powPowerMult", hlw8012.getPowerMultiplier());
setSetting("powCurrentMult", hlw8012.getCurrentMultiplier());
@ -76,6 +80,13 @@ void powSetExpectedVoltage(unsigned int voltage) {
}
}
void powReset() {
hlw8012.resetMultipliers();
powSaveCalibration();
}
// -----------------------------------------------------------------------------
unsigned int getActivePower() {
return hlw8012.getActivePower();
}
@ -84,6 +95,10 @@ unsigned int getApparentPower() {
return hlw8012.getApparentPower();
}
unsigned int getReactivePower() {
return hlw8012.getReactivePower();
}
double getCurrent() {
return hlw8012.getCurrent();
}
@ -96,6 +111,8 @@ unsigned int getPowerFactor() {
return (int) (100 * hlw8012.getPowerFactor());
}
// -----------------------------------------------------------------------------
void powSetup() {
// Initialize HLW8012
@ -104,7 +121,11 @@ void powSetup() {
// * 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
hlw8012.begin(POW_CF_PIN, POW_CF1_PIN, POW_SEL_PIN, POW_SEL_CURRENT, false, 1000000);
#if POW_USE_INTERRUPTS
hlw8012.begin(POW_CF_PIN, POW_CF1_PIN, POW_SEL_PIN, POW_SEL_CURRENT, true);
#else
hlw8012.begin(POW_CF_PIN, POW_CF1_PIN, POW_SEL_PIN, POW_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:
@ -113,25 +134,13 @@ void powSetup() {
// * The VOLTAGE_RESISTOR_DOWNSTREAM is the 1kOhm resistor in the voltage divider that feeds the V2P pin in the HLW8012
hlw8012.setResistors(POW_CURRENT_R, POW_VOLTAGE_R_UP, POW_VOLTAGE_R_DOWN);
// Retrieve calibration values
powRetrieveCalibration();
/*
static WiFiEventHandler e1 = WiFi.onStationModeDisconnected([](const WiFiEventStationModeDisconnected& event) {
powDettachInterrupts();
});
static WiFiEventHandler e2 = WiFi.onSoftAPModeStationDisconnected([](const WiFiEventSoftAPModeStationDisconnected& event) {
powDettachInterrupts();
});
static WiFiEventHandler e3 = WiFi.onStationModeConnected([](const WiFiEventStationModeConnected& event) {
powAttachInterrupts();
});
static WiFiEventHandler e4 = WiFi.onSoftAPModeStationConnected([](const WiFiEventSoftAPModeStationConnected& event) {
// Attach interrupts
#if POW_USE_INTERRUPTS
powAttachInterrupts();
});
*/
#endif
}
@ -140,20 +149,66 @@ void powLoop() {
static unsigned long last_update = 0;
static unsigned char report_count = POW_REPORT_EVERY;
static unsigned long power_sum = 0;
static double current_sum = 0;
static unsigned long voltage_sum = 0;
if ((millis() - last_update > POW_UPDATE_INTERVAL) || (last_update == 0 )){
last_update = millis();
unsigned int power = getActivePower();
char buffer[100];
sprintf_P(buffer, PSTR("{\"powVisible\": 1, \"powActivePower\": %d}"), power);
wsSend(buffer);
unsigned int voltage = getVoltage();
double current = getCurrent();
unsigned int apparent = getApparentPower();
unsigned int factor = getPowerFactor();
unsigned int reactive = getReactivePower();
power_sum += power;
current_sum += current;
voltage_sum += voltage;
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root["powVisible"] = 1;
root["powActivePower"] = power;
root["powCurrent"] = current;
root["powVoltage"] = voltage;
root["powApparentPower"] = apparent;
root["powReactivePower"] = reactive;
root["powPowerFactor"] = factor;
String output;
root.printTo(output);
wsSend((char *) output.c_str());
if (--report_count == 0) {
power = power_sum / POW_REPORT_EVERY;
current = current_sum / POW_REPORT_EVERY;
voltage = voltage_sum / POW_REPORT_EVERY;
apparent = current * voltage;
reactive = (apparent > power) ? sqrt(apparent * apparent - power * power) : 0;
factor = (apparent > 0) ? 100 * power / apparent : 100;
mqttSend(getSetting("powPowerTopic", POW_POWER_TOPIC).c_str(), String(power).c_str());
mqttSend(getSetting("powCurrentTopic", POW_CURRENT_TOPIC).c_str(), String(current).c_str());
mqttSend(getSetting("powVoltageTopic", POW_VOLTAGE_TOPIC).c_str(), String(voltage).c_str());
mqttSend(getSetting("powAPowerTopic", POW_APOWER_TOPIC).c_str(), String(apparent).c_str());
mqttSend(getSetting("powRPowerTopic", POW_RPOWER_TOPIC).c_str(), String(reactive).c_str());
mqttSend(getSetting("powPFactorTopic", POW_PFACTOR_TOPIC).c_str(), String(factor).c_str());
power_sum = current_sum = voltage_sum = 0;
report_count = POW_REPORT_EVERY;
}
// Toggle between current and voltage monitoring
#if POW_USE_INTERRUPTS == 0
hlw8012.toggleMode();
#endif
}
}


+ 34
- 16
code/espurna/web.ino View File

@ -80,7 +80,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
// Let the HTTP request return and disconnect after 100ms
deferred.once_ms(100, wifiDisconnect);
}
if (action.equals("on")) relayStatus(relayID, true);
if (action.equals("off")) relayStatus(relayID, false);
@ -93,8 +93,9 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
JsonArray& config = root["config"];
DEBUG_MSG("[WEBSOCKET] Parsing configuration data\n");
bool dirty = false;
bool dirtyMQTT = false;
bool save = false;
bool changed = false;
bool changedMQTT = false;
bool apiEnabled = false;
#if ENABLE_FAUXMO
bool fauxmoEnabled = false;
@ -112,15 +113,28 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
if (key == "powExpectedPower") {
powSetExpectedActivePower(value.toInt());
continue;
changed = true;
}
#else
if (key == "powExpectedVoltage") {
powSetExpectedVoltage(value.toInt());
changed = true;
}
if (key == "powExpectedCurrent") {
powSetExpectedCurrent(value.toInt());
changed = true;
}
if (key.startsWith("pow")) continue;
if (key == "powExpectedReset") {
powReset();
changed = true;
}
#endif
if (key.startsWith("pow")) continue;
#if ENABLE_DOMOTICZ
if (key == "dczIdx") {
@ -185,8 +199,8 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
if (value != getSetting(key)) {
//DEBUG_MSG("[WEBSOCKET] Storing %s = %s\n", key.c_str(), value.c_str());
setSetting(key, value);
dirty = true;
if (key.startsWith("mqtt")) dirtyMQTT = true;
save = changed = true;
if (key.startsWith("mqtt")) changedMQTT = true;
}
}
@ -194,12 +208,12 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
// Checkboxes
if (apiEnabled != (getSetting("apiEnabled").toInt() == 1)) {
setSetting("apiEnabled", apiEnabled);
dirty = true;
save = changed = true;
}
#if ENABLE_FAUXMO
if (fauxmoEnabled != (getSetting("fauxmoEnabled").toInt() == 1)) {
setSetting("fauxmoEnabled", fauxmoEnabled);
dirty = true;
save = changed = true;
}
#endif
@ -213,7 +227,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
}
for (int i = network; i<WIFI_MAX_NETWORKS; i++) {
if (getSetting("ssid" + String(i)).length() > 0) {
dirty = true;
save = changed = true;
}
delSetting("ssid" + String(i));
delSetting("pass" + String(i));
@ -224,7 +238,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
}
// Save settings
if (dirty) {
if (save) {
saveSettings();
wifiConfigure();
@ -243,16 +257,15 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
#endif
// Check if we should reconfigure MQTT connection
if (dirtyMQTT) {
if (changedMQTT) {
mqttDisconnect();
}
}
if (changed) {
ws.text(client_id, "{\"message\": \"Changes saved\"}");
} else {
ws.text(client_id, "{\"message\": \"No changes detected\"}");
}
}
@ -345,6 +358,11 @@ void _wsStart(uint32_t client_id) {
#if ENABLE_POW
root["powVisible"] = 1;
root["powActivePower"] = getActivePower();
root["powApparentPower"] = getApparentPower();
root["powReactivePower"] = getReactivePower();
root["powVoltage"] = getVoltage();
root["powCurrent"] = getCurrent();
root["powPowerFactor"] = getPowerFactor();
#endif
root["maxNetworks"] = WIFI_MAX_NETWORKS;


+ 3
- 0
code/html/custom.js View File

@ -36,6 +36,9 @@ function doUpdate() {
var data = $("#formSave").serializeArray();
websock.send(JSON.stringify({'config': data}));
$(".powExpected").val(0);
$("input[name='powExpectedReset']")
.prop("checked", false)
.iphoneStyle("refresh");
}
return false;
}


+ 48
- 2
code/html/index.html View File

@ -136,10 +136,35 @@
</div>
<div class="pure-g module module-pow">
<label class="pure-u-1 pure-u-sm-1-4" for="powActivePower">Power (W)</label>
<label class="pure-u-1 pure-u-sm-1-4" for="powActivePower">Active Power (W)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="powActivePower" readonly />
</div>
<div class="pure-g module module-pow">
<label class="pure-u-1 pure-u-sm-1-4" for="powApparentPower">Apparent Power (W)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="powApparentPower" readonly />
</div>
<div class="pure-g module module-pow">
<label class="pure-u-1 pure-u-sm-1-4" for="powReactivePower">Reactive Power (W)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="powReactivePower" readonly />
</div>
<div class="pure-g module module-pow">
<label class="pure-u-1 pure-u-sm-1-4" for="powCurrent">Current (A)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="powCurrent" readonly />
</div>
<div class="pure-g module module-pow">
<label class="pure-u-1 pure-u-sm-1-4" for="powVoltage">Voltage (V)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="powVoltage" readonly />
</div>
<div class="pure-g module module-pow">
<label class="pure-u-1 pure-u-sm-1-4" for="powPowerFactor">Power Factor (%)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="powPowerFactor" readonly />
</div>
<div id="relays">
</div>
@ -363,7 +388,28 @@
<label class="pure-u-1 pure-u-md-1-4" for="powExpectedPower">AC RMS Active Power</label>
<input class="pure-u-1 pure-u-md-3-4 powExpected" name="powExpectedPower" type="text" size="8" tabindex="41" placeholder="0" />
<div class="pure-u-0 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-md-3-4 hint">If you are using a pure resistive load like a bulb this will be writen on it, otherwise use a socket multimeter to get this value.</div>
<div class="pure-u-1 pure-u-md-3-4 hint">In Watts (W). If you are using a pure resistive load like a bulb this will be writen on it, otherwise use a socket multimeter to get this value.</div>
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4" for="powExpectedVoltage">AC RMS Voltage</label>
<input class="pure-u-1 pure-u-md-3-4 powExpected" name="powExpectedVoltage" type="text" size="8" tabindex="41" placeholder="0" />
<div class="pure-u-0 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-md-3-4 hint">In Volts (V). Enter your the nominal AC voltage for your household or facility, or use multimeter to get this value.</div>
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4" for="powExpectedCurrent">AC RMS Current</label>
<input class="pure-u-1 pure-u-md-3-4 powExpected" name="powExpectedCurrent" type="text" size="8" tabindex="41" placeholder="0" />
<div class="pure-u-0 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-md-3-4 hint">In Ampers (A). If you are using a pure resistive load like a bulb this will the ratio between the two previous values, i.e. power / voltage. You can also use a current clamp around one fo the power wires to get this value.</div>
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-sm-1-4"><label for="powExpectedReset">Reset calibration</label></div>
<div class="pure-u-1 pure-u-sm-1-4"><input type="checkbox" name="powExpectedReset" /></div>
<div class="pure-u-0 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-md-3-4 hint">Move this switch to ON and press "Update" to revert to factory values.</div>
</div>
</fieldset>


+ 2
- 2
code/platformio.ini View File

@ -17,7 +17,7 @@ lib_deps =
OneWire
DallasTemperature
https://bitbucket.org/xoseperez/justwifi.git
HLW8012
https://bitbucket.org/xoseperez/hlw8012.git
https://bitbucket.org/xoseperez/fauxmoesp.git
https://bitbucket.org/xoseperez/nofuss.git
https://bitbucket.org/xoseperez/emonliteesp.git
@ -127,7 +127,7 @@ extra_script = pio_hooks.py
build_flags = -g -Wl,-Tesp8266.flash.1m128.ld -DDEBUG_PORT=Serial -DSONOFF_POW
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
upload_flags = --auth=Algernon1 --port 8266
[env:sonoff-dual-debug]
platform = espressif8266


Loading…
Cancel
Save