diff --git a/code/espurna/config/sensors.h b/code/espurna/config/sensors.h index 338a9913..18d67bc9 100644 --- a/code/espurna/config/sensors.h +++ b/code/espurna/config/sensors.h @@ -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 diff --git a/code/espurna/data/index.html.gz b/code/espurna/data/index.html.gz index 2e18235d..0f4ed804 100644 Binary files a/code/espurna/data/index.html.gz and b/code/espurna/data/index.html.gz differ diff --git a/code/espurna/data/script.js.gz b/code/espurna/data/script.js.gz index 777cb053..c85032e7 100644 Binary files a/code/espurna/data/script.js.gz and b/code/espurna/data/script.js.gz differ diff --git a/code/espurna/espurna.ino b/code/espurna/espurna.ino index 0031b9dc..bca34abf 100644 --- a/code/espurna/espurna.ino +++ b/code/espurna/espurna.ino @@ -175,6 +175,4 @@ void loop() { powerMonitorLoop(); #endif - yield(); - } diff --git a/code/espurna/pow.ino b/code/espurna/pow.ino index f6865728..0b2d6910 100644 --- a/code/espurna/pow.ino +++ b/code/espurna/pow.ino @@ -11,6 +11,8 @@ Copyright (C) 2016-2017 by Xose PĂ©rez #include +#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 + } } diff --git a/code/espurna/web.ino b/code/espurna/web.ino index 072ae3d1..d0aa13e6 100644 --- a/code/espurna/web.ino +++ b/code/espurna/web.ino @@ -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 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; diff --git a/code/html/custom.js b/code/html/custom.js index c6a2791a..cae8fc19 100644 --- a/code/html/custom.js +++ b/code/html/custom.js @@ -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; } diff --git a/code/html/index.html b/code/html/index.html index ee8ab0c2..e7a7a5bc 100644 --- a/code/html/index.html +++ b/code/html/index.html @@ -136,10 +136,35 @@
- +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
@@ -363,7 +388,28 @@
 
-
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.
+
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.
+ + +
+ + +
 
+
In Volts (V). Enter your the nominal AC voltage for your household or facility, or use multimeter to get this value.
+
+ +
+ + +
 
+
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.
+
+ +
+
+
+
 
+
Move this switch to ON and press "Update" to revert to factory values.
diff --git a/code/platformio.ini b/code/platformio.ini index b8044901..97c03780 100644 --- a/code/platformio.ini +++ b/code/platformio.ini @@ -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