Browse Source

Merge branch 'dev' into ssl

Conflicts:
	code/espurna/config/general.h
	code/espurna/static/index.html.gz.h
	code/espurna/web.ino
	code/platformio.ini
fastled
Xose Pérez 7 years ago
parent
commit
91b66876af
29 changed files with 3861 additions and 630 deletions
  1. +14
    -0
      CHANGELOG.md
  2. +1
    -1
      README.md
  3. +18
    -0
      code/esp8266.flash.1m0.ld
  4. +0
    -2
      code/espurna/analog.ino
  5. +19
    -7
      code/espurna/button.ino
  6. +4
    -1
      code/espurna/config/arduino.h
  7. +112
    -94
      code/espurna/config/general.h
  8. +28
    -2
      code/espurna/config/hardware.h
  9. +1
    -0
      code/espurna/config/prototypes.h
  10. +25
    -25
      code/espurna/config/sensors.h
  11. +1
    -1
      code/espurna/config/version.h
  12. +1
    -0
      code/espurna/debug.ino
  13. +63
    -0
      code/espurna/domoticz.ino
  14. +71
    -60
      code/espurna/emon.ino
  15. +22
    -19
      code/espurna/espurna.ino
  16. +110
    -90
      code/espurna/hlw8012.ino
  17. +82
    -15
      code/espurna/mqtt.ino
  18. +15
    -3
      code/espurna/ntp.ino
  19. +61
    -114
      code/espurna/relay.ino
  20. +6
    -0
      code/espurna/settings.ino
  21. +3034
    -62
      code/espurna/static/index.html.gz.h
  22. +13
    -0
      code/espurna/utils.ino
  23. +40
    -18
      code/espurna/web.ino
  24. +6
    -5
      code/espurna/wifi.ino
  25. +2
    -10
      code/gulpfile.js
  26. +6
    -2
      code/html/custom.js
  27. +44
    -10
      code/html/index.html
  28. +0
    -11
      code/pio_hooks.py
  29. +62
    -78
      code/platformio.ini

+ 14
- 0
CHANGELOG.md View File

@ -3,6 +3,20 @@
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [1.8.3] 2017-07-23
### Added
- Issue #85 and #90. Option to report MQTT messages with JSON payloads
- Issue #170. Updated DebouceEvent library to allow disabling double click and get faster click responses
- Using memory layout with no SPIFFS for 1Mb devices
### Changed
- Rename settings s/POW/HLW8012/
- Return times in ISO8601 format
### Fix
- Issue #168. Added H801 to arduino.h file
- Issue #171. Fix corrupted will message
## [1.8.2] 2017-07-16
### Added
- InfluxDB support via HTTP API


+ 1
- 1
README.md View File

@ -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.8.2**, read the [changelog](https://bitbucket.org/xoseperez/espurna/src/master/CHANGELOG.md).
**Current Release Version is 1.8.3**, read the [changelog](https://bitbucket.org/xoseperez/espurna/src/master/CHANGELOG.md).
## Features


+ 18
- 0
code/esp8266.flash.1m0.ld View File

@ -0,0 +1,18 @@
/* Flash Split for 1M chips */
/* sketch 999KB */
/* eeprom 20KB */
MEMORY
{
dport0_0_seg : org = 0x3FF00000, len = 0x10
dram0_0_seg : org = 0x3FFE8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x8000
irom0_0_seg : org = 0x40201010, len = 0xf9ff0
}
PROVIDE ( _SPIFFS_start = 0x402FB000 );
PROVIDE ( _SPIFFS_end = 0x402FB000 );
PROVIDE ( _SPIFFS_page = 0 );
PROVIDE ( _SPIFFS_block = 0 );
INCLUDE "esp8266.flash.common.ld"

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

@ -8,8 +8,6 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
#if ENABLE_ANALOG
int _analog = 0;
// -----------------------------------------------------------------------------
// ANALOG
// -----------------------------------------------------------------------------


+ 19
- 7
code/espurna/button.ino View File

@ -68,8 +68,8 @@ uint8_t mapEvent(uint8_t event, uint8_t count, uint16_t length) {
if (event == EVENT_CHANGED) return BUTTON_EVENT_CLICK;
if (event == EVENT_RELEASED) {
if (count == 1) {
if (length > BUTTON_LNGLNGCLICK_LENGTH) return BUTTON_EVENT_LNGLNGCLICK;
if (length > BUTTON_LNGCLICK_LENGTH) return BUTTON_EVENT_LNGCLICK;
if (length > BUTTON_LNGLNGCLICK_DELAY) return BUTTON_EVENT_LNGLNGCLICK;
if (length > BUTTON_LNGCLICK_DELAY) return BUTTON_EVENT_LNGCLICK;
return BUTTON_EVENT_CLICK;
}
if (count == 2) return BUTTON_EVENT_DBLCLICK;
@ -92,6 +92,16 @@ void buttonEvent(unsigned int id, unsigned char event) {
relayToggle(_buttons[id].relayID - 1);
}
}
if (action == BUTTON_MODE_ON) {
if (_buttons[id].relayID > 0) {
relayStatus(_buttons[id].relayID - 1, true);
}
}
if (action == BUTTON_MODE_OFF) {
if (_buttons[id].relayID > 0) {
relayStatus(_buttons[id].relayID - 1, false);
}
}
if (action == BUTTON_MODE_AP) createAP();
if (action == BUTTON_MODE_RESET) {
customReset(CUSTOM_RESET_HARDWARE);
@ -118,28 +128,30 @@ void buttonSetup() {
#else
unsigned long btnDelay = getSetting("btnDelay", BUTTON_DBLCLICK_DELAY).toInt();
#ifdef BUTTON1_PIN
{
unsigned int actions = buttonStore(BUTTON1_PRESS, BUTTON1_CLICK, BUTTON1_DBLCLICK, BUTTON1_LNGCLICK, BUTTON1_LNGLNGCLICK);
_buttons.push_back({new DebounceEvent(BUTTON1_PIN, BUTTON1_MODE), actions, BUTTON1_RELAY});
_buttons.push_back({new DebounceEvent(BUTTON1_PIN, BUTTON1_MODE, BUTTON_DEBOUNCE_DELAY, btnDelay), actions, BUTTON1_RELAY});
}
#endif
#ifdef BUTTON2_PIN
{
unsigned int actions = buttonStore(BUTTON2_PRESS, BUTTON2_CLICK, BUTTON2_DBLCLICK, BUTTON2_LNGCLICK, BUTTON2_LNGLNGCLICK);
_buttons.push_back({new DebounceEvent(BUTTON2_PIN, BUTTON2_MODE), actions, BUTTON2_RELAY});
_buttons.push_back({new DebounceEvent(BUTTON2_PIN, BUTTON2_MODE, BUTTON_DEBOUNCE_DELAY, btnDelay), actions, BUTTON2_RELAY});
}
#endif
#ifdef BUTTON3_PIN
{
unsigned int actions = buttonStore(BUTTON3_PRESS, BUTTON3_CLICK, BUTTON3_DBLCLICK, BUTTON3_LNGCLICK, BUTTON3_LNGLNGCLICK);
_buttons.push_back({new DebounceEvent(BUTTON3_PIN, BUTTON3_MODE), actions, BUTTON3_RELAY});
_buttons.push_back({new DebounceEvent(BUTTON3_PIN, BUTTON3_MODE, BUTTON_DEBOUNCE_DELAY, btnDelay), actions, BUTTON3_RELAY});
}
#endif
#ifdef BUTTON4_PIN
{
unsigned int actions = buttonStore(BUTTON4_PRESS, BUTTON4_CLICK, BUTTON4_DBLCLICK, BUTTON4_LNGCLICK, BUTTON4_LNGLNGCLICK);
_buttons.push_back({new DebounceEvent(BUTTON4_PIN, BUTTON4_MODE), actions, BUTTON4_RELAY});
_buttons.push_back({new DebounceEvent(BUTTON4_PIN, BUTTON4_MODE, BUTTON_DEBOUNCE_DELAY, btnDelay), actions, BUTTON4_RELAY});
}
#endif
@ -172,7 +184,7 @@ void buttonLoop() {
}
// Otherwise check if any of the other two BUTTONs
// (in the header) has been pressent, but we should
// (in the header) has been pressed, but we should
// ensure that we only toggle one of them to avoid
// the synchronization going mad
// This loop is generic for any PSB-04 module


+ 4
- 1
code/espurna/config/arduino.h View File

@ -28,6 +28,7 @@
//#define WIFI_RELAYS_BOARD_KIT
//#define AI_LIGHT
//#define LED_CONTROLLER
//#define H801_LED_CONTROLLER
//#define ESPURNA_H
//--------------------------------------------------------------------------------
@ -37,9 +38,11 @@
//#define ENABLE_DHT 1
//#define ENABLE_DS18B20 1
//#define ENABLE_EMON 1
//#define ENABLE_HLW8018 1
//#define ENABLE_HLW8012 1
//#define ENABLE_RF 1
//#define ENABLE_FAUXMO 0
//#define ENABLE_NOFUSS 1
//#define ENABLE_DOMOTICZ 0
//#define ENABLE_ANALOG 1
//#define ENABLE_INFLUXDB 0
//#define ENABLE_I2C 1

+ 112
- 94
code/espurna/config/general.h View File

@ -1,19 +1,22 @@
//------------------------------------------------------------------------------
// Do not change this file unless you know what you are doing
// Configuration settings are in the settings.h file
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// GENERAL
//------------------------------------------------------------------------------
#define SERIAL_BAUDRATE 115200
#define HOSTNAME DEVICE
#define BUFFER_SIZE 1024
#define HEARTBEAT_INTERVAL 300000
#define UPTIME_OVERFLOW 4294967295
#define SERIAL_BAUDRATE 115200 // Debugging console boud rate
#define HOSTNAME DEVICE // Hostname
#define UPTIME_OVERFLOW 4294967295 // Uptime overflow value
//--------------------------------------------------------------------------------
// DEBUG
//--------------------------------------------------------------------------------
#ifndef DEBUG_PORT
#define DEBUG_PORT Serial
#define DEBUG_PORT Serial // Default debugging port
#endif
// Uncomment and configure these lines to enable remote debug via udpDebug
@ -27,24 +30,45 @@
// EEPROM
//--------------------------------------------------------------------------------
#define EEPROM_RELAY_STATUS 0
#define EEPROM_ENERGY_COUNT 1
#define EEPROM_CUSTOM_RESET 5
#define EEPROM_DATA_END 6
#define EEPROM_RELAY_STATUS 0 // Address for the relay status (1 byte)
#define EEPROM_ENERGY_COUNT 1 // Address for the energy counter (4 bytes)
#define EEPROM_CUSTOM_RESET 5 // Address for the reset reason (1 byte)
#define EEPROM_DATA_END 6 // End of custom EEPROM data block
//--------------------------------------------------------------------------------
// HEARTBEAT
//--------------------------------------------------------------------------------
#define HEARTBEAT_INTERVAL 300000 // Interval between heartbeat messages (in ms)
// Topics that will be reported in heartbeat
#define HEARTBEAT_REPORT_STATUS 1
#define HEARTBEAT_REPORT_IP 1
#define HEARTBEAT_REPORT_MAC 1
#define HEARTBEAT_REPORT_RSSI 1
#define HEARTBEAT_REPORT_UPTIME 1
#define HEARTBEAT_REPORT_FREEHEAP 1
#define HEARTBEAT_REPORT_VCC 1
#define HEARTBEAT_REPORT_RELAY 1
#define HEARTBEAT_REPORT_COLOR 1
#define HEARTBEAT_REPORT_HOSTNAME 1
#define HEARTBEAT_REPORT_APP 1
#define HEARTBEAT_REPORT_VERSION 1
#define HEARTBEAT_REPORT_INTERVAL 0
//--------------------------------------------------------------------------------
// RESET
//--------------------------------------------------------------------------------
#define CUSTOM_RESET_HARDWARE 1
#define CUSTOM_RESET_WEB 2
#define CUSTOM_RESET_TERMINAL 3
#define CUSTOM_RESET_MQTT 4
#define CUSTOM_RESET_RPC 5
#define CUSTOM_RESET_OTA 6
#define CUSTOM_RESET_NOFUSS 8
#define CUSTOM_RESET_UPGRADE 9
#define CUSTOM_RESET_FACTORY 10
#define CUSTOM_RESET_HARDWARE 1 // Reset from hardware button
#define CUSTOM_RESET_WEB 2 // Reset from web interface
#define CUSTOM_RESET_TERMINAL 3 // Reset from terminal
#define CUSTOM_RESET_MQTT 4 // Reset via MQTT
#define CUSTOM_RESET_RPC 5 // Reset via RPC (HTTP)
#define CUSTOM_RESET_OTA 6 // Reset after successful OTA update
#define CUSTOM_RESET_NOFUSS 8 // Reset after successful NOFUSS update
#define CUSTOM_RESET_UPGRADE 9 // Reset after update from web interface
#define CUSTOM_RESET_FACTORY 10 // Factory reset from terminal
#define CUSTOM_RESET_MAX 10
@ -69,11 +93,14 @@ PROGMEM const char* const custom_reset_string[] = {
// BUTTON
//--------------------------------------------------------------------------------
#define BUTTON_LNGCLICK_LENGTH 1000
#define BUTTON_LNGLNGCLICK_LENGTH 10000
#define BUTTON_DEBOUNCE_DELAY 50 // Debounce delay (ms)
#define BUTTON_DBLCLICK_DELAY 500 // Time in ms to wait for a second (or third...) click
#define BUTTON_LNGCLICK_DELAY 1000 // Time in ms holding the button down to get a long click
#define BUTTON_LNGLNGCLICK_DELAY 10000 // Time in ms holding the button down to get a long-long click
#define BUTTON_EVENT_NONE 0
#define BUTTON_EVENT_PRESSED 1
#define BUTTON_EVENT_RELEASED 2
#define BUTTON_EVENT_CLICK 2
#define BUTTON_EVENT_DBLCLICK 3
#define BUTTON_EVENT_LNGCLICK 4
@ -81,12 +108,12 @@ PROGMEM const char* const custom_reset_string[] = {
#define BUTTON_MODE_NONE 0
#define BUTTON_MODE_TOGGLE 1
#define BUTTON_MODE_AP 2
#define BUTTON_MODE_RESET 3
#define BUTTON_MODE_PULSE 4
#define BUTTON_MODE_FACTORY 5
#define BUTTON_DEFAULT_MODE BUTTON_MODE_TOGGLE
#define BUTTON_MODE_ON 2
#define BUTTON_MODE_OFF 3
#define BUTTON_MODE_AP 4
#define BUTTON_MODE_RESET 5
#define BUTTON_MODE_PULSE 6
#define BUTTON_MODE_FACTORY 7
//--------------------------------------------------------------------------------
// RELAY
@ -134,7 +161,7 @@ PROGMEM const char* const custom_reset_string[] = {
#define TMP_CELSIUS 0
#define TMP_FAHRENHEIT 1
#define TMP_UNITS TMP_CELSIUS
#define TMP_UNITS TMP_CELSIUS // Temperature units (TMP_CELSIUS | TMP_FAHRENHEIT)
//--------------------------------------------------------------------------------
// LED
@ -149,16 +176,17 @@ PROGMEM const char* const custom_reset_string[] = {
// WIFI & WEB
// -----------------------------------------------------------------------------
#define WIFI_RECONNECT_INTERVAL 120000
#define WIFI_MAX_NETWORKS 5
#define ADMIN_PASS "fibonacci"
#define FORCE_CHANGE_PASS 1
#define HTTP_USERNAME "admin"
#define WS_BUFFER_SIZE 5
#define WS_TIMEOUT 1800000
#define WEBSERVER_PORT 80
#define DNS_PORT 53
#define ENABLE_MDNS 1
#define WIFI_CONNECT_TIMEOUT 30000 // Connecting timeout for WIFI in ms
#define WIFI_RECONNECT_INTERVAL 120000 // If could not connect to WIFI, retry after this time in ms
#define WIFI_MAX_NETWORKS 5 // Max number of WIFI connection configurations
#define HTTP_USERNAME "admin" // HTTP username
#define ADMIN_PASS "fibonacci" // Default password
#define FORCE_CHANGE_PASS 1 // Force the user to change the password if default one
#define WS_BUFFER_SIZE 5 // Max number of secured websocket connections
#define WS_TIMEOUT 1800000 // Timeout for secured websocket
#define WEBSERVER_PORT 80 // HTTP port
#define DNS_PORT 53 // MDNS port
#define ENABLE_MDNS 1 // Enabled MDNS
#define WEB_MODE_NORMAL 0
#define WEB_MODE_PASSWORD 1
@ -166,9 +194,6 @@ PROGMEM const char* const custom_reset_string[] = {
#define AP_MODE AP_MODE_ALONE
// This option builds the firmware with the web interface embedded.
// You first have to build the data.h file that holds the contents
// of the web interface by running "gulp buildfs_embed"
#ifndef EMBEDDED_WEB
#define EMBEDDED_WEB 1
#endif
@ -177,16 +202,16 @@ PROGMEM const char* const custom_reset_string[] = {
// OTA & NOFUSS
// -----------------------------------------------------------------------------
#define OTA_PORT 8266
#define NOFUSS_SERVER ""
#define NOFUSS_INTERVAL 3600000
#define OTA_PORT 8266 // OTA port
#define NOFUSS_SERVER "" // Default NoFuss Server
#define NOFUSS_INTERVAL 3600000 // Check for updates every hour
// -----------------------------------------------------------------------------
// MQTT
// -----------------------------------------------------------------------------
#ifndef MQTT_USE_ASYNC
#define MQTT_USE_ASYNC 1
#define MQTT_USE_ASYNC 1 // Use AysncMQTTClient (1) or PubSubClient (0)
#endif
// You will need the fingerprint for your MQTT server, example for CloudMQTT:
@ -194,18 +219,23 @@ PROGMEM const char* const custom_reset_string[] = {
// $ openssl x509 -noout -in cloudmqtt.pem -fingerprint -sha1
#define MQTT_USE_SSL 0
#define MQTT_SERVER ""
#define MQTT_PORT 1883
#define MQTT_TOPIC "/test/switch/{identifier}"
#define MQTT_RETAIN true
#define MQTT_QOS 0
#define MQTT_KEEPALIVE 30
#define MQTT_RECONNECT_DELAY 10000
#define MQTT_TRY_INTERVAL 30000
#define MQTT_MAX_TRIES 12
#define MQTT_SKIP_RETAINED 1
#define MQTT_SKIP_TIME 1000
#define MQTT_SERVER "" // Default MQTT broker address
#define MQTT_PORT 1883 // MQTT broker port
#define MQTT_TOPIC "/test/switch/{identifier}" // Default MQTT base topic
#define MQTT_RETAIN true // MQTT retain flag
#define MQTT_QOS 0 // MQTT QoS value for all messages
#define MQTT_KEEPALIVE 30 // MQTT keepalive value
#define MQTT_RECONNECT_DELAY 10000 // Try to reconnect after 10s
#define MQTT_TRY_INTERVAL 30000 // Timeframe for disconnect retries
#define MQTT_MAX_TRIES 12 // After these many retries during the previous MQTT_TRY_INTERVAL the board will reset
#define MQTT_SKIP_RETAINED 1 // Skip retained messages on connection
#define MQTT_SKIP_TIME 1000 // Skip messages for 1 second anter connection
#define MQTT_USE_JSON 0 // Group messages in a JSON body
#define MQTT_USE_JSON_DELAY 100 // Wait this many ms before grouping messages
// These particles will be concatenated to the MQTT_TOPIC base to form the actual topic
#define MQTT_TOPIC_JSON "data"
#define MQTT_TOPIC_ACTION "action"
#define MQTT_TOPIC_RELAY "relay"
#define MQTT_TOPIC_LED "led"
@ -221,47 +251,34 @@ PROGMEM const char* const custom_reset_string[] = {
#define MQTT_TOPIC_RSSI "rssi"
#define MQTT_TOPIC_APP "app"
#define MQTT_TOPIC_INTERVAL "interval"
#define MQTT_TOPIC_HOSTNAME "hostname"
#define MQTT_TOPIC_HOSTNAME "host"
#define MQTT_TOPIC_TIME "time"
#define MQTT_TOPIC_ANALOG "analog"
// Periodic reports
#define MQTT_REPORT_STATUS 1
#define MQTT_REPORT_IP 1
#define MQTT_REPORT_MAC 1
#define MQTT_REPORT_RSSI 1
#define MQTT_REPORT_UPTIME 1
#define MQTT_REPORT_FREEHEAP 1
#define MQTT_REPORT_VCC 1
#define MQTT_REPORT_RELAY 1
#define MQTT_REPORT_COLOR 1
#define MQTT_REPORT_HOSTNAME 1
#define MQTT_REPORT_APP 1
#define MQTT_REPORT_VERSION 1
#define MQTT_REPORT_INTERVAL 0
#define MQTT_STATUS_ONLINE "1"
#define MQTT_STATUS_OFFLINE "0"
#define MQTT_ACTION_RESET "reset"
#define MQTT_STATUS_ONLINE "1" // Value for the device ON message
#define MQTT_STATUS_OFFLINE "0" // Value for the device OFF message (will)
#define MQTT_ACTION_RESET "reset" // RESET MQTT topic particle
// Internal MQTT events (do not change)
#define MQTT_CONNECT_EVENT 0
#define MQTT_DISCONNECT_EVENT 1
#define MQTT_MESSAGE_EVENT 2
// Custom get and set postfixes6+
// Custom get and set postfixes
// Use something like "/status" or "/set", with leading slash
#define MQTT_USE_GETTER ""
#define MQTT_USE_SETTER ""
#define MQTT_USE_SETTER "/set"
// -----------------------------------------------------------------------------
// I2C
// -----------------------------------------------------------------------------
#define ENABLE_I2C 0
#define I2C_SDA_PIN 4
#define I2C_SCL_PIN 14
#define I2C_CLOCK_STRETCH_TIME 200
#define I2C_SCL_FREQUENCY 1000
#define ENABLE_I2C 0 // I2C enabled
#define I2C_SDA_PIN 4 // SDA GPIO
#define I2C_SCL_PIN 14 // SCL GPIO
#define I2C_CLOCK_STRETCH_TIME 200 // BRZO clock stretch time
#define I2C_SCL_FREQUENCY 1000 // BRZO SCL frequency
// -----------------------------------------------------------------------------
// LIGHT
@ -280,6 +297,7 @@ PROGMEM const char* const custom_reset_string[] = {
#define LIGHT_SAVE_DELAY 5
#define LIGHT_MAX_VALUE 255
// Settings for MY9291 bulbs (AI Light)
#define MY9291_DI_PIN 13
#define MY9291_DCKI_PIN 15
#define MY9291_COMMAND MY9291_COMMAND_DEFAULT
@ -296,28 +314,28 @@ PROGMEM const char* const custom_reset_string[] = {
// -----------------------------------------------------------------------------
#ifndef ENABLE_DOMOTICZ
#define ENABLE_DOMOTICZ 1
#define ENABLE_DOMOTICZ 1 // Enable Domoticz support by default
#endif
#define DOMOTICZ_IN_TOPIC "domoticz/in"
#define DOMOTICZ_OUT_TOPIC "domoticz/out"
#define DOMOTICZ_IN_TOPIC "domoticz/in" // Default subscription topic
#define DOMOTICZ_OUT_TOPIC "domoticz/out" // Default publication topic
// -----------------------------------------------------------------------------
// INFLUXDB
// -----------------------------------------------------------------------------
#ifndef ENABLE_INFLUXDB
#define ENABLE_INFLUXDB 1
#define ENABLE_INFLUXDB 1 // Enable InfluxDB support by default
#endif
#define INFLUXDB_PORT 8086
#define INFLUXDB_PORT 8086 // Default InfluxDB port
// -----------------------------------------------------------------------------
// NTP
// -----------------------------------------------------------------------------
#define NTP_SERVER "pool.ntp.org"
#define NTP_TIME_OFFSET 1
#define NTP_DAY_LIGHT true
#define NTP_UPDATE_INTERVAL 1800
#define NTP_SERVER "pool.ntp.org" // Default NTP server
#define NTP_TIME_OFFSET 1 // Default timezone offset (GMT+1)
#define NTP_DAY_LIGHT true // Enable daylight time saving by default
#define NTP_UPDATE_INTERVAL 1800 // NTP check every 30 minutes
// -----------------------------------------------------------------------------
// FAUXMO
@ -325,7 +343,7 @@ PROGMEM const char* const custom_reset_string[] = {
// This setting defines whether Alexa support should be built into the firmware
#ifndef ENABLE_FAUXMO
#define ENABLE_FAUXMO 1
#define ENABLE_FAUXMO 1
#endif
// This is default value for the fauxmoEnabled setting that defines whether


+ 28
- 2
code/espurna/config/hardware.h View File

@ -59,7 +59,7 @@
#define BUTTON1_PIN 4
#define BUTTON1_RELAY 1
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define ENABLE_POW 1
#define ENABLE_HLW8012 1
// -----------------------------------------------------------------------------
// Itead Studio boards
@ -148,7 +148,7 @@
#define RELAY1_PIN_INVERSE 0
#define LED1_PIN 15
#define LED1_PIN_INVERSE 0
#define ENABLE_POW 1
#define ENABLE_HLW8012 1
#elif defined(SONOFF_DUAL)
@ -477,6 +477,32 @@
#define BUTTON4_RELAY 0
#endif
#ifndef RELAY1_DELAY_ON
#define RELAY1_DELAY_ON 0
#endif
#ifndef RELAY2_DELAY_ON
#define RELAY2_DELAY_ON 0
#endif
#ifndef RELAY3_DELAY_ON
#define RELAY3_DELAY_ON 0
#endif
#ifndef RELAY4_DELAY_ON
#define RELAY4_DELAY_ON 0
#endif
#ifndef RELAY1_DELAY_OFF
#define RELAY1_DELAY_OFF 0
#endif
#ifndef RELAY2_DELAY_OFF
#define RELAY2_DELAY_OFF 0
#endif
#ifndef RELAY3_DELAY_OFF
#define RELAY3_DELAY_OFF 0
#endif
#ifndef RELAY4_DELAY_OFF
#define RELAY4_DELAY_OFF 0
#endif
#ifndef RELAY1_LED
#define RELAY1_LED 0
#endif


+ 1
- 0
code/espurna/config/prototypes.h View File

@ -17,3 +17,4 @@ template<typename T> String getSetting(const String& key, unsigned int index, T
template<typename T> void domoticzSend(const char * key, T value);
template<typename T> void domoticzSend(const char * key, T nvalue, const char * svalue);
template<typename T> bool influxDBSend(const char * topic, T payload);
char * ltrim(char * s);

+ 25
- 25
code/espurna/config/sensors.h View File

@ -91,37 +91,37 @@
#define EMON_CURRENT_TOPIC "current"
//--------------------------------------------------------------------------------
// HLW8012 power sensor (Sonoff POW)
// Enable support by passing ENABLE_POW=1 build flag
// HLW8012 power sensor (Sonoff POW, Espurna H)
// Enable support by passing ENABLE_HLW8012=1 build flag
// Enabled by default when selecting SONOFF_POW hardware
//--------------------------------------------------------------------------------
#ifdef ESPURNA_H
#define POW_SEL_PIN 2
#define HLW8012_SEL_PIN 2
#else
#define POW_SEL_PIN 5
#define HLW8012_SEL_PIN 5
#endif
#define POW_CF1_PIN 13
#define POW_CF_PIN 14
#define POW_USE_INTERRUPTS 1
#define POW_SEL_CURRENT HIGH
#define POW_CURRENT_R 0.001
#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_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
#define POW_MIN_POWER 5
#define POW_MAX_POWER 2500
#define POW_MIN_CURRENT 0.05
#define POW_MAX_CURRENT 10
#define HLW8012_CF1_PIN 13
#define HLW8012_CF_PIN 14
#define HLW8012_USE_INTERRUPTS 1
#define HLW8012_SEL_CURRENT HIGH
#define HLW8012_CURRENT_R 0.001
#define HLW8012_VOLTAGE_R_UP ( 5 * 470000 ) // Real: 2280k
#define HLW8012_VOLTAGE_R_DOWN ( 1000 ) // Real 1.009k
#define HLW8012_POWER_TOPIC "power"
#define HLW8012_CURRENT_TOPIC "current"
#define HLW8012_VOLTAGE_TOPIC "voltage"
#define HLW8012_APOWER_TOPIC "apower"
#define HLW8012_RPOWER_TOPIC "rpower"
#define HLW8012_PFACTOR_TOPIC "pfactor"
#define HLW8012_ENERGY_TOPIC "energy"
#define HLW8012_UPDATE_INTERVAL 5000
#define HLW8012_REPORT_EVERY 12
#define HLW8012_MIN_POWER 5
#define HLW8012_MAX_POWER 2500
#define HLW8012_MIN_CURRENT 0.05
#define HLW8012_MAX_CURRENT 10
//--------------------------------------------------------------------------------
// Internal power montior


+ 1
- 1
code/espurna/config/version.h View File

@ -1,4 +1,4 @@
#define APP_NAME "ESPurna"
#define APP_VERSION "1.8.2"
#define APP_VERSION "1.8.3"
#define APP_AUTHOR "xose.perez@gmail.com"
#define APP_WEBSITE "http://tinkerman.cat"

+ 1
- 0
code/espurna/debug.ino View File

@ -37,6 +37,7 @@ void debugSend(const char * format, ...) {
udpDebug.write(" (...)\n");
}
udpDebug.endPacket();
delay(1);
#endif
}


+ 63
- 0
code/espurna/domoticz.ino View File

@ -8,6 +8,8 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
#if ENABLE_DOMOTICZ
#include <ArduinoJson.h>
template<typename T> void domoticzSend(const char * key, T nvalue, const char * svalue) {
unsigned int idx = getSetting(key).toInt();
if (idx > 0) {
@ -21,4 +23,65 @@ template<typename T> void domoticzSend(const char * key, T nvalue) {
domoticzSend(key, nvalue, "");
}
void relayDomoticzSend(unsigned int relayID) {
char buffer[15];
sprintf(buffer, "dczRelayIdx%d", relayID);
domoticzSend(buffer, relayStatus(relayID) ? "1" : "0");
}
int relayFromIdx(unsigned int idx) {
for (int relayID=0; relayID<relayCount(); relayID++) {
if (relayToIdx(relayID) == idx) {
return relayID;
}
}
return -1;
}
int relayToIdx(unsigned int relayID) {
char buffer[15];
sprintf(buffer, "dczRelayIdx%d", relayID);
return getSetting(buffer).toInt();
}
void domoticzSetup() {
mqttRegister([](unsigned int type, const char * topic, const char * payload) {
String dczTopicOut = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC);
if (type == MQTT_CONNECT_EVENT) {
mqttSubscribeRaw(dczTopicOut.c_str());
}
if (type == MQTT_MESSAGE_EVENT) {
// Check topic
if (dczTopicOut.equals(topic)) {
// Parse response
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.parseObject((char *) payload);
if (!root.success()) {
DEBUG_MSG_P(PSTR("[DOMOTICZ] Error parsing data\n"));
return;
}
// IDX
unsigned long idx = root["idx"];
int relayID = relayFromIdx(idx);
if (relayID >= 0) {
unsigned long value = root["nvalue"];
DEBUG_MSG_P(PSTR("[DOMOTICZ] Received value %d for IDX %d\n"), value, idx);
relayStatus(relayID, value == 1);
}
}
}
});
}
#endif

+ 71
- 60
code/espurna/emon.ino View File

@ -25,8 +25,10 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
#define ADC121_REG_CONVH 0x07
EmonLiteESP emon;
double _current = 0;
unsigned int _power = 0;
bool _emonReady = false;
double _emonCurrent = 0;
unsigned int _emonPower = 0;
unsigned int _emonVoltage = 0;
// -----------------------------------------------------------------------------
// Provider
@ -54,28 +56,40 @@ unsigned int currentCallback() {
}
// -----------------------------------------------------------------------------
// EMON
// HAL
// -----------------------------------------------------------------------------
void setCurrentRatio(float value) {
emon.setCurrentRatio(value);
}
unsigned int getPower() {
return _power;
unsigned int getApparentPower() {
return int(getCurrent() * getVoltage());
}
double getCurrent() {
return _current;
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("emonMains", tmp);
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");
@ -98,7 +112,19 @@ void powerMonitorSetup() {
#endif
apiRegister(EMON_APOWER_TOPIC, EMON_APOWER_TOPIC, [](char * buffer, size_t len) {
snprintf(buffer, len, "%d", _power);
if (_emonReady) {
snprintf(buffer, len, "%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;
}
});
}
@ -119,80 +145,65 @@ void powerMonitorLoop() {
if (millis() > next_measurement) {
// Safety check: do not read current if relay is OFF
// You could be monitoring another line with the current clamp...
//if (!relayStatus(0)) {
// _current = 0;
//} else {
_current = emon.getCurrent(EMON_SAMPLES);
_current -= EMON_CURRENT_OFFSET;
if (_current < 0) _current = 0;
//}
if (measurements == 0) {
max = min = _current;
} else {
if (_current > max) max = _current;
if (_current < min) min = _current;
}
sum += _current;
++measurements;
int voltage = getVoltage();
float mainsVoltage = getSetting("emonMains", EMON_MAINS_VOLTAGE).toFloat();
{
char current[6];
dtostrf(_current, 5, 2, current);
DEBUG_MSG_P(PSTR("[ENERGY] Current: %sA\n"), current);
DEBUG_MSG_P(PSTR("[ENERGY] Power: %dW\n"), int(_current * mainsVoltage));
double current = getCurrent();
if (measurements == 0) {
max = min = current;
} else {
if (_emonCurrent > max) max = current;
if (_emonCurrent < min) min = current;
}
sum += current;
++measurements;
// Update websocket clients
char text[64];
sprintf_P(text, PSTR("{\"emonVisible\": 1, \"powApparentPower\": %d}"), int(_current * mainsVoltage));
wsSend(text);
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) and create C-string
double average = (sum - max - min) / (measurements - 2);
dtostrf(average, 5, 2, current);
char *c = current;
while ((unsigned char) *c == ' ') ++c;
// Calculate average apparent power from current and create C-string
_power = (int) (average * mainsVoltage);
char power[6];
snprintf(power, 6, "%d", _power);
// 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) and create C-string
double energy_inc = (double) _power * EMON_INTERVAL * EMON_MEASUREMENTS / 1000.0 / 3600.0;
char energy_buf[11];
dtostrf(energy_inc, 10, 3, energy_buf);
char *e = energy_buf;
while ((unsigned char) *e == ' ') ++e;
// 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(), power);
mqttSend(getSetting("emonCurrTopic", EMON_CURRENT_TOPIC).c_str(), c);
mqttSend(getSetting("emonEnergyTopic", EMON_ENERGY_TOPIC).c_str(), e);
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 ENABLE_DOMOTICZ
{
char buffer[20];
snprintf(buffer, 20, "%s;%s", power, e);
snprintf(buffer, 20, "%d;%s", _emonPower, String(energy_delta, 3).c_str());
domoticzSend("dczPowIdx", 0, buffer);
snprintf(buffer, 20, "%s", e);
snprintf(buffer, 20, "%s", String(energy_delta, 3).c_str());
domoticzSend("dczEnergyIdx", 0, buffer);
snprintf(buffer, 20, "%s", c);
snprintf(buffer, 20, "%s", String(_emonCurrent, 3).c_str());
domoticzSend("dczCurrentIdx", 0, buffer);
}
#endif
#if ENABLE_INFLUXDB
influxDBSend(getSetting("emonPowerTopic", EMON_APOWER_TOPIC).c_str(), power);
//influxDBSend(getSetting("emonCurrTopic", EMON_CURRENT_TOPIC).c_str(), c);
//influxDBSend(getSetting("emonEnergyTopic", EMON_ENERGY_TOPIC).c_str(), e);
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


+ 22
- 19
code/espurna/espurna.ino View File

@ -42,7 +42,7 @@ void heartbeat() {
unsigned long uptime_seconds = uptime_overflows * (UPTIME_OVERFLOW / 1000) + (last_uptime / 1000);
unsigned int free_heap = ESP.getFreeHeap();
DEBUG_MSG_P(PSTR("[MAIN] Time: %s\n"), (char *) NTP.getTimeDateString().c_str());
DEBUG_MSG_P(PSTR("[MAIN] Time: %s\n"), (char *) ntpDateTime().c_str());
if (!mqttConnected()) {
DEBUG_MSG_P(PSTR("[MAIN] Uptime: %ld seconds\n"), uptime_seconds);
DEBUG_MSG_P(PSTR("[MAIN] Free heap: %d bytes\n"), free_heap);
@ -52,54 +52,54 @@ void heartbeat() {
}
#if (MQTT_REPORT_INTERVAL)
#if (HEARTBEAT_REPORT_INTERVAL)
mqttSend(MQTT_TOPIC_INTERVAL, HEARTBEAT_INTERVAL / 1000);
#endif
#if (MQTT_REPORT_APP)
#if (HEARTBEAT_REPORT_APP)
mqttSend(MQTT_TOPIC_APP, APP_NAME);
#endif
#if (MQTT_REPORT_VERSION)
#if (HEARTBEAT_REPORT_VERSION)
mqttSend(MQTT_TOPIC_VERSION, APP_VERSION);
#endif
#if (MQTT_REPORT_HOSTNAME)
#if (HEARTBEAT_REPORT_HOSTNAME)
mqttSend(MQTT_TOPIC_HOSTNAME, getSetting("hostname").c_str());
#endif
#if (MQTT_REPORT_IP)
#if (HEARTBEAT_REPORT_IP)
mqttSend(MQTT_TOPIC_IP, getIP().c_str());
#endif
#if (MQTT_REPORT_MAC)
#if (HEARTBEAT_REPORT_MAC)
mqttSend(MQTT_TOPIC_MAC, WiFi.macAddress().c_str());
#endif
#if (MQTT_REPORT_RSSI)
#if (HEARTBEAT_REPORT_RSSI)
mqttSend(MQTT_TOPIC_RSSI, String(WiFi.RSSI()).c_str());
#endif
#if (MQTT_REPORT_UPTIME)
#if (HEARTBEAT_REPORT_UPTIME)
mqttSend(MQTT_TOPIC_UPTIME, String(uptime_seconds).c_str());
#if ENABLE_INFLUXDB
influxDBSend(MQTT_TOPIC_UPTIME, String(uptime_seconds).c_str());
#endif
#endif
#if (MQTT_REPORT_FREEHEAP)
#if (HEARTBEAT_REPORT_FREEHEAP)
mqttSend(MQTT_TOPIC_FREEHEAP, String(free_heap).c_str());
#if ENABLE_INFLUXDB
influxDBSend(MQTT_TOPIC_FREEHEAP, String(free_heap).c_str());
#endif
#endif
#if (MQTT_REPORT_RELAY)
#if (HEARTBEAT_REPORT_RELAY)
relayMQTT();
#endif
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
#if (MQTT_REPORT_COLOR)
#if (HEARTBEAT_REPORT_COLOR)
mqttSend(MQTT_TOPIC_COLOR, lightColor().c_str());
#endif
#endif
#if (MQTT_REPORT_VCC)
#if (HEARTBEAT_REPORT_VCC)
#if ENABLE_ADC_VCC
mqttSend(MQTT_TOPIC_VCC, String(ESP.getVcc()).c_str());
#endif
#endif
#if (MQTT_REPORT_STATUS)
mqttSend(MQTT_TOPIC_STATUS, MQTT_STATUS_ONLINE);
#if (HEARTBEAT_REPORT_STATUS)
mqttSend(MQTT_TOPIC_STATUS, MQTT_STATUS_ONLINE, true);
#endif
}
@ -226,8 +226,8 @@ void setup() {
#if ENABLE_INFLUXDB
influxDBSetup();
#endif
#if ENABLE_POW
powSetup();
#if ENABLE_HLW8012
hlw8012Setup();
#endif
#if ENABLE_DS18B20
dsSetup();
@ -244,6 +244,9 @@ void setup() {
#if ENABLE_EMON
powerMonitorSetup();
#endif
#if ENABLE_DOMOTICZ
domoticzSetup();
#endif
// Prepare configuration for version 2.0
hwUpwardsCompatibility();
@ -270,8 +273,8 @@ void loop() {
#if ENABLE_NOFUSS
nofussLoop();
#endif
#if ENABLE_POW
powLoop();
#if ENABLE_HLW8012
hlw8012Loop();
#endif
#if ENABLE_DS18B20
dsLoop();


code/espurna/pow.ino → code/espurna/hlw8012.ino View File

@ -7,15 +7,18 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
*/
#if ENABLE_POW
#if ENABLE_HLW8012
#include <HLW8012.h>
#include <Hash.h>
#include <ArduinoJson.h>
#include <EEPROM.h>
HLW8012 hlw8012;
bool _powEnabled = false;
bool _hlw8012Enabled = false;
bool _hlwReady = false;
int _hlwPower = 0;
double _hlwCurrent = 0;
int _hlwVoltage = 0;
// -----------------------------------------------------------------------------
// POW
@ -31,18 +34,18 @@ void ICACHE_RAM_ATTR hlw8012_cf_interrupt() {
hlw8012.cf_interrupt();
}
void powEnable(bool status) {
_powEnabled = status;
if (_powEnabled) {
#if POW_USE_INTERRUPTS == 1
attachInterrupt(POW_CF1_PIN, hlw8012_cf1_interrupt, CHANGE);
attachInterrupt(POW_CF_PIN, hlw8012_cf_interrupt, CHANGE);
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 POW_USE_INTERRUPTS == 1
detachInterrupt(POW_CF1_PIN);
detachInterrupt(POW_CF_PIN);
#if HLW8012_USE_INTERRUPTS == 1
detachInterrupt(HLW8012_CF1_PIN);
detachInterrupt(HLW8012_CF_PIN);
#endif
DEBUG_MSG_P(PSTR("[POW] Disabled\n"));
}
@ -50,13 +53,13 @@ void powEnable(bool status) {
// -----------------------------------------------------------------------------
void powSaveCalibration() {
void hlw8012SaveCalibration() {
setSetting("powPowerMult", hlw8012.getPowerMultiplier());
setSetting("powCurrentMult", hlw8012.getCurrentMultiplier());
setSetting("powVoltageMult", hlw8012.getVoltageMultiplier());
}
void powRetrieveCalibration() {
void hlw8012RetrieveCalibration() {
double value;
value = getSetting("powPowerMult", 0).toFloat();
if (value > 0) hlw8012.setPowerMultiplier((int) value);
@ -66,55 +69,57 @@ void powRetrieveCalibration() {
if (value > 0) hlw8012.setVoltageMultiplier((int) value);
}
void powSetExpectedActivePower(unsigned int power) {
void hlw8012SetExpectedActivePower(unsigned int power) {
if (power > 0) {
hlw8012.expectedActivePower(power);
powSaveCalibration();
hlw8012SaveCalibration();
}
}
void powSetExpectedCurrent(double current) {
void hlw8012SetExpectedCurrent(double current) {
if (current > 0) {
hlw8012.expectedCurrent(current);
powSaveCalibration();
hlw8012SaveCalibration();
}
}
void powSetExpectedVoltage(unsigned int voltage) {
void hlw8012SetExpectedVoltage(unsigned int voltage) {
if (voltage > 0) {
hlw8012.expectedVoltage(voltage);
powSaveCalibration();
hlw8012SaveCalibration();
}
}
void powReset() {
void hlw8012Reset() {
hlw8012.resetMultipliers();
powSaveCalibration();
hlw8012SaveCalibration();
}
// -----------------------------------------------------------------------------
// HAL
// -----------------------------------------------------------------------------
unsigned int getActivePower() {
unsigned int power = hlw8012.getActivePower();
if (POW_MIN_POWER > power || power > POW_MAX_POWER) power = 0;
if (HLW8012_MIN_POWER > power || power > HLW8012_MAX_POWER) power = 0;
return power;
}
unsigned int getApparentPower() {
unsigned int power = hlw8012.getApparentPower();
if (POW_MIN_POWER > power || power > POW_MAX_POWER) power = 0;
if (HLW8012_MIN_POWER > power || power > HLW8012_MAX_POWER) power = 0;
return power;
}
unsigned int getReactivePower() {
unsigned int power = hlw8012.getReactivePower();
if (POW_MIN_POWER > power || power > POW_MAX_POWER) power = 0;
if (HLW8012_MIN_POWER > power || power > HLW8012_MAX_POWER) power = 0;
return power;
}
double getCurrent() {
double current = hlw8012.getCurrent();
if (POW_MIN_CURRENT > current || current > POW_MAX_CURRENT) current = 0;
if (HLW8012_MIN_CURRENT > current || current > HLW8012_MAX_CURRENT) current = 0;
return current;
}
@ -128,7 +133,7 @@ double getPowerFactor() {
// -----------------------------------------------------------------------------
void powSetup() {
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);
@ -136,10 +141,10 @@ 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
#if POW_USE_INTERRUPTS
hlw8012.begin(POW_CF_PIN, POW_CF1_PIN, POW_SEL_PIN, POW_SEL_CURRENT, true);
#if HLW8012_USE_INTERRUPTS
hlw8012.begin(HLW8012_CF_PIN, HLW8012_CF1_PIN, HLW8012_SEL_PIN, HLW8012_SEL_CURRENT, true);
#else
hlw8012.begin(POW_CF_PIN, POW_CF1_PIN, POW_SEL_PIN, POW_SEL_CURRENT, false, 1000000);
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
@ -147,28 +152,40 @@ void powSetup() {
// * 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(POW_CURRENT_R, POW_VOLTAGE_R_UP, POW_VOLTAGE_R_DOWN);
hlw8012.setResistors(HLW8012_CURRENT_R, HLW8012_VOLTAGE_R_UP, HLW8012_VOLTAGE_R_DOWN);
// Retrieve calibration values
powRetrieveCalibration();
hlw8012RetrieveCalibration();
// API definitions
apiRegister(POW_POWER_TOPIC, POW_POWER_TOPIC, [](char * buffer, size_t len) {
snprintf(buffer, len, "%d", getActivePower());
apiRegister(HLW8012_POWER_TOPIC, HLW8012_POWER_TOPIC, [](char * buffer, size_t len) {
if (_hlwReady) {
snprintf(buffer, len, "%d", _hlwPower);
} else {
buffer = NULL;
}
});
apiRegister(POW_CURRENT_TOPIC, POW_CURRENT_TOPIC, [](char * buffer, size_t len) {
dtostrf(getCurrent(), len-1, 3, buffer);
apiRegister(HLW8012_CURRENT_TOPIC, HLW8012_CURRENT_TOPIC, [](char * buffer, size_t len) {
if (_hlwReady) {
dtostrf(_hlwCurrent, len-1, 3, buffer);
} else {
buffer = NULL;
}
});
apiRegister(POW_VOLTAGE_TOPIC, POW_VOLTAGE_TOPIC, [](char * buffer, size_t len) {
snprintf(buffer, len, "%d", getVoltage());
apiRegister(HLW8012_VOLTAGE_TOPIC, HLW8012_VOLTAGE_TOPIC, [](char * buffer, size_t len) {
if (_hlwReady) {
snprintf(buffer, len, "%d", _hlwVoltage);
} else {
buffer = NULL;
}
});
}
void powLoop() {
void hlw8012Loop() {
static unsigned long last_update = 0;
static unsigned char report_count = POW_REPORT_EVERY;
static unsigned char report_count = HLW8012_REPORT_EVERY;
static bool power_spike = false;
static unsigned long power_sum = 0;
@ -186,7 +203,7 @@ void powLoop() {
// POW is disabled while there is no internet connection
// When the HLW8012 measurements are enabled back we reset the timer
if (!_powEnabled) {
if (!_hlw8012Enabled) {
powWasEnabled = false;
return;
}
@ -195,16 +212,13 @@ void powLoop() {
powWasEnabled = true;
}
if (millis() - last_update > POW_UPDATE_INTERVAL) {
if (millis() - last_update > HLW8012_UPDATE_INTERVAL) {
last_update = millis();
unsigned int power = getActivePower();
unsigned int voltage = getVoltage();
double current = getCurrent();
unsigned int apparent = getApparentPower();
double factor = getPowerFactor();
unsigned int reactive = getReactivePower();
if (power > 0) {
power_spike = (power_previous == 0);
@ -230,75 +244,81 @@ void powLoop() {
}
voltage_previous = voltage;
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
if (wsConnected()) {
unsigned int apparent = getApparentPower();
double factor = getPowerFactor();
unsigned int reactive = getReactivePower();
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);
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
String output;
root.printTo(output);
wsSend(output.c_str());
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());
}
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) ? (double) power / apparent : 1;
if (factor > 1) factor = 1;
// Update globals
_hlwPower = power_sum / HLW8012_REPORT_EVERY;
_hlwCurrent = current_sum / HLW8012_REPORT_EVERY;
_hlwVoltage = voltage_sum / HLW8012_REPORT_EVERY;
_hlwReady = true;
// Calculate energy increment (ppower times time) and create C-string
double energy_inc = (double) power * POW_REPORT_EVERY * POW_UPDATE_INTERVAL / 1000.0 / 3600.0;
char energy_buf[11];
dtostrf(energy_inc, 11, 3, energy_buf);
char *e = energy_buf;
while ((unsigned char) *e == ' ') ++e;
// 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", POW_POWER_TOPIC).c_str(), String(power).c_str());
mqttSend(getSetting("powEnergyTopic", POW_ENERGY_TOPIC).c_str(), e);
mqttSend(getSetting("powCurrentTopic", POW_CURRENT_TOPIC).c_str(), String(current, 3).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, 2).c_str());
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 ENABLE_DOMOTICZ
{
char buffer[20];
snprintf(buffer, 20, "%d;%s", power, e);
snprintf(buffer, 20, "%d;%s", _hlwPower, String(energy_delta, 3).c_str());
domoticzSend("dczPowIdx", 0, buffer);
snprintf(buffer, 20, "%s", e);
snprintf(buffer, 20, "%s", String(energy_delta, 3).c_str());
domoticzSend("dczEnergyIdx", 0, buffer);
snprintf(buffer, 20, "%d", voltage);
snprintf(buffer, 20, "%d", _hlwVoltage);
domoticzSend("dczVoltIdx", 0, buffer);
snprintf(buffer, 20, "%s", String(current).c_str());
snprintf(buffer, 20, "%s", String(_hlwCurrent).c_str());
domoticzSend("dczCurrentIdx", 0, buffer);
}
#endif
#if ENABLE_INFLUXDB
influxDBSend(getSetting("powPowerTopic", POW_POWER_TOPIC).c_str(), String(power).c_str());
//influxDBSend(getSetting("powEnergyTopic", POW_ENERGY_TOPIC).c_str(), e);
//influxDBSend(getSetting("powCurrentTopic", POW_CURRENT_TOPIC).c_str(), String(current, 3).c_str());
//influxDBSend(getSetting("powVoltageTopic", POW_VOLTAGE_TOPIC).c_str(), String(voltage).c_str());
//influxDBSend(getSetting("powAPowerTopic", POW_APOWER_TOPIC).c_str(), String(apparent).c_str());
//influxDBSend(getSetting("powRPowerTopic", POW_RPOWER_TOPIC).c_str(), String(reactive).c_str());
//influxDBSend(getSetting("powPFactorTopic", POW_PFACTOR_TOPIC).c_str(), String(factor, 2).c_str());
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 = POW_REPORT_EVERY;
report_count = HLW8012_REPORT_EVERY;
}
@ -308,7 +328,7 @@ void powLoop() {
voltage_sum += voltage_previous;
// Toggle between current and voltage monitoring
#if POW_USE_INTERRUPTS == 0
#if HLW8012_USE_INTERRUPTS == 0
hlw8012.toggleMode();
#endif

+ 82
- 15
code/espurna/mqtt.ino View File

@ -7,7 +7,9 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
*/
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <vector>
#include <Ticker.h>
const char *mqtt_user = 0;
const char *mqtt_pass = 0;
@ -26,11 +28,19 @@ String mqttTopic;
bool _mqttForward;
char *_mqttUser = 0;
char *_mqttPass = 0;
char *_mqttWill;
std::vector<void (*)(unsigned int, const char *, const char *)> _mqtt_callbacks;
#if MQTT_SKIP_RETAINED
unsigned long mqttConnectedAt = 0;
#endif
typedef struct {
char * topic;
char * message;
} mqtt_message_t;
std::vector<mqtt_message_t> _mqtt_queue;
Ticker mqttFlushTicker;
// -----------------------------------------------------------------------------
// Public API
// -----------------------------------------------------------------------------
@ -71,31 +81,82 @@ String mqttSubtopic(char * topic) {
void mqttSendRaw(const char * topic, const char * message) {
if (mqtt.connected()) {
DEBUG_MSG_P(PSTR("[MQTT] Sending %s => %s\n"), topic, message);
#if MQTT_USE_ASYNC
mqtt.publish(topic, MQTT_QOS, MQTT_RETAIN, message);
unsigned int packetId = mqtt.publish(topic, MQTT_QOS, MQTT_RETAIN, message);
DEBUG_MSG_P(PSTR("[MQTT] Sending %s => %s (PID %d)\n"), topic, message, packetId);
#else
mqtt.publish(topic, message, MQTT_RETAIN);
DEBUG_MSG_P(PSTR("[MQTT] Sending %s => %s\n"), topic, message);
#endif
}
}
void _mqttFlush() {
if (_mqtt_queue.size() == 0) return;
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
for (unsigned char i=0; i<_mqtt_queue.size(); i++) {
mqtt_message_t element = _mqtt_queue[i];
root[element.topic] = element.message;
}
if (ntpConnected()) root[MQTT_TOPIC_TIME] = ntpDateTime();
root[MQTT_TOPIC_HOSTNAME] = getSetting("hostname", HOSTNAME);
root[MQTT_TOPIC_IP] = getIP();
String output;
root.printTo(output);
String path = mqttTopic + String(MQTT_TOPIC_JSON);
mqttSendRaw(path.c_str(), output.c_str());
for (unsigned char i = 0; i < _mqtt_queue.size(); i++) {
mqtt_message_t element = _mqtt_queue[i];
free(element.topic);
free(element.message);
}
_mqtt_queue.clear();
}
void mqttSend(const char * topic, const char * message, bool force) {
bool useJson = force ? false : getSetting("mqttUseJson", MQTT_USE_JSON).toInt() == 1;
if (useJson) {
mqtt_message_t element;
element.topic = strdup(topic);
element.message = strdup(message);
_mqtt_queue.push_back(element);
mqttFlushTicker.once_ms(MQTT_USE_JSON_DELAY, _mqttFlush);
} else {
String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER);
String path = mqttTopic + String(topic) + mqttGetter;
mqttSendRaw(path.c_str(), message);
}
}
void mqttSend(const char * topic, const char * message) {
String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER);
String path = mqttTopic + String(topic) + mqttGetter;
mqttSendRaw(path.c_str(), message);
mqttSend(topic, message, false);
}
void mqttSend(const char * topic, unsigned int index, const char * message, bool force) {
char buffer[strlen(topic)+5];
sprintf(buffer, "%s/%d", topic, index);
mqttSend(buffer, message, force);
}
void mqttSend(const char * topic, unsigned int index, const char * message) {
String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER);
String path = mqttTopic + String(topic) + String ("/") + String(index) + mqttGetter;;
mqttSendRaw(path.c_str(), message);
mqttSend(topic, index, message, false);
}
void mqttSubscribeRaw(const char * topic) {
if (mqtt.connected() && (strlen(topic) > 0)) {
DEBUG_MSG_P(PSTR("[MQTT] Subscribing to %s\n"), topic);
mqtt.subscribe(topic, MQTT_QOS);
#if MQTT_USE_ASYNC
unsigned int packetId = mqtt.subscribe(topic, MQTT_QOS);
DEBUG_MSG_P(PSTR("[MQTT] Subscribing to %s (PID %d)\n"), topic, packetId);
#else
mqtt.subscribe(topic, MQTT_QOS);
DEBUG_MSG_P(PSTR("[MQTT] Subscribing to %s\n"), topic);
#endif
}
}
@ -226,7 +287,8 @@ void mqttConnect() {
unsigned int port = getSetting("mqttPort", MQTT_PORT).toInt();
_mqttUser = strdup(getSetting("mqttUser").c_str());
_mqttPass = strdup(getSetting("mqttPassword").c_str());
char * will = strdup((mqttTopic + MQTT_TOPIC_STATUS).c_str());
if (_mqttWill) free(_mqttWill);
_mqttWill = strdup((mqttTopic + MQTT_TOPIC_STATUS).c_str());
DEBUG_MSG_P(PSTR("[MQTT] Connecting to broker at %s:%d"), host, port);
mqtt.setServer(host, port);
@ -234,7 +296,7 @@ void mqttConnect() {
#if MQTT_USE_ASYNC
mqtt.setKeepAlive(MQTT_KEEPALIVE).setCleanSession(false);
mqtt.setWill(will, MQTT_QOS, MQTT_RETAIN, "0");
mqtt.setWill(_mqttWill, MQTT_QOS, MQTT_RETAIN, "0");
if ((strlen(_mqttUser) > 0) && (strlen(_mqttPass) > 0)) {
DEBUG_MSG_P(PSTR(" as user '%s'."), _mqttUser);
mqtt.setCredentials(_mqttUser, _mqttPass);
@ -261,10 +323,10 @@ void mqttConnect() {
if ((strlen(_mqttUser) > 0) && (strlen(_mqttPass) > 0)) {
DEBUG_MSG_P(PSTR(" as user '%s'\n"), _mqttUser);
response = mqtt.connect(getIdentifier().c_str(), _mqttUser, _mqttPass, will, MQTT_QOS, MQTT_RETAIN, "0");
response = mqtt.connect(getIdentifier().c_str(), _mqttUser, _mqttPass, _mqttWill, MQTT_QOS, MQTT_RETAIN, "0");
} else {
DEBUG_MSG_P(PSTR("\n"));
response = mqtt.connect(getIdentifier().c_str(), will, MQTT_QOS, MQTT_RETAIN, "0");
response = mqtt.connect(getIdentifier().c_str(), _mqttWill, MQTT_QOS, MQTT_RETAIN, "0");
}
if (response) {
@ -277,7 +339,6 @@ void mqttConnect() {
#endif
free(host);
free(will);
String mqttSetter = getSetting("mqttSetter", MQTT_USE_SETTER);
String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER);
@ -316,6 +377,12 @@ void mqttSetup() {
mqtt.onMessage([](char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
_mqttOnMessage(topic, payload, len);
});
mqtt.onSubscribe([](uint16_t packetId, uint8_t qos) {
DEBUG_MSG_P(PSTR("[MQTT] Subscribe ACK for PID %d\n"), packetId);
});
mqtt.onPublish([](uint16_t packetId) {
DEBUG_MSG_P(PSTR("[MQTT] Publish ACK for PID %d\n"), packetId);
});
#else
mqtt.setCallback([](char* topic, byte* payload, unsigned int length) {
_mqttOnMessage(topic, (char *) payload, length);


+ 15
- 3
code/espurna/ntp.ino View File

@ -29,8 +29,21 @@ bool ntpConnected() {
return (timeStatus() == timeSet);
}
void ntpSetup() {
String ntpDateTime() {
if (!ntpConnected()) return String("Not set");
String value = NTP.getTimeDateString();
int hour = value.substring(0, 2).toInt();
int minute = value.substring(3, 5).toInt();
int second = value.substring(6, 8).toInt();
int day = value.substring(9, 11).toInt();
int month = value.substring(12, 14).toInt();
int year = value.substring(15, 19).toInt();
char buffer[20];
sprintf(buffer, "%04d/%02d/%02dT%02d:%02d:%02d", year, month, day, hour, minute, second);
return String(buffer);
}
void ntpSetup() {
NTP.onNTPSyncEvent([](NTPSyncEvent_t error) {
if (error) {
if (error == noResponse) {
@ -40,11 +53,10 @@ void ntpSetup() {
}
wsSend("{\"ntpStatus\": false}");
} else {
DEBUG_MSG_P(PSTR("[NTP] Time: %s\n"), (char *) NTP.getTimeDateString(NTP.getLastNTPSync()).c_str());
DEBUG_MSG_P(PSTR("[NTP] Time: %s\n"), (char *) ntpDateTime().c_str());
wsSend("{\"ntpStatus\": true}");
}
});
}
void ntpLoop() {


+ 61
- 114
code/espurna/relay.ino View File

@ -16,6 +16,8 @@ typedef struct {
unsigned char pin;
bool reverse;
unsigned char led;
unsigned long delay_on;
unsigned long delay_off;
unsigned int floodWindowStart;
unsigned char floodWindowChanges;
unsigned int scheduledStatusTime;
@ -81,22 +83,6 @@ bool relayProviderStatus(unsigned char id) {
// RELAY
// -----------------------------------------------------------------------------
String relayString() {
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
JsonArray& relay = root.createNestedArray("relayStatus");
for (unsigned char i=0; i<relayCount(); i++) {
relay.add(relayStatus(i));
}
String output;
root.printTo(output);
return output;
}
bool relayStatus(unsigned char id) {
return relayProviderStatus(id);
}
void relayPulse(unsigned char id) {
byte relayPulseMode = getSetting("relayPulseMode", RELAY_PULSE_MODE).toInt();
@ -158,23 +144,35 @@ bool relayStatus(unsigned char id, bool status, bool report) {
if (relayStatus(id) != status) {
unsigned int floodWindowEnd = _relays[id].floodWindowStart + 1000 * RELAY_FLOOD_WINDOW;
unsigned int currentTime = millis();
unsigned int floodWindowEnd = _relays[id].floodWindowStart + 1000 * RELAY_FLOOD_WINDOW;
unsigned long delay = status ? _relays[id].delay_on : _relays[id].delay_off;
_relays[id].floodWindowChanges++;
_relays[id].scheduledStatusTime = currentTime;
_relays[id].scheduledStatusTime = currentTime + delay;
// If currentTime is off-limits the floodWindow...
if (currentTime < _relays[id].floodWindowStart || floodWindowEnd <= currentTime) {
if (currentTime >= floodWindowEnd || currentTime < _relays[id].floodWindowStart) {
// We reset the floodWindow
_relays[id].floodWindowStart = currentTime;
_relays[id].floodWindowChanges = 1;
// If currentTime is in the floodWindow and there have been too many requests...
} else if (_relays[id].floodWindowChanges >= RELAY_FLOOD_CHANGES) {
_relays[id].scheduledStatusTime = floodWindowEnd;
// We schedule the changes to the end of the floodWindow
// unless it's already delayed beyond that point
if (floodWindowEnd - delay > currentTime) {
_relays[id].scheduledStatusTime = floodWindowEnd;
}
}
_relays[id].scheduledStatus = status;
_relays[id].scheduledReport = (report ? true : _relays[id].scheduledReport);
if (report) _relays[id].scheduledReport = true;
DEBUG_MSG_P(PSTR("[RELAY] Scheduled %d => %s in %u ms\n"),
DEBUG_MSG_P(PSTR("[RELAY] #%d scheduled %s in %u ms\n"),
id, status ? "ON" : "OFF",
(_relays[id].scheduledStatusTime - currentTime));
@ -189,6 +187,10 @@ bool relayStatus(unsigned char id, bool status) {
return relayStatus(id, status, true);
}
bool relayStatus(unsigned char id) {
return relayProviderStatus(id);
}
void relaySync(unsigned char id) {
if (_relays.size() > 1) {
@ -302,79 +304,17 @@ void relaySetupAPI() {
//------------------------------------------------------------------------------
void relayWS() {
String output = relayString();
wsSend(output.c_str());
}
//------------------------------------------------------------------------------
// Domoticz
//------------------------------------------------------------------------------
#if ENABLE_DOMOTICZ
void relayDomoticzSend(unsigned int relayID) {
char buffer[15];
sprintf(buffer, "dczRelayIdx%d", relayID);
domoticzSend(buffer, relayStatus(relayID) ? "1" : "0");
}
int relayFromIdx(unsigned int idx) {
for (int relayID=0; relayID<relayCount(); relayID++) {
if (relayToIdx(relayID) == idx) {
return relayID;
}
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
JsonArray& relay = root.createNestedArray("relayStatus");
for (unsigned char i=0; i<relayCount(); i++) {
relay.add(relayStatus(i));
}
return -1;
}
int relayToIdx(unsigned int relayID) {
char buffer[15];
sprintf(buffer, "dczRelayIdx%d", relayID);
return getSetting(buffer).toInt();
}
void relayDomoticzSetup() {
mqttRegister([](unsigned int type, const char * topic, const char * payload) {
String dczTopicOut = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC);
if (type == MQTT_CONNECT_EVENT) {
mqttSubscribeRaw(dczTopicOut.c_str());
}
if (type == MQTT_MESSAGE_EVENT) {
// Check topic
if (dczTopicOut.equals(topic)) {
// Parse response
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.parseObject((char *) payload);
if (!root.success()) {
DEBUG_MSG_P(PSTR("[DOMOTICZ] Error parsing data\n"));
return;
}
// IDX
unsigned long idx = root["idx"];
int relayID = relayFromIdx(idx);
if (relayID >= 0) {
unsigned long value = root["nvalue"];
DEBUG_MSG_P(PSTR("[DOMOTICZ] Received value %d for IDX %d\n"), value, idx);
relayStatus(relayID, value == 1);
}
}
}
});
String output;
root.printTo(output);
wsSend(output.c_str());
}
#endif
//------------------------------------------------------------------------------
// MQTT
//------------------------------------------------------------------------------
@ -384,15 +324,6 @@ void relayMQTT(unsigned char id) {
mqttSend(MQTT_TOPIC_RELAY, id, relayStatus(id) ? "1" : "0");
}
#if ENABLE_INFLUXDB
void relayInfluxDB(unsigned char id) {
if (id >= _relays.size()) return;
char buffer[10];
sprintf(buffer, "%s,id=%d", MQTT_TOPIC_RELAY, id);
influxDBSend(buffer, relayStatus(id) ? "1" : "0");
}
#endif
void relayMQTT() {
for (unsigned int i=0; i < _relays.size(); i++) {
relayMQTT(i);
@ -403,7 +334,7 @@ void relayMQTTCallback(unsigned int type, const char * topic, const char * paylo
if (type == MQTT_CONNECT_EVENT) {
#if not MQTT_REPORT_RELAY
#if not HEARTBEAT_REPORT_RELAY
relayMQTT();
#endif
@ -450,6 +381,19 @@ void relaySetupMQTT() {
mqttRegister(relayMQTTCallback);
}
//------------------------------------------------------------------------------
// InfluxDB
//------------------------------------------------------------------------------
#if ENABLE_INFLUXDB
void relayInfluxDB(unsigned char id) {
if (id >= _relays.size()) return;
char buffer[10];
sprintf(buffer, "%s,id=%d", MQTT_TOPIC_RELAY, id);
influxDBSend(buffer, relayStatus(id) ? "1" : "0");
}
#endif
//------------------------------------------------------------------------------
// Setup
//------------------------------------------------------------------------------
@ -459,27 +403,27 @@ void relaySetup() {
#if defined(SONOFF_DUAL)
// Two dummy relays for the dual
_relays.push_back((relay_t) {0, 0});
_relays.push_back((relay_t) {0, 0});
_relays.push_back((relay_t) {0, 0, 0, RELAY1_DELAY_ON, RELAY1_DELAY_OFF});
_relays.push_back((relay_t) {0, 0, 0, RELAY2_DELAY_ON, RELAY2_DELAY_OFF});
#elif defined(AI_LIGHT) | defined(LED_CONTROLLER) | defined(H801_LED_CONTROLLER)
// One dummy relay for the AI Thinker Light & Magic Home and H801 led controllers
_relays.push_back((relay_t) {0, 0});
_relays.push_back((relay_t) {0, 0, 0, RELAY1_DELAY_ON, RELAY1_DELAY_OFF});
#else
#ifdef RELAY1_PIN
_relays.push_back((relay_t) { RELAY1_PIN, RELAY1_PIN_INVERSE, RELAY1_LED });
_relays.push_back((relay_t) { RELAY1_PIN, RELAY1_PIN_INVERSE, RELAY1_LED, RELAY1_DELAY_ON, RELAY1_DELAY_OFF });
#endif
#ifdef RELAY2_PIN
_relays.push_back((relay_t) { RELAY2_PIN, RELAY2_PIN_INVERSE, RELAY2_LED });
_relays.push_back((relay_t) { RELAY2_PIN, RELAY2_PIN_INVERSE, RELAY2_LED, RELAY2_DELAY_ON, RELAY2_DELAY_OFF });
#endif
#ifdef RELAY3_PIN
_relays.push_back((relay_t) { RELAY3_PIN, RELAY3_PIN_INVERSE, RELAY3_LED });
_relays.push_back((relay_t) { RELAY3_PIN, RELAY3_PIN_INVERSE, RELAY3_LED, RELAY3_DELAY_ON, RELAY3_DELAY_OFF });
#endif
#ifdef RELAY4_PIN
_relays.push_back((relay_t) { RELAY4_PIN, RELAY4_PIN_INVERSE, RELAY4_LED });
_relays.push_back((relay_t) { RELAY4_PIN, RELAY4_PIN_INVERSE, RELAY4_LED, RELAY4_DELAY_ON, RELAY4_DELAY_OFF });
#endif
#endif
@ -496,9 +440,6 @@ void relaySetup() {
relaySetupAPI();
relaySetupMQTT();
#if ENABLE_DOMOTICZ
relayDomoticzSetup();
#endif
DEBUG_MSG_P(PSTR("[RELAY] Number of relays: %d\n"), _relays.size());
@ -515,15 +456,21 @@ void relayLoop(void) {
if (relayStatus(id) != status && currentTime >= _relays[id].scheduledStatusTime) {
DEBUG_MSG_P(PSTR("[RELAY] %d => %s\n"), id, status ? "ON" : "OFF");
DEBUG_MSG_P(PSTR("[RELAY] #%d set to %s\n"), id, status ? "ON" : "OFF");
// Call the provider to perform the action
relayProviderStatus(id, status);
// Change the binded LED if any
if (_relays[id].led > 0) {
ledStatus(_relays[id].led - 1, status);
}
if (_relays[id].scheduledReport) relayMQTT(id);
// Send MQTT report if requested
if (_relays[id].scheduledReport) {
relayMQTT(id);
}
if (!recursive) {
relayPulse(id);
relaySync(id);


+ 6
- 0
code/espurna/settings.ino View File

@ -94,6 +94,12 @@ void settingsSetup() {
e->response(Embedis::OK);
});
Embedis::command( F("RESTART"), [](Embedis* e) {
e->response(Embedis::OK);
customReset(CUSTOM_RESET_TERMINAL);
ESP.restart();
});
Embedis::command( F("RESET"), [](Embedis* e) {
e->response(Embedis::OK);
customReset(CUSTOM_RESET_TERMINAL);


+ 3034
- 62
code/espurna/static/index.html.gz.h
File diff suppressed because it is too large
View File


+ 13
- 0
code/espurna/utils.ino View File

@ -0,0 +1,13 @@
/*
UTILS MODULE
Copyright (C) 2017 by Xose Pérez <xose dot perez at gmail dot com>
*/
char * ltrim(char * s) {
char *p = s;
while ((unsigned char) *p == ' ') ++p;
return p;
}

+ 40
- 18
code/espurna/web.ino View File

@ -43,15 +43,17 @@ char _last_modified[50];
// WEBSOCKETS
// -----------------------------------------------------------------------------
bool wsConnected() {
return (ws.count() > 0);
}
bool wsSend(const char * payload) {
if (ws.count() > 0) {
DEBUG_MSG_P(PSTR("[WEBSOCKET] Broadcasting '%s'\n"), payload);
ws.textAll(payload);
}
}
bool wsSend(uint32_t client_id, const char * payload) {
DEBUG_MSG_P(PSTR("[WEBSOCKET] Sending '%s' to #%ld\n"), payload, client_id);
ws.text(client_id, payload);
}
@ -163,6 +165,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
bool changedNTP = false;
bool apiEnabled = false;
bool dstEnabled = false;
bool mqttUseJson = false;
#if ASYNC_TCP_SSL_ENABLED
bool mqttUseSSL = false;
#endif
@ -181,25 +184,25 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
// Skip firmware filename
if (key.equals("filename")) continue;
#if ENABLE_POW
#if ENABLE_HLW8012
if (key == "powExpectedPower") {
powSetExpectedActivePower(value.toInt());
hlw8012SetExpectedActivePower(value.toInt());
changed = true;
}
if (key == "powExpectedVoltage") {
powSetExpectedVoltage(value.toInt());
hlw8012SetExpectedVoltage(value.toInt());
changed = true;
}
if (key == "powExpectedCurrent") {
powSetExpectedCurrent(value.toFloat());
hlw8012SetExpectedCurrent(value.toFloat());
changed = true;
}
if (key == "powExpectedReset") {
powReset();
hlw8012Reset();
changed = true;
}
@ -259,6 +262,10 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
dstEnabled = true;
continue;
}
if (key == "mqttUseJson") {
mqttUseJson = true;
continue;
}
#if ASYNC_TCP_SSL_ENABLED
if (key == "mqttUseSSL") {
mqttUseSSL = true;
@ -313,6 +320,10 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
setSetting("ntpDST", dstEnabled);
save = changed = changedNTP = true;
}
if (mqttUseJson != (getSetting("mqttUseJson").toInt() == 1)) {
setSetting("mqttUseJson", mqttUseJson);
save = changed = true;
}
#if ASYNC_TCP_SSL_ENABLED
if (mqttUseSSL != (getSetting("mqttUseSSL", 0). toInt() == 1)) {
setSetting("mqttUseSSL", mqttUseSSL);
@ -452,6 +463,7 @@ void _wsStart(uint32_t client_id) {
root["mqttFP"] = getSetting("mqttFP");
#endif
root["mqttTopic"] = getSetting("mqttTopic", MQTT_TOPIC);
root["mqttUseJson"] = getSetting("mqttUseJson", MQTT_USE_JSON).toInt() == 1;
JsonArray& relay = root.createNestedArray("relayStatus");
for (unsigned char relayID=0; relayID<relayCount(); relayID++) {
@ -471,6 +483,8 @@ void _wsStart(uint32_t client_id) {
root["relaySync"] = getSetting("relaySync", RELAY_SYNC);
}
root["btnDelay"] = getSetting("btnDelay", BUTTON_DBLCLICK_DELAY).toInt();
root["webPort"] = getSetting("webPort", WEBSERVER_PORT).toInt();
root["apiEnabled"] = getSetting("apiEnabled").toInt() == 1;
@ -508,7 +522,7 @@ void _wsStart(uint32_t client_id) {
root["dczAnaIdx"] = getSetting("dczAnaIdx").toInt();
#endif
#if ENABLE_POW
#if ENABLE_HLW8012
root["dczPowIdx"] = getSetting("dczPowIdx").toInt();
root["dczEnergyIdx"] = getSetting("dczEnergyIdx").toInt();
root["dczVoltIdx"] = getSetting("dczVoltIdx").toInt();
@ -550,8 +564,9 @@ void _wsStart(uint32_t client_id) {
#if ENABLE_EMON
root["emonVisible"] = 1;
root["emonPower"] = getPower();
root["emonMains"] = getSetting("emonMains", EMON_MAINS_VOLTAGE);
root["emonApparentPower"] = getApparentPower();
root["emonCurrent"] = getCurrent();
root["emonVoltage"] = getVoltage();
root["emonRatio"] = getSetting("emonRatio", EMON_CURRENT_RATIO);
#endif
@ -560,7 +575,7 @@ void _wsStart(uint32_t client_id) {
root["analogValue"] = getAnalog();
#endif
#if ENABLE_POW
#if ENABLE_HLW8012
root["powVisible"] = 1;
root["powActivePower"] = getActivePower();
root["powApparentPower"] = getApparentPower();
@ -704,13 +719,13 @@ bool _asJson(AsyncWebServerRequest *request) {
ArRequestHandlerFunction _bindAPI(unsigned int apiID) {
return [apiID](AsyncWebServerRequest *request) {
webLogRequest(request);
webLogRequest(request);
if (!_authAPI(request)) return;
bool asJson = _asJson(request);
web_api_t api = _apis[apiID];
// Check if its a PUT
if (api.putFn != NULL) {
if (request->hasParam("value", request->method() == HTTP_PUT)) {
AsyncWebParameter* p = request->getParam("value", request->method() == HTTP_PUT);
@ -718,14 +733,21 @@ ArRequestHandlerFunction _bindAPI(unsigned int apiID) {
}
}
// Get response from callback
char value[10];
(api.getFn)(value, 10);
char *p = ltrim(value);
// jump over leading spaces
char *p = value;
while ((unsigned char) *p == ' ') ++p;
// The response will be a 404 NOT FOUND if the resource is not available
if (*value == NULL) {
DEBUG_MSG_P(PSTR("[API] Sending 404 response\n"));
request->send(404);
return;
}
DEBUG_MSG_P(PSTR("[API] Sending response '%s'\n"), p);
if (asJson) {
// Format response according to the Accept header
if (_asJson(request)) {
char buffer[64];
sprintf_P(buffer, PSTR("{ \"%s\": %s }"), api.key, p);
request->send(200, "application/json", buffer);


+ 6
- 5
code/espurna/wifi.ino View File

@ -27,8 +27,8 @@ String getNetwork() {
}
void wifiDisconnect() {
#if ENABLE_POW
powEnable(false);
#if ENABLE_HLW8012
hlw8012Enable(false);
#endif
jw.disconnect();
}
@ -51,6 +51,7 @@ void wifiConfigure() {
jw.setHostname(getSetting("hostname", HOSTNAME).c_str());
jw.setSoftAP(getSetting("hostname", HOSTNAME).c_str(), getSetting("adminPass", ADMIN_PASS).c_str());
jw.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
jw.setReconnectTimeout(WIFI_RECONNECT_INTERVAL);
jw.setAPMode(AP_MODE);
jw.cleanNetworks();
@ -196,12 +197,12 @@ void wifiSetup() {
}
// Manage POW
#if ENABLE_POW
#if ENABLE_HLW8012
if (code == MESSAGE_CONNECTED) {
powEnable(true);
hlw8012Enable(true);
}
if (code == MESSAGE_DISCONNECTED) {
powEnable(false);
hlw8012Enable(false);
}
#endif


+ 2
- 10
code/gulpfile.js View File

@ -2,7 +2,7 @@
ESP8266 file system builder
Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
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
@ -41,14 +41,6 @@ gulp.task('clean', function() {
return true;
});
gulp.task('files', ['clean'], function() {
return gulp.src([
'html/**/*.{jpg,jpeg,png,ico,gif}',
'html/fsversion'
])
.pipe(gulp.dest(dataFolder));
});
gulp.task('buildfs_embeded', ['buildfs_inline'], function() {
var source = dataFolder + 'index.html.gz';
@ -65,7 +57,7 @@ gulp.task('buildfs_embeded', ['buildfs_inline'], function() {
wstream.write('const uint8_t index_html_gz[] PROGMEM = {')
for (i=0; i<data.length; i++) {
if (i % 1000 == 0) wstream.write("\n");
if (i % 20 == 0) wstream.write("\n");
wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2));
if (i<data.length-1) wstream.write(',');
}


+ 6
- 2
code/html/custom.js View File

@ -444,7 +444,9 @@ function processData(data) {
} else if (element.attr('type') == 'radio') {
element.val([data[key]]);
} else {
element.val(data[key]);
var pre = element.attr("pre") || "";
var post = element.attr("post") || "";
element.val(pre + data[key] + post);
}
return;
}
@ -452,7 +454,9 @@ function processData(data) {
// Look for SPANs
var element = $("span[name=" + key + "]");
if (element.length > 0) {
element.html(data[key]);
var pre = element.attr("pre") || "";
var post = element.attr("post") || "";
element.html(pre + data[key] + post);
return;
}


+ 44
- 10
code/html/index.html View File

@ -179,12 +179,22 @@
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="dhtHum" readonly />
</div>
<div class="pure-g module module-emon">
<label class="pure-u-1 pure-u-sm-1-4" for="emonApparentPower">Apparent Power (VA)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="emonApparentPower" readonly />
</div>
<div class="pure-g module module-emon">
<label class="pure-u-1 pure-u-sm-1-4" for="emonCurrent">Current (A)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="emonCurrent" readonly />
</div>
<div class="pure-g module module-pow">
<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 module-emon">
<div class="pure-g module module-pow">
<label class="pure-u-1 pure-u-sm-1-4" for="powApparentPower">Apparent Power (VA)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="powApparentPower" readonly />
</div>
@ -292,7 +302,7 @@
<select name="relaySync" class="pure-u-3-4" tabindex="3">
<option value="0">No synchonisation</a>
<option value="1">Zero or one switches active</a>
<option value="2">One and just one switches active</a>
<option value="2">One and just one switch active</a>
<option value="3">All synchonised</a>
</select>
</div>
@ -320,15 +330,27 @@
<div class="pure-u-1 pure-u-md-3-4 hint">Pulse time in seconds (maximum value is 60s, accepts decimals).</div>
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4" for="btnDelay">Double click delay</label>
<input name="btnDelay" class="pure-u-1 pure-u-md-3-4" type="number" min="0" step="100" max="1000" tabindex="6" />
<div class="pure-u-0 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-md-3-4 hint">Delay in milliseconds to detect a double click (from 0 to 1000ms).<br />
The lower this number the faster the device will respond to button clicks but the harder it will be to get a double click.
Increase this number if you are having trouble to double click the button.
Set this value to 0 to disable double click. You won't be able to set the device in AP mode manually but your device will respond immediately to button clicks.<br />
You will have to <strong>reset the device</strong> after updating for this setting to apply.
</div>
</div>
<div class="pure-g module module-fauxmo">
<div class="pure-u-1 pure-u-sm-1-4"><label for="fauxmoEnabled">Alexa integration</label></div>
<div class="pure-u-1 pure-u-sm-1-4"><input type="checkbox" name="fauxmoEnabled" tabindex="6" /></div>
<div class="pure-u-1 pure-u-sm-1-4"><input type="checkbox" name="fauxmoEnabled" tabindex="7" /></div>
</div>
<div class="pure-g module module-ds module-dht">
<label class="pure-u-1 pure-u-sm-1-4" for="tmpUnits">Temperature units</label>
<div class="pure-u-1 pure-u-sm-1-4"><input type="radio" name="tmpUnits" tabindex="7" value="0"> Celsius (ºC)</input></div>
<div class="pure-u-1 pure-u-sm-1-4"><input type="radio" name="tmpUnits" tabindex="8" value="1"> Fahrenheit (ºF)</input></div>
<div class="pure-u-1 pure-u-sm-1-4"><input type="radio" name="tmpUnits" tabindex="8" value="0"> Celsius (ºC)</input></div>
<div class="pure-u-1 pure-u-sm-1-4"><input type="radio" name="tmpUnits" tabindex="9" value="1"> Fahrenheit (ºF)</input></div>
</div>
<div class="pure-g">
@ -481,15 +503,27 @@
<div class="pure-u-0 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-md-3-4 hint">
This is the root topic for this device. A trailing slash will be added if not preset. The {identifier} placeholder will be replaces by the device hostname.<br />
- <strong>&lt;root/&gt;relay/#</strong> Send a 0 or a 1 as a payload to this topic to switch it on or off. You can also send a 2 to toggle its current state. Replace # with the switch ID (starting from 0). If the board has only one switch it will be 0.<br />
<span class="module module-color">- <strong>&lt;root&gt;color</strong> The device will report the current color in #RRGGBB format to this topic. You can also set the color using this same topic.<br /></span>
- <strong>&lt;root/&gt;led/#</strong> Send a 0 or a 1 as a payload to this topic to set the onboard LED to the given state, send a 3 to turn it back to WIFI indicator. Replace # with the LED ID (starting from 0). If the board has only one LED it will be 0.<br />
- <strong>&lt;root/&gt;button/#</strong> For each button in the board subscribe to this topic to know when it is pressed (payload 1) or released (payload 0).<br />
- <strong>&lt;root/&gt;status</strong> The device will report a 1 to this topic every few minutes. Upon MQTT disconnecting this will be set to 0.<br />
- <strong>&lt;root&gt;/relay/#</strong> Send a 0 or a 1 as a payload to this topic to switch it on or off. You can also send a 2 to toggle its current state. Replace # with the switch ID (starting from 0). If the board has only one switch it will be 0.<br />
<span class="module module-color">- <strong>&lt;root&gt;/color</strong> The device will report the current color in #RRGGBB format to this topic. You can also set the color using this same topic.<br /></span>
- <strong>&lt;root&gt;/led/#</strong> Send a 0 or a 1 as a payload to this topic to set the onboard LED to the given state, send a 3 to turn it back to WIFI indicator. Replace # with the LED ID (starting from 0). If the board has only one LED it will be 0.<br />
- <strong>&lt;root&gt;/button/#</strong> For each button in the board subscribe to this topic to know when it is pressed (payload 1) or released (payload 0).<br />
- <strong>&lt;root&gt;/status</strong> The device will report a 1 to this topic every few minutes. Upon MQTT disconnecting this will be set to 0.<br />
- Other values reported (depending on the build) are: <strong>firmware</strong> and <strong>version</strong>, <strong>hostname</strong>, <strong>IP</strong>, <strong>MAC</strong>, signal strenth (<strong>RSSI</strong>), <strong>uptime</strong> (in seconds), <strong>free heap</strong> and <strong>power supply</strong>.
</div>
</div>
<div class="pure-g module module-fauxmo">
<div class="pure-u-1 pure-u-sm-1-4"><label for="mqttUseJson">Use JSON payload</label></div>
<div class="pure-u-1 pure-u-sm-3-4"><input type="checkbox" name="mqttUseJson" tabindex="26" /></div>
<div class="pure-u-1 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-md-3-4 hint">
All messages (except the device status) will be included in a JSON payload along with the timestamp and hostname
and sent under the <strong>&lt;root&gt;/data</strong> topic.<br />
Messages will be queued and sent after 100ms, so different messages could be merged into a single payload.<br />
Subscribtions will still be done to single topics.
</div>
</div>
</fieldset>
</div>


+ 0
- 11
code/pio_hooks.py View File

@ -1,11 +0,0 @@
#!/bin/python
import subprocess
import socket
from SCons.Script import DefaultEnvironment
env = DefaultEnvironment()
def before_build_spiffs(source, target, env):
env.Execute("node node_modules/gulp/bin/gulp.js")
env.AddPreAction(".pioenvs/%s/spiffs.bin" % env['PIOENV'], before_build_spiffs)

+ 62
- 78
code/platformio.ini View File

@ -5,15 +5,16 @@ data_dir = espurna/data
[common]
build_flags = -g -DDEBUG_PORT=Serial -DMQTT_MAX_PACKET_SIZE=400
build_flags_1m128 = ${common.build_flags} -Wl,-Tesp8266.flash.1m128.ld
build_flags_512k = ${common.build_flags} -Wl,-Tesp8266.flash.512k0.ld
build_flags_1m = ${common.build_flags} -Wl,-Tesp8266.flash.1m0.ld
lib_deps =
DHT sensor library
Adafruit Unified Sensor
https://github.com/xoseperez/Time
ArduinoJson
https://github.com/me-no-dev/ESPAsyncTCP#36b6b5a
https://github.com/me-no-dev/ESPAsyncWebServer#bab5457
https://github.com/marvinroger/async-mqtt-client#ddbf4d1
https://github.com/me-no-dev/ESPAsyncTCP#991f855
https://github.com/me-no-dev/ESPAsyncWebServer#a94265d
https://github.com/marvinroger/async-mqtt-client#v0.8.1
PubSubClient
Embedis
NtpClientLib
@ -25,7 +26,7 @@ lib_deps =
https://bitbucket.org/xoseperez/fauxmoesp.git#2.1.0
https://bitbucket.org/xoseperez/nofuss.git#0.2.2
https://bitbucket.org/xoseperez/emonliteesp.git#0.1.2
https://bitbucket.org/xoseperez/debounceevent.git#2.0.0
https://bitbucket.org/xoseperez/debounceevent.git#2.0.1
https://github.com/xoseperez/my9291#1.0.0
https://github.com/xoseperez/RemoteSwitch-arduino-library.git
lib_ignore =
@ -37,7 +38,6 @@ framework = arduino
board = d1_mini
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags} -DD1_MINI -DENABLE_DS18B20=1 -DDS_PIN=14 -DENABLE_ADC_VCC=0 -DMQTT_USE_ASYNC=0 -DENABLE_ANALOG=1 -DNOWSAUTH
[env:d1-debug]
@ -46,7 +46,6 @@ framework = arduino
board = d1_mini
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags} -DD1_RELAYSHIELD -DDEBUG_FAUXMO=Serial -DNOWSAUTH
[env:d1-ssl-debug]
@ -64,7 +63,6 @@ framework = arduino
board = d1_mini
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags} -DD1_RELAYSHIELD -DDEBUG_FAUXMO=Serial -DNOWSAUTH
upload_speed = 115200
upload_port = "192.168.4.1"
@ -76,7 +74,6 @@ framework = arduino
board = nodemcuv2
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags} -DNODEMCUV2 -DDEBUG_FAUXMO=Serial -DNOWSAUTH
[env:node-debug-ssl]
@ -94,7 +91,6 @@ framework = arduino
board = nodemcuv2
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags} -DNODEMCUV2 -DDEBUG_FAUXMO=Serial -DNOWSAUTH
upload_speed = 115200
upload_port = "192.168.4.1"
@ -106,7 +102,6 @@ framework = arduino
board = esp12e
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags} -DESPURNA_H
[env:espurna-debug-ota]
@ -115,7 +110,6 @@ framework = arduino
board = esp12e
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags} -DESPURNA_H
upload_speed = 115200
upload_port = "192.168.4.1"
@ -125,19 +119,19 @@ upload_flags = --auth=fibonacci --port 8266
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DSONOFF
build_flags = ${common.build_flags_1m} -DSONOFF
[env:sonoff-debug-ota]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DSONOFF
build_flags = ${common.build_flags_1m} -DSONOFF
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
@ -146,37 +140,37 @@ upload_flags = --auth=fibonacci --port 8266
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DSONOFF -DENABLE_DHT=1
build_flags = ${common.build_flags_1m} -DSONOFF -DENABLE_DHT=1
[env:sonoff-ds18b20-debug]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DSONOFF -DENABLE_DS18B20=1
build_flags = ${common.build_flags_1m} -DSONOFF -DENABLE_DS18B20=1
[env:sonoff-pow-debug]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DSONOFF_POW
build_flags = ${common.build_flags_1m} -DSONOFF_POW
[env:sonoff-pow-debug-ota]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DSONOFF_POW
build_flags = ${common.build_flags_1m} -DSONOFF_POW
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
@ -185,19 +179,19 @@ upload_flags = --auth=fibonacci --port 8266
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DSONOFF_DUAL
build_flags = ${common.build_flags_1m} -DSONOFF_DUAL
[env:sonoff-dual-debug-ota]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DSONOFF_DUAL
build_flags = ${common.build_flags_1m} -DSONOFF_DUAL
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
@ -206,19 +200,19 @@ upload_flags = --auth=fibonacci --port 8266
platform = espressif8266
framework = arduino
board = esp8285
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DSONOFF_4CH
build_flags = ${common.build_flags_1m} -DSONOFF_4CH
[env:sonoff-4ch-debug-ota]
platform = espressif8266
framework = arduino
board = esp8285
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DSONOFF_4CH
build_flags = ${common.build_flags_1m} -DSONOFF_4CH
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
@ -227,19 +221,19 @@ upload_flags = --auth=fibonacci --port 8266
platform = espressif8266
framework = arduino
board = esp8285
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DSONOFF_TOUCH
build_flags = ${common.build_flags_1m} -DSONOFF_TOUCH
[env:sonoff-touch-debug-ota]
platform = espressif8266
framework = arduino
board = esp8285
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DSONOFF_TOUCH
build_flags = ${common.build_flags_1m} -DSONOFF_TOUCH
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
@ -248,19 +242,19 @@ upload_flags = --auth=fibonacci --port 8266
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DSLAMPHER
build_flags = ${common.build_flags_1m} -DSLAMPHER
[env:slampher-debug-ota]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DSLAMPHER
build_flags = ${common.build_flags_1m} -DSLAMPHER
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
@ -269,19 +263,19 @@ upload_flags = --auth=fibonacci --port 8266
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DS20
build_flags = ${common.build_flags_1m} -DS20
[env:s20-debug-ota]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DS20
build_flags = ${common.build_flags_1m} -DS20
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
@ -290,19 +284,19 @@ upload_flags = --auth=fibonacci --port 8266
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DITEAD_1CH_INCHING
build_flags = ${common.build_flags_1m} -DITEAD_1CH_INCHING
[env:1ch-inching-debug-ota]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DITEAD_1CH_INCHING
build_flags = ${common.build_flags_1m} -DITEAD_1CH_INCHING
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
@ -311,19 +305,19 @@ upload_flags = --auth=fibonacci --port 8266
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DITEAD_MOTOR
build_flags = ${common.build_flags_1m} -DITEAD_MOTOR
[env:motor-debug-ota]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DITEAD_MOTOR
build_flags = ${common.build_flags_1m} -DITEAD_MOTOR
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
@ -334,7 +328,6 @@ framework = arduino
board = esp12e
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags} -DESP_RELAY_BOARD -DENABLE_DHT=1
[env:electrodragon-debug-ota]
@ -343,7 +336,6 @@ framework = arduino
board = esp12e
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags} -DESP_RELAY_BOARD -DENABLE_DHT=1
upload_speed = 115200
upload_port = "192.168.4.1"
@ -353,19 +345,19 @@ upload_flags = --auth=fibonacci --port 8266
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DECOPLUG
build_flags = ${common.build_flags_1m} -DECOPLUG
[env:ecoplug-debug-ota]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DECOPLUG
build_flags = ${common.build_flags_1m} -DECOPLUG
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
@ -376,7 +368,6 @@ framework = arduino
board = esp12e
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags} -DWIFI_RELAY_NC
[env:jangoe-debug-ota]
@ -385,7 +376,6 @@ framework = arduino
board = esp12e
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags} -DWIFI_RELAY_NC
upload_speed = 115200
upload_port = "192.168.4.1"
@ -397,7 +387,6 @@ framework = arduino
board = esp_wroom_02
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags} -DMQTT_RELAY -DENABLE_DS18B20=1
[env:mqtt-relay-debug-ota]
@ -406,7 +395,6 @@ framework = arduino
board = esp_wroom_02
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags} -DMQTT_RELAY -DENABLE_DS18B20=1
upload_speed = 115200
upload_port = "192.168.4.1"
@ -418,8 +406,7 @@ framework = arduino
board = esp01_1m
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DWIFI_RELAYS_BOARD_KIT
build_flags = ${common.build_flags_1m} -DWIFI_RELAYS_BOARD_KIT
[env:wifi-relays-debug-ota]
platform = espressif8266
@ -427,8 +414,7 @@ framework = arduino
board = esp01_1m
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DWIFI_RELAYS_BOARD_KIT
build_flags = ${common.build_flags_1m} -DWIFI_RELAYS_BOARD_KIT
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
@ -436,20 +422,20 @@ upload_flags = --auth=fibonacci --port 8266
[env:ai-light-debug]
platform = espressif8266
framework = arduino
board = esp8285
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DAI_LIGHT
build_flags = ${common.build_flags_1m} -DAI_LIGHT
[env:ai-light-debug-ota]
platform = espressif8266
framework = arduino
board = esp8285
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DAI_LIGHT
build_flags = ${common.build_flags_1m} -DAI_LIGHT
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
@ -460,8 +446,7 @@ framework = arduino
board = esp01_1m
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DLED_CONTROLLER
build_flags = ${common.build_flags_1m} -DLED_CONTROLLER
[env:led-controller-debug-ota]
platform = espressif8266
@ -469,8 +454,7 @@ framework = arduino
board = esp01_1m
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = ${common.build_flags_1m128} -DLED_CONTROLLER
build_flags = ${common.build_flags_1m} -DLED_CONTROLLER
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
@ -479,18 +463,18 @@ upload_flags = --auth=fibonacci --port 8266
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = -g -Wl,-Tesp8266.flash.1m128.ld -DH801_LED_CONTROLLER -DDEBUG_PORT=Serial1
[env:h801-debug-ota]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = -g -Wl,-Tesp8266.flash.1m128.ld -DH801_LED_CONTROLLER -DDEBUG_PORT=Serial1
upload_speed = 115200
upload_port = "192.168.4.1"


Loading…
Cancel
Save