diff --git a/CHANGELOG.md b/CHANGELOG.md index b41f4a4d..57b4226e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,26 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [1.4.4] 2017-01-13 +### Added +- Adding current, voltage, apparent and reactive power reports to Sonoff POW (Web & MQTT) + +### Fixed +- #35 Fixed frequent MQTT connection drops after WIFI reconnect +- Defer wifi disconnection from web interface to allow request to return + +### Changed +- Move all Arduino IDE configuration values to their own file +- Using latest HLW8012 library in interrupt mode + +## [1.4.3] 2017-01-11 +### Fixed +- #6 Using forked Time library to prevent conflict with Arduino Core for ESP8266 time.h file in windows machines + ## [1.4.2] 2017-01-09 +### Added +- Support for inverse logic relays + ### Fixed - Fixed error in relay identification from MQTT messages (issue #31) diff --git a/README.md b/README.md index 4a9031c3..d60fb6d5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ESPurna ("spark" in Catalan) is a custom firmware for ESP8266 based smart switch It was originally developed with the **[IteadStudio Sonoff](https://www.itead.cc/sonoff-wifi-wireless-switch.html)** in mind but now it supports a growing number of ESP8266-based boards. It uses the Arduino Core for ESP8266 framework and a number of 3rd party libraries. -**Current Release Version is 1.4.2**, read the [changelog](CHANGELOG.md). +**Current Release Version is 1.4.4**, read the [changelog](CHANGELOG.md). ## Features @@ -44,11 +44,12 @@ For more information please refer to the [ESPurna Wiki](https://bitbucket.org/xo |![IteadStudio Sonoff Dual](images/devices/sonoff-dual.jpg) **IteadStudio Sonoff Dual**|![IteadStudio Sonoff POW](images/devices/sonoff-pow.jpg) **IteadStudio Sonoff POW**|![IteadStudio Sonoff TH10/TH16](images/devices/sonoff-th10-th16.jpg) **IteadStudio Sonoff TH10/TH16**| |![IteadStudio Sonoff RF](images/devices/sonoff-rf.jpg) **IteadStudio Sonoff RF**|![IteadStudio Sonoff SV](images/devices/sonoff-sv.jpg) **IteadStudio Sonoff SV**|![IteadStudio Sonoff Touch](images/devices/sonoff-touch.jpg) **IteadStudio Sonoff Touch**| |![Wemos D1 Mini Relay Shield](images/devices/d1mini.jpg) **Wemos D1 Mini Relay Shield**|![Electrodragon Relay Board](images/devices/electrodragon-relay-board.jpg) **Electrodragon Relay Board**|![WorkChoice EcoPlug](images/devices/workchoice-ecoplug.jpg) **WorkChoice EcoPlug**| +|![JanGoe Wifi Relay (NO/NC)](images/devices/jangoe-wifi-relay.png) **JanGoe Wifi Relay (NO/NC)**||| ## License -Copyright (C) 2016 by Xose Pérez (@xoseperez) +Copyright (C) 2016-2017 by Xose Pérez (@xoseperez) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/code/debug b/code/debug index b225b90e..7ccba580 100755 --- a/code/debug +++ b/code/debug @@ -4,7 +4,7 @@ # CONFIGURATION # ------------------------------------------------------------------------------ -ENVIRONMENT="node-debug" +ENVIRONMENT="d1-debug" ADDR2LINE=$HOME/.platformio/packages/toolchain-xtensa/bin/xtensa-lx106-elf-addr2line DECODER=utils/EspStackTraceDecoder.jar DECODER_ORIGIN=https://github.com/littleyoda/EspStackTraceDecoder/releases/download/untagged-83b6db3208da17a0f1fd/EspStackTraceDecoder.jar diff --git a/code/espurna/button.ino b/code/espurna/button.ino index 1d39a35f..a9806d3e 100644 --- a/code/espurna/button.ino +++ b/code/espurna/button.ino @@ -1,9 +1,8 @@ /* -ESPurna BUTTON MODULE -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ @@ -13,6 +12,16 @@ Copyright (C) 2016 by Xose Pérez #ifdef SONOFF_DUAL +#ifdef MQTT_BUTTON_TOPIC +void buttonMQTT(unsigned char id) { + String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER); + char buffer[strlen(MQTT_BUTTON_TOPIC) + mqttGetter.length() + 3]; + sprintf(buffer, "%s/%d%s", MQTT_BUTTON_TOPIC, id, mqttGetter.c_str()); + mqttSend(buffer, 1); + mqttSend(buffer, 0); +} +#endif + void buttonSetup() {} void buttonLoop() { @@ -30,7 +39,12 @@ void buttonLoop() { // Since we are not passing back RELAY2 value // (in the relayStatus method) it will only be present // here if it has actually been pressed - if ((value & 4) == 4) value = value ^ 1; + if ((value & 4) == 4) { + value = value ^ 1; + #ifdef MQTT_BUTTON_TOPIC + buttonMQTT(0); + #endif + } // Otherwise check if any of the other two BUTTONs // (in the header) has been pressent, but we should @@ -60,21 +74,36 @@ void buttonLoop() { #include #include -std::vector _buttons; +typedef struct { + DebounceEvent * button; + unsigned int relayID; +} button_t; + +std::vector _buttons; + +#ifdef MQTT_BUTTON_TOPIC +void buttonMQTT(unsigned char id) { + if (id >= _buttons.size()) return; + String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER); + char buffer[strlen(MQTT_BUTTON_TOPIC) + mqttGetter.length() + 3]; + sprintf(buffer, "%s/%d%s", MQTT_BUTTON_TOPIC, id, mqttGetter.c_str()); + mqttSend(buffer, _buttons[id].button->pressed() ? "1" : "0"); +} +#endif void buttonSetup() { #ifdef BUTTON1_PIN - _buttons.push_back(new DebounceEvent(BUTTON1_PIN)); + _buttons.push_back({new DebounceEvent(BUTTON1_PIN), BUTTON1_RELAY}); #endif #ifdef BUTTON2_PIN - _buttons.push_back(new DebounceEvent(BUTTON2_PIN)); + _buttons.push_back({new DebounceEvent(BUTTON2_PIN), BUTTON2_RELAY}); #endif #ifdef BUTTON3_PIN - _buttons.push_back(new DebounceEvent(BUTTON3_PIN)); + _buttons.push_back({new DebounceEvent(BUTTON3_PIN), BUTTON3_RELAY}); #endif #ifdef BUTTON4_PIN - _buttons.push_back(new DebounceEvent(BUTTON4_PIN)); + _buttons.push_back({new DebounceEvent(BUTTON4_PIN), BUTTON4_RELAY}); #endif #ifdef LED_PULSE @@ -90,20 +119,33 @@ void buttonSetup() { void buttonLoop() { for (unsigned int i=0; i < _buttons.size(); i++) { - if (_buttons[i]->loop()) { - uint8_t event = _buttons[i]->getEvent(); + if (_buttons[i].button->loop()) { + + uint8_t event = _buttons[i].button->getEvent(); DEBUG_MSG("[BUTTON] Pressed #%d, event: %d\n", i, event); + + #ifdef MQTT_BUTTON_TOPIC + buttonMQTT(i); + #endif + if (i == 0) { if (event == EVENT_DOUBLE_CLICK) createAP(); if (event == EVENT_LONG_CLICK) ESP.reset(); } + #ifdef ITEAD_1CH_INCHING if (i == 1) { relayPulseToggle(); continue; } #endif - if (event == EVENT_SINGLE_CLICK) relayToggle(i); + + if (event == EVENT_SINGLE_CLICK) { + if (_buttons[i].relayID > 0) { + relayToggle(_buttons[i].relayID - 1); + } + } + } } diff --git a/code/espurna/config/all.h b/code/espurna/config/all.h index 7c49a3e4..ddbefc6d 100644 --- a/code/espurna/config/all.h +++ b/code/espurna/config/all.h @@ -1,4 +1,5 @@ #include "version.h" +#include "arduino.h" #include "debug.h" #include "general.h" #include "hardware.h" diff --git a/code/espurna/config/arduino.h b/code/espurna/config/arduino.h new file mode 100644 index 00000000..340029f6 --- /dev/null +++ b/code/espurna/config/arduino.h @@ -0,0 +1,43 @@ +//-------------------------------------------------------------------------------- +// These settings are normally provided by PlatformIO +// Uncomment the appropiate line(s) to build from the Arduino IDE +//-------------------------------------------------------------------------------- + +//-------------------------------------------------------------------------------- +// General +//-------------------------------------------------------------------------------- + +//#define DEBUG_PORT Serial + +//-------------------------------------------------------------------------------- +// Hardware +//-------------------------------------------------------------------------------- + +//#define D1_RELAYSHIELD +//#define NODEMCUV2 +//#define SONOFF +//#define SONOFF_TH +//#define SLAMPHER +//#define S20 +//#define SONOFF_SV +//#define SONOFF_POW +//#define SONOFF_DUAL +//#define SONOFF_4CH +//#define ESP_RELAY_BOARD +//#define ECOPLUG +//#define WIFI_RELAY_NC +//#define WIFI_RELAY_NO +//#define ESPURNA + +//-------------------------------------------------------------------------------- +// Features (values below are default values) +//-------------------------------------------------------------------------------- + +//#define ENABLE_DHT 0 +//#define ENABLE_DS18B20 0 +//#define ENABLE_EMON 0 +//#define ENABLE_HLW8018 0 +//#define ENABLE_RF 0 +//#define ENABLE_FAUXMO 1 +//#define ENABLE_NOFUSS 0 +//#define ENABLE_DOMOTICZ 1 diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index 7b23d978..2fe1b694 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -80,6 +80,7 @@ #define MQTT_SKIP_TIME 1000 #define MQTT_RELAY_TOPIC "/relay" #define MQTT_LED_TOPIC "/led" +#define MQTT_BUTTON_TOPIC "/button" #define MQTT_IP_TOPIC "/ip" #define MQTT_VERSION_TOPIC "/version" #define MQTT_FSVERSION_TOPIC "/fsversion" @@ -99,7 +100,9 @@ // DOMOTICZ // ----------------------------------------------------------------------------- -#define ENABLE_DOMOTICZ 1 +#ifndef ENABLE_DOMOTICZ + #define ENABLE_DOMOTICZ 1 +#endif #define DOMOTICZ_IN_TOPIC "domoticz/in" #define DOMOTICZ_OUT_TOPIC "domoticz/out" @@ -113,7 +116,15 @@ #define NTP_UPDATE_INTERVAL 1800 // ----------------------------------------------------------------------------- -// FAUXO +// FAUXMO // ----------------------------------------------------------------------------- +// This setting defines whether Alexa support should be built into the firmware +#ifndef ENABLE_FAUXMO + #define ENABLE_FAUXMO 1 +#endif + +// This is default value for the fauxmoEnabled setting that defines whether +// this device should be discoberable and respond to Alexa commands. +// Both ENABLE_FAUXMO and fauxmoEnabled should be 1 for Alexa support to work. #define FAUXMO_ENABLED 1 diff --git a/code/espurna/config/hardware.h b/code/espurna/config/hardware.h index 289bfac5..71ddf4f7 100644 --- a/code/espurna/config/hardware.h +++ b/code/espurna/config/hardware.h @@ -1,34 +1,3 @@ -//-------------------------------------------------------------------------------- -// HARDWARE -// This setting is normally provided by PlatformIO -// Uncomment the appropiate line to build from the Arduino IDE -//-------------------------------------------------------------------------------- - -//#define NODEMCUV2 -//#define SONOFF -//#define SONOFF_TH -//#define SLAMPHER -//#define S20 -//#define SONOFF_SV -//#define SONOFF_POW -//#define SONOFF_DUAL -//#define SONOFF_4CH -//#define ESP_RELAY_BOARD -//#define ECOPLUG -//#define ESPURNA - -//#define ENABLE_DHT 1 -//#define ENABLE_DS18B20 1 -//#define ENABLE_EMON 1 -//#define ENABLE_HLW8018 1 -//#define ENABLE_RF 1 -//#define ENABLE_FAUXMO 0 -//#define ENABLE_NOFUSS 1 - -#ifndef ENABLE_FAUXMO - #define ENABLE_FAUXMO 1 -#endif - // ----------------------------------------------------------------------------- // Development boards // ----------------------------------------------------------------------------- @@ -38,6 +7,7 @@ #define MANUFACTURER "NODEMCU" #define DEVICE "LOLIN" #define BUTTON1_PIN 0 + #define BUTTON1_RELAY 1 #define RELAY1_PIN 12 #define RELAY1_PIN_INVERSE 0 #define LED1_PIN 2 @@ -61,6 +31,7 @@ #define MANUFACTURER "ITEAD" #define DEVICE "SONOFF" #define BUTTON1_PIN 0 + #define BUTTON1_RELAY 1 #define RELAY1_PIN 12 #define RELAY1_PIN_INVERSE 0 #define LED1_PIN 13 @@ -71,6 +42,7 @@ #define MANUFACTURER "ITEAD" #define DEVICE "SONOFF_TH" #define BUTTON1_PIN 0 + #define BUTTON1_RELAY 1 #define RELAY1_PIN 12 #define RELAY1_PIN_INVERSE 0 #define LED1_PIN 13 @@ -81,6 +53,7 @@ #define MANUFACTURER "ITEAD" #define DEVICE "SONOFF_SV" #define BUTTON1_PIN 0 + #define BUTTON1_RELAY 1 #define RELAY1_PIN 12 #define RELAY1_PIN_INVERSE 0 #define LED1_PIN 13 @@ -91,6 +64,7 @@ #define MANUFACTURER "ITEAD" #define DEVICE "SLAMPHER" #define BUTTON1_PIN 0 + #define BUTTON1_RELAY 1 #define RELAY1_PIN 12 #define RELAY1_PIN_INVERSE 0 #define LED1_PIN 13 @@ -101,6 +75,7 @@ #define MANUFACTURER "ITEAD" #define DEVICE "S20" #define BUTTON1_PIN 0 + #define BUTTON1_RELAY 1 #define RELAY1_PIN 12 #define RELAY1_PIN_INVERSE 0 #define LED1_PIN 13 @@ -111,6 +86,7 @@ #define MANUFACTURER "ITEAD" #define DEVICE "SONOFF_TOUCH" #define BUTTON1_PIN 0 + #define BUTTON1_RELAY 1 #define RELAY1_PIN 12 #define RELAY1_PIN_INVERSE 0 #define LED1_PIN 13 @@ -121,6 +97,7 @@ #define MANUFACTURER "ITEAD" #define DEVICE "SONOFF_POW" #define BUTTON1_PIN 0 + #define BUTTON1_RELAY 1 #define RELAY1_PIN 12 #define RELAY1_PIN_INVERSE 0 #define LED1_PIN 15 @@ -142,9 +119,13 @@ #define MANUFACTURER "ITEAD" #define DEVICE "SONOFF_4CH" #define BUTTON1_PIN 0 + #define BUTTON1_RELAY 1 #define BUTTON2_PIN 9 + #define BUTTON2_RELAY 2 #define BUTTON3_PIN 10 + #define BUTTON3_RELAY 3 #define BUTTON4_PIN 14 + #define BUTTON4_RELAY 4 #define RELAY1_PIN 12 #define RELAY1_PIN_INVERSE 0 #define RELAY2_PIN 5 @@ -180,7 +161,9 @@ #define MANUFACTURER "ELECTRODRAGON" #define DEVICE "ESP_RELAY_BOARD" #define BUTTON1_PIN 0 + #define BUTTON1_RELAY 1 #define BUTTON2_PIN 2 + #define BUTTON2_RELAY 2 #define RELAY1_PIN 12 #define RELAY1_PIN_INVERSE 0 #define RELAY2_PIN 13 @@ -197,11 +180,42 @@ #define MANUFACTURER "WORKCHOICE" #define DEVICE "ECOPLUG" #define BUTTON1_PIN 13 + #define BUTTON1_RELAY 1 #define RELAY1_PIN 15 #define RELAY1_PIN_INVERSE 0 #define LED1_PIN 2 #define LED1_PIN_INVERSE 0 +// ----------------------------------------------------------------------------- +// JanGoe Wifi Relay (https://github.com/JanGoe/esp8266-wifi-relay) +// ----------------------------------------------------------------------------- + +#elif defined(WIFI_RELAY_NC) + + #define MANUFACTURER "JANGOE" + #define DEVICE "WIFI_RELAY_NC" + #define BUTTON1_PIN 12 + #define BUTTON1_RELAY 1 + #define BUTTON2_PIN 13 + #define BUTTON2_RELAY 2 + #define RELAY1_PIN 2 + #define RELAY1_PIN_INVERSE 1 + #define RELAY2_PIN 14 + #define RELAY2_PIN_INVERSE 1 + +#elif defined(WIFI_RELAY_NO) + + #define MANUFACTURER "JANGOE" + #define DEVICE "WIFI_RELAY_NO" + #define BUTTON1_PIN 12 + #define BUTTON1_RELAY 1 + #define BUTTON2_PIN 13 + #define BUTTON2_RELAY 2 + #define RELAY1_PIN 2 + #define RELAY1_PIN_INVERSE 0 + #define RELAY2_PIN 14 + #define RELAY2_PIN_INVERSE 0 + // ----------------------------------------------------------------------------- // ESPurna board (still beta) // ----------------------------------------------------------------------------- @@ -211,6 +225,7 @@ #define MANUFACTURER "TINKERMAN" #define DEVICE "ESPURNA" #define BUTTON1_PIN 0 + #define BUTTON1_RELAY 1 #define RELAY1_PIN 12 #define RELAY1_PIN_INVERSE 0 #define LED1_PIN 13 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/config/version.h b/code/espurna/config/version.h index 448d7a24..70a58974 100644 --- a/code/espurna/config/version.h +++ b/code/espurna/config/version.h @@ -1,4 +1,4 @@ #define APP_NAME "ESPurna" -#define APP_VERSION "1.4.2" +#define APP_VERSION "1.4.4" #define APP_AUTHOR "xose.perez@gmail.com" #define APP_WEBSITE "http://tinkerman.cat" diff --git a/code/espurna/data/index.html.gz b/code/espurna/data/index.html.gz index 006c0967..64b44bfd 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..168524e0 100644 Binary files a/code/espurna/data/script.js.gz and b/code/espurna/data/script.js.gz differ diff --git a/code/espurna/dht.ino b/code/espurna/dht.ino index 4da26661..21e8f1f1 100644 --- a/code/espurna/dht.ino +++ b/code/espurna/dht.ino @@ -1,9 +1,8 @@ /* -ESPurna DHT MODULE -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ diff --git a/code/espurna/domoticz.ino b/code/espurna/domoticz.ino index be9b4e59..2bbbcf14 100644 --- a/code/espurna/domoticz.ino +++ b/code/espurna/domoticz.ino @@ -1,9 +1,8 @@ /* -ESPurna DOMOTICZ MODULE -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ diff --git a/code/espurna/ds18b20.ino b/code/espurna/ds18b20.ino index 7ea03cf1..adc95da2 100644 --- a/code/espurna/ds18b20.ino +++ b/code/espurna/ds18b20.ino @@ -1,9 +1,8 @@ /* -ESPurna -SD18B20 MODULE +DS18B20 MODULE -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ diff --git a/code/espurna/emon.ino b/code/espurna/emon.ino index 7bb21e20..ecf28301 100644 --- a/code/espurna/emon.ino +++ b/code/espurna/emon.ino @@ -1,9 +1,8 @@ /* -ESPurna EMON MODULE -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ diff --git a/code/espurna/espurna.ino b/code/espurna/espurna.ino index e91a6d30..3fca7dd0 100644 --- a/code/espurna/espurna.ino +++ b/code/espurna/espurna.ino @@ -1,7 +1,8 @@ /* ESPurna -Copyright (C) 2016 by Xose Pérez + +Copyright (C) 2016-2017 by Xose Pérez This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -175,6 +176,4 @@ void loop() { powerMonitorLoop(); #endif - yield(); - } diff --git a/code/espurna/fauxmo.ino b/code/espurna/fauxmo.ino index 939f1e94..4450394f 100644 --- a/code/espurna/fauxmo.ino +++ b/code/espurna/fauxmo.ino @@ -1,9 +1,8 @@ /* -ESPurna -DHT MODULE +FAUXMO MODULE -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ diff --git a/code/espurna/led.ino b/code/espurna/led.ino index 24d14ed8..630a5790 100644 --- a/code/espurna/led.ino +++ b/code/espurna/led.ino @@ -1,9 +1,8 @@ /* -ESPurna LED MODULE -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ @@ -50,9 +49,9 @@ void ledBlink(unsigned char id, unsigned long delayOff, unsigned long delayOn) { void showStatus() { if (wifiConnected()) { if (WiFi.getMode() == WIFI_AP) { - ledBlink(0, 2000, 2000); + ledBlink(0, 2500, 2500); } else { - ledBlink(0, 5000, 500); + ledBlink(0, 4900, 100); } } else { ledBlink(0, 500, 500); diff --git a/code/espurna/mqtt.ino b/code/espurna/mqtt.ino index d47d5f58..b0302d55 100644 --- a/code/espurna/mqtt.ino +++ b/code/espurna/mqtt.ino @@ -1,9 +1,8 @@ /* -ESPurna MQTT MODULE -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ @@ -43,7 +42,7 @@ unsigned int mqttTopicRootLength() { void mqttSendRaw(const char * topic, const char * message) { if (mqtt.connected()) { - DEBUG_MSG("[MQTT] Sending %s %s\n", topic, message); + DEBUG_MSG("[MQTT] Sending %s => %s\n", topic, message); mqtt.publish(topic, MQTT_QOS, MQTT_RETAIN, message); } } @@ -131,6 +130,8 @@ void mqttConnect() { if (!mqtt.connected()) { + mqtt.disconnect(); + char * host = strdup(getSetting("mqttServer", MQTT_SERVER).c_str()); if (strlen(host) == 0) return; unsigned int port = getSetting("mqttPort", MQTT_PORT).toInt(); @@ -140,6 +141,7 @@ void mqttConnect() { DEBUG_MSG("[MQTT] Connecting to broker at %s", host); mqtt.setServer(host, port); mqtt.setKeepAlive(MQTT_KEEPALIVE).setCleanSession(false); + mqtt.setWill(MQTT_HEARTBEAT_TOPIC, MQTT_QOS, MQTT_RETAIN, "0"); if ((strlen(user) > 0) && (strlen(pass) > 0)) { DEBUG_MSG(" as user '%s'.", user); mqtt.setCredentials(user, pass); diff --git a/code/espurna/nofuss.ino b/code/espurna/nofuss.ino index 35b1e20e..97e4dd1f 100644 --- a/code/espurna/nofuss.ino +++ b/code/espurna/nofuss.ino @@ -1,9 +1,8 @@ /* -ESPurna NOFUSS MODULE -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ diff --git a/code/espurna/ntp.ino b/code/espurna/ntp.ino index 7b6c158d..0772ef20 100644 --- a/code/espurna/ntp.ino +++ b/code/espurna/ntp.ino @@ -1,9 +1,8 @@ /* -ESPURNA NTP MODULE -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ diff --git a/code/espurna/ota.ino b/code/espurna/ota.ino index 56048e0c..07f27c5d 100644 --- a/code/espurna/ota.ino +++ b/code/espurna/ota.ino @@ -1,9 +1,8 @@ /* -ESPurna OTA MODULE -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ @@ -25,10 +24,13 @@ void otaSetup() { ArduinoOTA.onStart([]() { DEBUG_MSG("[OTA] Start\n"); + wsSend("{\"message\": \"OTA update started\"}"); }); ArduinoOTA.onEnd([]() { DEBUG_MSG("\n[OTA] End\n"); + wsSend("{\"action\": \"reload\"}"); + delay(100); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { diff --git a/code/espurna/pow.ino b/code/espurna/pow.ino index bf820296..327ad08f 100644 --- a/code/espurna/pow.ino +++ b/code/espurna/pow.ino @@ -1,10 +1,9 @@ /* -ESPurna POW MODULE Support for Sonoff POW HLW8012-based power monitor -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ @@ -12,6 +11,8 @@ Copyright (C) 2016 by Xose Pérez #include +#define POW_USE_INTERRUPTS 1 + HLW8012 hlw8012; // ----------------------------------------------------------------------------- @@ -29,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()); @@ -77,6 +80,13 @@ void powSetExpectedVoltage(unsigned int voltage) { } } +void powReset() { + hlw8012.resetMultipliers(); + powSaveCalibration(); +} + +// ----------------------------------------------------------------------------- + unsigned int getActivePower() { return hlw8012.getActivePower(); } @@ -85,6 +95,10 @@ unsigned int getApparentPower() { return hlw8012.getApparentPower(); } +unsigned int getReactivePower() { + return hlw8012.getReactivePower(); +} + double getCurrent() { return hlw8012.getCurrent(); } @@ -97,6 +111,8 @@ unsigned int getPowerFactor() { return (int) (100 * hlw8012.getPowerFactor()); } +// ----------------------------------------------------------------------------- + void powSetup() { // Initialize HLW8012 @@ -105,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: @@ -114,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 } @@ -141,20 +149,67 @@ 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(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; + if (factor > 100) factor = 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/relay.ino b/code/espurna/relay.ino index 3fe65617..dfdcffeb 100644 --- a/code/espurna/relay.ino +++ b/code/espurna/relay.ino @@ -1,9 +1,8 @@ /* -ESPurna RELAY MODULE -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ @@ -55,7 +54,7 @@ String relayString() { void relayWS() { String output = relayString(); - wsSend((char *) output.c_str()); + wsSend(output.c_str()); } bool relayStatus(unsigned char id) { diff --git a/code/espurna/rf.ino b/code/espurna/rf.ino index 817c0a8e..0933ed83 100644 --- a/code/espurna/rf.ino +++ b/code/espurna/rf.ino @@ -1,9 +1,8 @@ /* -ESPurna RF MODULE -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ diff --git a/code/espurna/settings.ino b/code/espurna/settings.ino index b5093d72..04463f48 100644 --- a/code/espurna/settings.ino +++ b/code/espurna/settings.ino @@ -1,9 +1,8 @@ /* -ESPurna SETTINGS MODULE -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ diff --git a/code/espurna/web.ino b/code/espurna/web.ino index bb2df58f..69573cc9 100644 --- a/code/espurna/web.ino +++ b/code/espurna/web.ino @@ -1,9 +1,8 @@ /* -ESPurna WEBSERVER MODULE -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ @@ -14,6 +13,7 @@ Copyright (C) 2016 by Xose Pérez #include #include #include +#include AsyncWebServer server(80); AsyncWebSocket ws("/ws"); @@ -24,29 +24,30 @@ typedef struct { } ws_ticket_t; ws_ticket_t _ticket[WS_BUFFER_SIZE]; +Ticker deferred; // ----------------------------------------------------------------------------- // WEBSOCKETS // ----------------------------------------------------------------------------- -bool wsSend(char * payload) { - //DEBUG_MSG("[WEBSOCKET] Broadcasting '%s'\n", payload); +bool wsSend(const char * payload) { + DEBUG_MSG("[WEBSOCKET] Broadcasting '%s'\n", payload); ws.textAll(payload); } -bool wsSend(uint32_t client_id, char * payload) { - //DEBUG_MSG("[WEBSOCKET] Sending '%s' to #%ld\n", payload, client_id); +bool wsSend(uint32_t client_id, const char * payload) { + DEBUG_MSG("[WEBSOCKET] Sending '%s' to #%ld\n", payload, client_id); ws.text(client_id, payload); } void wsMQTTCallback(unsigned int type, const char * topic, const char * payload) { if (type == MQTT_CONNECT_EVENT) { - wsSend((char *) "{\"mqttStatus\": true}"); + wsSend("{\"mqttStatus\": true}"); } if (type == MQTT_DISCONNECT_EVENT) { - wsSend((char *) "{\"mqttStatus\": false}"); + wsSend("{\"mqttStatus\": false}"); } } @@ -75,7 +76,12 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { DEBUG_MSG("[WEBSOCKET] Requested action: %s\n", action.c_str()); if (action.equals("reset")) ESP.reset(); - if (action.equals("reconnect")) wifiDisconnect(); + if (action.equals("reconnect")) { + + // 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); @@ -87,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; @@ -106,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") { @@ -179,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; } } @@ -188,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 @@ -207,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)); @@ -218,7 +238,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { } // Save settings - if (dirty) { + if (save) { saveSettings(); wifiConfigure(); @@ -242,16 +262,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\"}"); - } } @@ -346,6 +365,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; @@ -547,8 +571,6 @@ ArRequestHandlerFunction _onRelayStatusWrapper(unsigned int relayID) { if (request->method() == HTTP_PUT) { if (request->hasParam("status", true)) { AsyncWebParameter* p = request->getParam("status", true); - wsSend((char *) String(relayID).c_str()); - wsSend((char *) p->value().c_str()); unsigned int value = p->value().toInt(); if (value == 2) { relayToggle(relayID); diff --git a/code/espurna/wifi.ino b/code/espurna/wifi.ino index 22ec197a..9f65520d 100644 --- a/code/espurna/wifi.ino +++ b/code/espurna/wifi.ino @@ -1,9 +1,8 @@ /* -ESPurna WIFI MODULE -Copyright (C) 2016 by Xose Pérez +Copyright (C) 2016-2017 by Xose Pérez */ @@ -148,11 +147,6 @@ void wifiSetup() { #endif - // Disconnect from MQTT server if no WIFI - if (code != MESSAGE_CONNECTED) { - if (mqttConnected()) mqttDisconnect(); - } - // Configure mDNS if (code == MESSAGE_CONNECTED) { diff --git a/code/gulpfile.js b/code/gulpfile.js index 1bf02633..35279c9f 100644 --- a/code/gulpfile.js +++ b/code/gulpfile.js @@ -2,7 +2,7 @@ ESP8266 file system builder -Copyright (C) 2016 by Xose PĆ©rez +Copyright (C) 2016 by Xose Pérez This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/code/html/custom.js b/code/html/custom.js index c6a2791a..118c1ac7 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; } @@ -204,6 +207,8 @@ function processData(data) { window.location = "/"; }); + } else { + window.location = "/"; } } diff --git a/code/html/index.html b/code/html/index.html index 48b1b969..defd5d43 100644 --- a/code/html/index.html +++ b/code/html/index.html @@ -136,10 +136,35 @@
- +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
@@ -217,7 +242,7 @@
-
+
@@ -383,7 +408,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 f4256545..cb9ca7eb 100644 --- a/code/platformio.ini +++ b/code/platformio.ini @@ -7,7 +7,7 @@ data_dir = espurna/data lib_deps = DHT sensor library Adafruit Unified Sensor - Time + https://github.com/xoseperez/Time ArduinoJson ESPAsyncTCP ESPAsyncWebServer @@ -16,8 +16,8 @@ lib_deps = NtpClientLib OneWire DallasTemperature - JustWifi - HLW8012 + https://bitbucket.org/xoseperez/justwifi.git + 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 @@ -263,3 +263,24 @@ build_flags = -g -Wl,-Tesp8266.flash.1m128.ld -DDEBUG_PORT=Serial -DECOPLUG upload_speed = 115200 upload_port = "192.168.4.1" upload_flags = --auth=fibonacci --port 8266 + +[env:jangoe-debug] +platform = espressif8266 +framework = arduino +board = esp01_1m +lib_deps = ${common.lib_deps} +lib_ignore = ${common.lib_ignore} +extra_script = pio_hooks.py +build_flags = -g -DDEBUG_PORT=Serial -DWIFI_RELAY_NC + +[env:jangoe-debug-ota] +platform = espressif8266 +framework = arduino +board = esp01_1m +lib_deps = ${common.lib_deps} +lib_ignore = ${common.lib_ignore} +extra_script = pio_hooks.py +build_flags = -g -DDEBUG_PORT=Serial -DWIFI_RELAY_NC +upload_speed = 115200 +upload_port = "192.168.4.1" +upload_flags = --auth=fibonacci --port 8266 diff --git a/images/devices/jangoe-wifi-relay.png b/images/devices/jangoe-wifi-relay.png new file mode 100644 index 00000000..271b6c7d Binary files /dev/null and b/images/devices/jangoe-wifi-relay.png differ