Browse Source

Merge branch 'dev' into ssl

fastled
Xose Pérez 7 years ago
parent
commit
d304765e1d
39 changed files with 1712 additions and 337 deletions
  1. +70
    -0
      CHANGELOG.md
  2. +10
    -3
      README.md
  3. +1
    -5
      code/build-all
  4. +64
    -0
      code/espurna/analog.ino
  5. +20
    -4
      code/espurna/button.ino
  6. +6
    -15
      code/espurna/config/arduino.h
  7. +91
    -19
      code/espurna/config/general.h
  8. +60
    -4
      code/espurna/config/hardware.h
  9. +3
    -0
      code/espurna/config/prototypes.h
  10. +37
    -14
      code/espurna/config/sensors.h
  11. +1
    -1
      code/espurna/config/version.h
  12. BIN
      code/espurna/data/index.html.gz
  13. +17
    -4
      code/espurna/debug.ino
  14. +7
    -2
      code/espurna/dht.ino
  15. +5
    -1
      code/espurna/ds18b20.ino
  16. +7
    -1
      code/espurna/emon.ino
  17. +64
    -3
      code/espurna/espurna.ino
  18. +283
    -0
      code/espurna/hardware.ino
  19. +62
    -0
      code/espurna/influxdb.ino
  20. +179
    -47
      code/espurna/light.ino
  21. +8
    -4
      code/espurna/mqtt.ino
  22. +1
    -1
      code/espurna/nofuss.ino
  23. +13
    -1
      code/espurna/ntp.ino
  24. +1
    -0
      code/espurna/ota.ino
  25. +74
    -22
      code/espurna/pow.ino
  26. +93
    -32
      code/espurna/relay.ino
  27. +27
    -0
      code/espurna/settings.ino
  28. +61
    -60
      code/espurna/static/index.html.gz.h
  29. +104
    -8
      code/espurna/web.ino
  30. +2
    -1
      code/espurna/wifi.ino
  31. +4
    -21
      code/gulpfile.js
  32. +33
    -2
      code/html/custom.css
  33. +81
    -0
      code/html/custom.js
  34. +165
    -51
      code/html/index.html
  35. +3
    -6
      code/package.json
  36. +55
    -5
      code/platformio.ini
  37. BIN
      images/devices/h801.jpg
  38. BIN
      images/devices/magic-home-led-controller.jpg
  39. BIN
      images/devices/tinkerman-espurna-h.jpg

+ 70
- 0
CHANGELOG.md View File

@ -3,6 +3,76 @@
The format is based on [Keep a Changelog](http://keepachangelog.com/) The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/). and this project adheres to [Semantic Versioning](http://semver.org/).
## [1.8.2] 2017-07-16
### Added
- InfluxDB support via HTTP API
- Added custom reset reason to debug log
- Enable WIFI debug on hardware reset (button long click)
### Changed
- Issue #159. Allow decimals in relay pulse interval
- Updated HLW8012 library
### Fix
- Issue #148. Fix bug in conditional compilation check
- Issue #149. Using different pulse counters for each relay (thanks to Lauris Ieviņš)
- Issue #141. Limit relay pulse interval to 60s
- Fixed units for apparent & reactive power (thanks to Lauris Ieviņš)
- Fixed mDNS setup when using custom HTTP port for web interface
## [1.8.1] 2017-05-22
### Fix
- Issue #140. Fix no relay control bug in Sonoff Dual
## [1.8.0] 2017-05-21
### Added
- Added gamma correction to RGB strips. Thanks to Chris Ward.
- Added support for Huacanxing H801 WiFi LED Controller. Thanks to Minh Phuong Ly.
- Issue #138. Added NTP configuration from web interface
- Issue #128. Report color when booting and in heartbeat stream.
- Issue #126. Show NTP status in web interface.
- Added filter limits on POW readings.
- Added color temperature to RGB calculation. Thanks to Sacha Telgenhof.
- Issue #120. Added relay flood protection. Thanks to Izik Dubnov.
- Support for "#RRGGBB", "RRR,GGG,BBB" and "WWW" color formats.
- Issue #117. Added build date & time to web interface.
### Fix
- Fix MQTT_RELAY board conifugration. Thanks to Denis French.
- Issue #125. Fix bug in relay status reading from EEPROM
- Issue #127. Fix button action in DUAL.
- Fix bug in Sonoff POW current reading. Thanks to Emmanuel Tatto.
- Minimizing my9291 flickering when booting.
- Fix conditional flags in hardware.ino to support Arduino IDE.
## [1.7.1] 2017-03-28
### Fix
- Issue #113. Fix restoring color from EEPROM upon reboot
- Issue #113. Fix bug in API handlers
## [1.7.0] 2017-03-27
### Added
- Web interface embedded in firmware image by default
- Upload firmware image from web interface
- Added API entry point to change light color
- Added generic analog sensor. Thanks to Francesco Boscarino
- Report RSSI value in debug console and MQTT status messages
- Added support for Magic Home LED Controller
- Added support for ESPurna-H Board (based on HLW8012)
- Added forward compatible code for v2.0
### Changed
- Added ellipsis (...) in debug messages longer than 80 characters
- Changed topic constants in code
- Prevent the SDK from saving WiFi configuration to flash
### Fix
- Issue #113. Fix light bulb state to OFF in library prevented the bulb from turning on
- Issue #58. Added code to handle spurious readings
- Fix bug in HLW8012 calibration current parameter casting to int instead of float
- Issue #115. Removed local declaration of _mqttForward variable. Thanks to Paweł Fiedor
- Fix MQTT will topic. Thanks to Asbjorn Tronhus
## [1.6.9] 2017-03-12 ## [1.6.9] 2017-03-12
### Added ### Added
- Two stage read for DS18B20 devices. Thanks to Izik Dubnov. - Two stage read for DS18B20 devices. Thanks to Izik Dubnov.


+ 10
- 3
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 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. It uses the Arduino Core for ESP8266 framework and a number of 3rd party libraries.
**Current Release Version is 1.6.9**, read the [changelog](https://bitbucket.org/xoseperez/espurna/src/master/CHANGELOG.md).
**Current Release Version is 1.8.2**, read the [changelog](https://bitbucket.org/xoseperez/espurna/src/master/CHANGELOG.md).
## Features ## Features
@ -28,6 +28,7 @@ It uses the Arduino Core for ESP8266 framework and a number of 3rd party librari
* **Alexa** integration using the [FauxmoESP Library](https://bitbucket.org/xoseperez/fauxmoesp) * **Alexa** integration using the [FauxmoESP Library](https://bitbucket.org/xoseperez/fauxmoesp)
* [**Domoticz**](https://domoticz.com/) integration via MQTT * [**Domoticz**](https://domoticz.com/) integration via MQTT
* [**Home Assistant**](https://home-assistant.io/) integration via MQTT * [**Home Assistant**](https://home-assistant.io/) integration via MQTT
* [**InfluxDB**](https://www.influxdata.com/) integration via HTTP API
* Support for different **sensors** * Support for different **sensors**
* DHT11 / DHT22 / DHT21 / AM2301 (supports celsius & fahrenheit reporting) * DHT11 / DHT22 / DHT21 / AM2301 (supports celsius & fahrenheit reporting)
* DS18B20 (supports celsius & fahrenheit reporting) * DS18B20 (supports celsius & fahrenheit reporting)
@ -40,8 +41,10 @@ It uses the Arduino Core for ESP8266 framework and a number of 3rd party librari
* Relay switching and sensor data from the web interface * Relay switching and sensor data from the web interface
* Websockets-based communication between the device and the browser * Websockets-based communication between the device and the browser
* Backup and restore settings option * Backup and restore settings option
* Upgrade firmware from the web interface
* **REST API** (enable/disable from web interface) * **REST API** (enable/disable from web interface)
* GET and PUT relay status * GET and PUT relay status
* Change light color (for supported hardware)
* GET sensor data (power, current, voltage, temperature and humidity) depending on the available hardware * GET sensor data (power, current, voltage, temperature and humidity) depending on the available hardware
* **RPC API** (enable/disable from web interface) * **RPC API** (enable/disable from web interface)
* Remote reset the board * Remote reset the board
@ -65,10 +68,14 @@ Here is the list of supported hardware. For more information please refer to the
|||| ||||
|-|-|-| |-|-|-|
|![Tinkerman Espurna H](images/devices/tinkerman-espurna-h.jpg)|||
|**Tinkerman ESPurna H**|||
|![IteadStudio S20](images/devices/s20.jpg)|![WorkChoice EcoPlug](images/devices/workchoice-ecoplug.jpg)|![IteadStudio Sonoff Touch](images/devices/sonoff-touch.jpg)| |![IteadStudio S20](images/devices/s20.jpg)|![WorkChoice EcoPlug](images/devices/workchoice-ecoplug.jpg)|![IteadStudio Sonoff Touch](images/devices/sonoff-touch.jpg)|
|**IteadStudio S20**|**WorkChoice EcoPlug**|**IteadStudio Sonoff Touch**| |**IteadStudio S20**|**WorkChoice EcoPlug**|**IteadStudio Sonoff Touch**|
|![IteadStudio Slampher](images/devices/slampher.jpg)|![AI-Thinker Wifi Light / Noduino OpenLight](images/devices/aithinker-ailight.jpg)||
|**IteadStudio Slampher**|**AI-Thinker Wifi Light / Noduino OpenLight**||
|![IteadStudio Slampher](images/devices/slampher.jpg)|![AI-Thinker Wifi Light / Noduino OpenLight](images/devices/aithinker-ailight.jpg)|![Magic Home LED Controller](images/devices/magic-home-led-controller.jpg)|
|**IteadStudio Slampher**|**AI-Thinker Wifi Light / Noduino OpenLight**|**Magic Home LED Controller**|
|![Huacanxing H801](images/devices/h801.jpg)|||
|**Huacanxing H801**|||
|![IteadStudio Sonoff Basic](images/devices/sonoff-basic.jpg)|![IteadStudio Sonoff RF](images/devices/sonoff-rf.jpg)|![Electrodragon Relay Board](images/devices/electrodragon-relay-board.jpg)| |![IteadStudio Sonoff Basic](images/devices/sonoff-basic.jpg)|![IteadStudio Sonoff RF](images/devices/sonoff-rf.jpg)|![Electrodragon Relay Board](images/devices/electrodragon-relay-board.jpg)|
|**IteadStudio Sonoff Basic**|**IteadStudio Sonoff RF**|**Electrodragon Relay Board**| |**IteadStudio Sonoff Basic**|**IteadStudio Sonoff RF**|**Electrodragon Relay Board**|
|![IteadStudio Sonoff Dual](images/devices/sonoff-dual.jpg)|![IteadStudio Sonoff POW](images/devices/sonoff-pow.jpg)|![IteadStudio Sonoff TH10/TH16](images/devices/sonoff-th10-th16.jpg)| |![IteadStudio Sonoff Dual](images/devices/sonoff-dual.jpg)|![IteadStudio Sonoff POW](images/devices/sonoff-pow.jpg)|![IteadStudio Sonoff TH10/TH16](images/devices/sonoff-th10-th16.jpg)|


+ 1
- 5
code/build-all View File

@ -1,11 +1,10 @@
#!/bin/bash #!/bin/bash
# Environments to build # Environments to build
ENVIRONMENTS="sonoff-debug sonoff-dht22-debug sonoff-ds18b20-debug sonoff-pow-debug sonoff-dual-debug sonoff-4ch-debug 1ch-inching-debug electrodragon-debug ecoplug-debug jangoe-debug ai-light-debug led-controller-debug"
ENVIRONMENTS="espurna-debug sonoff-debug sonoff-dht22-debug sonoff-ds18b20-debug sonoff-pow-debug sonoff-dual-debug sonoff-4ch-debug 1ch-inching-debug electrodragon-debug ecoplug-debug jangoe-debug ai-light-debug led-controller-debug h801-debug sonoff-touch-debug"
# Get current version # Get current version
version=`cat espurna/config/version.h | grep APP_VERSION | awk '{print $3}' | sed 's/"//g'` version=`cat espurna/config/version.h | grep APP_VERSION | awk '{print $3}' | sed 's/"//g'`
echo $version
# Create output folder # Create output folder
mkdir -p firmware mkdir -p firmware
@ -15,6 +14,3 @@ for environment in $ENVIRONMENTS; do
platformio run -e $environment platformio run -e $environment
mv .pioenvs/$environment/firmware.bin firmware/espurna-$version-$environment.bin mv .pioenvs/$environment/firmware.bin firmware/espurna-$version-$environment.bin
done done
platformio run -vv -t uploadfs -e node-debug
mv .pioenvs/node-debug/spiffs.bin firmware/espurna-$version-spiffs.bin

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

@ -0,0 +1,64 @@
/*
ANALOG MODULE
Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
*/
#if ENABLE_ANALOG
int _analog = 0;
// -----------------------------------------------------------------------------
// ANALOG
// -----------------------------------------------------------------------------
unsigned int getAnalog() {
return analogRead(ANALOG_PIN);
}
void analogSetup() {
pinMode(ANALOG_PIN, INPUT);
apiRegister(ANALOG_TOPIC, ANALOG_TOPIC, [](char * buffer, size_t len) {
snprintf(buffer, len, "%d", getAnalog());
});
}
void analogLoop() {
// Check if we should read new data
static unsigned long last_update = 0;
if ((millis() - last_update > ANALOG_UPDATE_INTERVAL) || (last_update == 0)) {
last_update = millis();
unsigned int analog = getAnalog();
DEBUG_MSG_P(PSTR("[ANALOG] Value: %d\n"), analog);
// Send MQTT messages
mqttSend(getSetting("analogTopic", ANALOG_TOPIC).c_str(), String(analog).c_str());
// Send to Domoticz
#if ENABLE_DOMOTICZ
domoticzSend("dczAnaIdx", 0, String(analog).c_str());
#endif
// Send to InfluxDB
#if ENABLE_INFLUXDB
influxDBSend(MQTT_TOPIC_ANALOG, analog);
#endif
// Update websocket clients
char buffer[100];
sprintf_P(buffer, PSTR("{\"analogVisible\": 1, \"analogValue\": %d}"), analog);
wsSend(buffer);
}
}
#endif

+ 20
- 4
code/espurna/button.ino View File

@ -30,6 +30,18 @@ void buttonMQTT(unsigned char id, uint8_t event) {
} }
#endif #endif
int buttonFromRelay(unsigned int relayID) {
for (unsigned int i=0; i < _buttons.size(); i++) {
if (_buttons[i].relayID == relayID) return i;
}
return -1;
}
bool buttonState(unsigned char id) {
if (id >= _buttons.size()) return false;
return _buttons[id].button->pressed();
}
unsigned char buttonAction(unsigned char id, unsigned char event) { unsigned char buttonAction(unsigned char id, unsigned char event) {
if (id >= _buttons.size()) return BUTTON_MODE_NONE; if (id >= _buttons.size()) return BUTTON_MODE_NONE;
unsigned long actions = _buttons[id].actions; unsigned long actions = _buttons[id].actions;
@ -81,11 +93,15 @@ void buttonEvent(unsigned int id, unsigned char event) {
} }
} }
if (action == BUTTON_MODE_AP) createAP(); if (action == BUTTON_MODE_AP) createAP();
if (action == BUTTON_MODE_RESET) ESP.restart();
if (action == BUTTON_MODE_RESET) {
customReset(CUSTOM_RESET_HARDWARE);
ESP.restart();
}
if (action == BUTTON_MODE_PULSE) relayPulseToggle(); if (action == BUTTON_MODE_PULSE) relayPulseToggle();
if (action == BUTTON_MODE_FACTORY) { if (action == BUTTON_MODE_FACTORY) {
DEBUG_MSG_P(PSTR("\n\nFACTORY RESET\n\n")); DEBUG_MSG_P(PSTR("\n\nFACTORY RESET\n\n"));
settingsFactoryReset(); settingsFactoryReset();
customReset(CUSTOM_RESET_FACTORY);
ESP.restart(); ESP.restart();
} }
@ -96,8 +112,8 @@ void buttonSetup() {
#ifdef SONOFF_DUAL #ifdef SONOFF_DUAL
unsigned int actions = buttonStore(BUTTON_MODE_NONE, BUTTON_MODE_TOGGLE, BUTTON_MODE_NONE, BUTTON_MODE_NONE, BUTTON_MODE_NONE); unsigned int actions = buttonStore(BUTTON_MODE_NONE, BUTTON_MODE_TOGGLE, BUTTON_MODE_NONE, BUTTON_MODE_NONE, BUTTON_MODE_NONE);
_buttons.push_back({new DebounceEvent(0, BUTTON_PUSHBUTTON), 0, 1});
_buttons.push_back({new DebounceEvent(0, BUTTON_PUSHBUTTON), 0, 2});
_buttons.push_back({new DebounceEvent(0, BUTTON_PUSHBUTTON), actions, 1});
_buttons.push_back({new DebounceEvent(0, BUTTON_PUSHBUTTON), actions, 2});
_buttons.push_back({new DebounceEvent(0, BUTTON_PUSHBUTTON), actions, BUTTON3_RELAY}); _buttons.push_back({new DebounceEvent(0, BUTTON_PUSHBUTTON), actions, BUTTON3_RELAY});
#else #else
@ -164,7 +180,7 @@ void buttonLoop() {
bool status = (value & (1 << i)) > 0; bool status = (value & (1 << i)) > 0;
// Cjeck if the status for that relay has changed
// Check if the status for that relay has changed
if (relayStatus(i) != status) { if (relayStatus(i) != status) {
buttonEvent(i, BUTTON_EVENT_CLICK); buttonEvent(i, BUTTON_EVENT_CLICK);
break; break;


+ 6
- 15
code/espurna/config/arduino.h View File

@ -3,21 +3,6 @@
// Uncomment the appropiate line(s) to build from the Arduino IDE // Uncomment the appropiate line(s) to build from the Arduino IDE
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
// General
//--------------------------------------------------------------------------------
#ifndef DEBUG_PORT
#define DEBUG_PORT Serial
#endif
// Uncomment and configure these lines to enable remote debug via udpDebug
// To receive the message son the destination computer use nc:
// nc -ul 8111
//#define DEBUG_UDP_IP IPAddress(192, 168, 1, 100)
//#define DEBUG_UDP_PORT 8111
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// Hardware // Hardware
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
@ -32,6 +17,8 @@
//#define SONOFF_SV //#define SONOFF_SV
//#define SONOFF_POW //#define SONOFF_POW
//#define SONOFF_DUAL //#define SONOFF_DUAL
//#define ITEAD_1CH_INCHING
//#define ITEAD_MOTOR
//#define SONOFF_4CH //#define SONOFF_4CH
//#define ESP_RELAY_BOARD //#define ESP_RELAY_BOARD
//#define ECOPLUG //#define ECOPLUG
@ -39,6 +26,9 @@
//#define WIFI_RELAY_NO //#define WIFI_RELAY_NO
//#define MQTT_RELAY //#define MQTT_RELAY
//#define WIFI_RELAYS_BOARD_KIT //#define WIFI_RELAYS_BOARD_KIT
//#define AI_LIGHT
//#define LED_CONTROLLER
//#define ESPURNA_H
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// Features (values below are non-default values) // Features (values below are non-default values)
@ -52,3 +42,4 @@
//#define ENABLE_FAUXMO 0 //#define ENABLE_FAUXMO 0
//#define ENABLE_NOFUSS 1 //#define ENABLE_NOFUSS 1
//#define ENABLE_DOMOTICZ 0 //#define ENABLE_DOMOTICZ 0
//#define ENABLE_ANALOG 1

+ 91
- 19
code/espurna/config/general.h View File

@ -8,13 +8,62 @@
#define HEARTBEAT_INTERVAL 300000 #define HEARTBEAT_INTERVAL 300000
#define UPTIME_OVERFLOW 4294967295 #define UPTIME_OVERFLOW 4294967295
//--------------------------------------------------------------------------------
// DEBUG
//--------------------------------------------------------------------------------
#ifndef DEBUG_PORT
#define DEBUG_PORT Serial
#endif
// Uncomment and configure these lines to enable remote debug via udpDebug
// To receive the message son the destination computer use nc:
// nc -ul 8111
//#define DEBUG_UDP_IP IPAddress(192, 168, 1, 100)
//#define DEBUG_UDP_PORT 8111
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// EEPROM // EEPROM
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
#define EEPROM_RELAY_STATUS 0 #define EEPROM_RELAY_STATUS 0
#define EEPROM_ENERGY_COUNT 1 #define EEPROM_ENERGY_COUNT 1
#define EEPROM_DATA_END 5
#define EEPROM_CUSTOM_RESET 5
#define EEPROM_DATA_END 6
//--------------------------------------------------------------------------------
// 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_MAX 10
#include <pgmspace.h>
PROGMEM const char custom_reset_hardware[] = "Hardware button";
PROGMEM const char custom_reset_web[] = "Reset from web interface";
PROGMEM const char custom_reset_terminal[] = "Reset from terminal";
PROGMEM const char custom_reset_mqtt[] = "Reset from MQTT";
PROGMEM const char custom_reset_rpc[] = "Reset from RPC";
PROGMEM const char custom_reset_ota[] = "Reset after successful OTA update";
PROGMEM const char custom_reset_nofuss[] = "Reset after successful NoFUSS update";
PROGMEM const char custom_reset_upgrade[] = "Reset after successful web update";
PROGMEM const char custom_reset_factory[] = "Factory reset";
PROGMEM const char* const custom_reset_string[] = {
custom_reset_hardware, custom_reset_web, custom_reset_terminal,
custom_reset_mqtt, custom_reset_rpc, custom_reset_ota,
custom_reset_nofuss, custom_reset_upgrade, custom_reset_factory
};
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// BUTTON // BUTTON
@ -61,8 +110,8 @@
#define RELAY_PROVIDER_DUAL 1 #define RELAY_PROVIDER_DUAL 1
#define RELAY_PROVIDER_LIGHT 2 #define RELAY_PROVIDER_LIGHT 2
// Pulse time in seconds
#define RELAY_PULSE_TIME 1
// Pulse time in milliseconds
#define RELAY_PULSE_TIME 1.0
// 0 means OFF, 1 ON and 2 whatever was before // 0 means OFF, 1 ON and 2 whatever was before
#define RELAY_MODE RELAY_MODE_OFF #define RELAY_MODE RELAY_MODE_OFF
@ -73,6 +122,12 @@
// 0 means no pulses, 1 means normally off, 2 normally on // 0 means no pulses, 1 means normally off, 2 normally on
#define RELAY_PULSE_MODE RELAY_PULSE_NONE #define RELAY_PULSE_MODE RELAY_PULSE_NONE
// Relay requests flood protection window - in seconds
#define RELAY_FLOOD_WINDOW 3
// Allowed actual relay changes inside requests flood protection window
#define RELAY_FLOOD_CHANGES 5
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// I18N // I18N
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
@ -147,30 +202,34 @@
#define MQTT_SKIP_RETAINED 1 #define MQTT_SKIP_RETAINED 1
#define MQTT_SKIP_TIME 1000 #define MQTT_SKIP_TIME 1000
#define MQTT_TOPIC_ACTION "/action"
#define MQTT_TOPIC_RELAY "/relay"
#define MQTT_TOPIC_LED "/led"
#define MQTT_TOPIC_COLOR "/color"
#define MQTT_TOPIC_BUTTON "/button"
#define MQTT_TOPIC_IP "/ip"
#define MQTT_TOPIC_VERSION "/version"
#define MQTT_TOPIC_UPTIME "/uptime"
#define MQTT_TOPIC_FREEHEAP "/freeheap"
#define MQTT_TOPIC_VCC "/vcc"
#define MQTT_TOPIC_STATUS "/status"
#define MQTT_TOPIC_MAC "/mac"
#define MQTT_TOPIC_APP "/app"
#define MQTT_TOPIC_INTERVAL "/interval"
#define MQTT_TOPIC_HOSTNAME "/hostname"
#define MQTT_TOPIC_ACTION "action"
#define MQTT_TOPIC_RELAY "relay"
#define MQTT_TOPIC_LED "led"
#define MQTT_TOPIC_COLOR "color"
#define MQTT_TOPIC_BUTTON "button"
#define MQTT_TOPIC_IP "ip"
#define MQTT_TOPIC_VERSION "version"
#define MQTT_TOPIC_UPTIME "uptime"
#define MQTT_TOPIC_FREEHEAP "freeheap"
#define MQTT_TOPIC_VCC "vcc"
#define MQTT_TOPIC_STATUS "status"
#define MQTT_TOPIC_MAC "mac"
#define MQTT_TOPIC_RSSI "rssi"
#define MQTT_TOPIC_APP "app"
#define MQTT_TOPIC_INTERVAL "interval"
#define MQTT_TOPIC_HOSTNAME "hostname"
#define MQTT_TOPIC_ANALOG "analog"
// Periodic reports // Periodic reports
#define MQTT_REPORT_STATUS 1 #define MQTT_REPORT_STATUS 1
#define MQTT_REPORT_IP 1 #define MQTT_REPORT_IP 1
#define MQTT_REPORT_MAC 1 #define MQTT_REPORT_MAC 1
#define MQTT_REPORT_RSSI 1
#define MQTT_REPORT_UPTIME 1 #define MQTT_REPORT_UPTIME 1
#define MQTT_REPORT_FREEHEAP 1 #define MQTT_REPORT_FREEHEAP 1
#define MQTT_REPORT_VCC 1 #define MQTT_REPORT_VCC 1
#define MQTT_REPORT_RELAY 1 #define MQTT_REPORT_RELAY 1
#define MQTT_REPORT_COLOR 1
#define MQTT_REPORT_HOSTNAME 1 #define MQTT_REPORT_HOSTNAME 1
#define MQTT_REPORT_APP 1 #define MQTT_REPORT_APP 1
#define MQTT_REPORT_VERSION 1 #define MQTT_REPORT_VERSION 1
@ -185,7 +244,7 @@
#define MQTT_DISCONNECT_EVENT 1 #define MQTT_DISCONNECT_EVENT 1
#define MQTT_MESSAGE_EVENT 2 #define MQTT_MESSAGE_EVENT 2
// Custom get and set postfixes
// Custom get and set postfixes6+
// Use something like "/status" or "/set", with leading slash // Use something like "/status" or "/set", with leading slash
#define MQTT_USE_GETTER "" #define MQTT_USE_GETTER ""
#define MQTT_USE_SETTER "" #define MQTT_USE_SETTER ""
@ -204,14 +263,18 @@
// LIGHT // LIGHT
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#define ENABLE_GAMMA_CORRECTION 0
#define LIGHT_PROVIDER_NONE 0 #define LIGHT_PROVIDER_NONE 0
#define LIGHT_PROVIDER_WS2812 1 #define LIGHT_PROVIDER_WS2812 1
#define LIGHT_PROVIDER_RGB 2 #define LIGHT_PROVIDER_RGB 2
#define LIGHT_PROVIDER_RGBW 3 #define LIGHT_PROVIDER_RGBW 3
#define LIGHT_PROVIDER_MY9192 4 #define LIGHT_PROVIDER_MY9192 4
#define LIGHT_PROVIDER_RGB2W 5
#define LIGHT_DEFAULT_COLOR "#000080" #define LIGHT_DEFAULT_COLOR "#000080"
#define LIGHT_SAVE_DELAY 5 #define LIGHT_SAVE_DELAY 5
#define LIGHT_MAX_VALUE 255
#define MY9291_DI_PIN 13 #define MY9291_DI_PIN 13
#define MY9291_DCKI_PIN 15 #define MY9291_DCKI_PIN 15
@ -234,6 +297,15 @@
#define DOMOTICZ_IN_TOPIC "domoticz/in" #define DOMOTICZ_IN_TOPIC "domoticz/in"
#define DOMOTICZ_OUT_TOPIC "domoticz/out" #define DOMOTICZ_OUT_TOPIC "domoticz/out"
// -----------------------------------------------------------------------------
// INFLUXDB
// -----------------------------------------------------------------------------
#ifndef ENABLE_INFLUXDB
#define ENABLE_INFLUXDB 1
#endif
#define INFLUXDB_PORT 8086
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// NTP // NTP
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


+ 60
- 4
code/espurna/config/hardware.h View File

@ -44,6 +44,23 @@
#define LED1_PIN 2 #define LED1_PIN 2
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
// -----------------------------------------------------------------------------
// ESPurna
// -----------------------------------------------------------------------------
#elif defined(ESPURNA_H)
#define MANUFACTURER "TINKERMAN"
#define DEVICE "ESPURNA_H"
#define RELAY1_PIN 12
#define RELAY1_PIN_INVERSE 1
#define LED1_PIN 5
#define LED1_PIN_INVERSE 0
#define BUTTON1_PIN 4
#define BUTTON1_RELAY 1
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define ENABLE_POW 1
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Itead Studio boards // Itead Studio boards
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -248,8 +265,8 @@
#elif defined(AI_LIGHT) #elif defined(AI_LIGHT)
#define MANUFACTURER "AI THINKER"
#define DEVICE "AI LIGHT"
#define MANUFACTURER "AI_THINKER"
#define DEVICE "AI_LIGHT"
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT #define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
#define LIGHT_PROVIDER LIGHT_PROVIDER_MY9192 #define LIGHT_PROVIDER LIGHT_PROVIDER_MY9192
@ -259,13 +276,51 @@
#elif defined(LED_CONTROLLER) #elif defined(LED_CONTROLLER)
#define MANUFACTURER "MAGIC HOME"
#define DEVICE "LED CONTROLLER"
#define MANUFACTURER "MAGIC_HOME"
#define DEVICE "LED_CONTROLLER"
#define LED1_PIN 2 #define LED1_PIN 2
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT #define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
#define LIGHT_PROVIDER LIGHT_PROVIDER_RGB #define LIGHT_PROVIDER LIGHT_PROVIDER_RGB
#undef RGBW_INVERSE_LOGIC
#undef RGBW_RED_PIN
#undef RGBW_GREEN_PIN
#undef RGBW_BLUE_PIN
#undef RGBW_WHITE_PIN
#define RGBW_INVERSE_LOGIC 1
#define RGBW_RED_PIN 14
#define RGBW_GREEN_PIN 5
#define RGBW_BLUE_PIN 12
#define RGBW_WHITE_PIN 13
// -----------------------------------------------------------------------------
// HUACANXING H801
// -----------------------------------------------------------------------------
#elif defined(H801_LED_CONTROLLER)
#define MANUFACTURER "HUACANXING"
#define DEVICE "H801"
#define LED1_PIN 5
#define LED1_PIN_INVERSE 1
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
#define LIGHT_PROVIDER LIGHT_PROVIDER_RGB2W
#undef RGBW_INVERSE_LOGIC
#undef RGBW_RED_PIN
#undef RGBW_GREEN_PIN
#undef RGBW_BLUE_PIN
#undef RGBW_WHITE_PIN
#define RGBW_INVERSE_LOGIC 1
#define RGBW_RED_PIN 15
#define RGBW_GREEN_PIN 13
#define RGBW_BLUE_PIN 12
#define RGBW_WHITE_PIN 14
#define RGBW_WHITE2_PIN 4
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Jan Goedeke Wifi Relay // Jan Goedeke Wifi Relay
// https://github.com/JanGoe/esp8266-wifi-relay // https://github.com/JanGoe/esp8266-wifi-relay
@ -326,6 +381,7 @@
#define DEVICE "MQTT_RELAY" #define DEVICE "MQTT_RELAY"
#define BUTTON1_PIN 0 #define BUTTON1_PIN 0
#define BUTTON1_RELAY 1 #define BUTTON1_RELAY 1
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define RELAY1_PIN 12 #define RELAY1_PIN 12
#define RELAY1_PIN_INVERSE 0 #define RELAY1_PIN_INVERSE 0
#define LED1_PIN 16 #define LED1_PIN 16


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

@ -11,6 +11,9 @@ void apiRegister(const char * url, const char * key, apiGetCallbackFunction getF
void mqttRegister(void (*callback)(unsigned int, const char *, const char *)); void mqttRegister(void (*callback)(unsigned int, const char *, const char *));
String mqttSubtopic(char * topic); String mqttSubtopic(char * topic);
template<typename T> bool setSetting(const String& key, T value); template<typename T> bool setSetting(const String& key, T value);
template<typename T> bool setSetting(const String& key, unsigned int index, T value);
template<typename T> String getSetting(const String& key, T defaultValue); template<typename T> String getSetting(const String& key, T defaultValue);
template<typename T> String getSetting(const String& key, unsigned int index, T defaultValue);
template<typename T> void domoticzSend(const char * key, T value); 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> void domoticzSend(const char * key, T nvalue, const char * svalue);
template<typename T> bool influxDBSend(const char * topic, T payload);

+ 37
- 14
code/espurna/config/sensors.h View File

@ -17,14 +17,28 @@
#define DHT_UPDATE_INTERVAL 60000 #define DHT_UPDATE_INTERVAL 60000
#define DHT_TYPE DHT22 #define DHT_TYPE DHT22
#define DHT_TIMING 11 #define DHT_TIMING 11
#define DHT_TEMPERATURE_TOPIC "/temperature"
#define DHT_HUMIDITY_TOPIC "/humidity"
#define DHT_TEMPERATURE_TOPIC "temperature"
#define DHT_HUMIDITY_TOPIC "humidity"
#define HUMIDITY_NORMAL 0 #define HUMIDITY_NORMAL 0
#define HUMIDITY_COMFORTABLE 1 #define HUMIDITY_COMFORTABLE 1
#define HUMIDITY_DRY 2 #define HUMIDITY_DRY 2
#define HUMIDITY_WET 3 #define HUMIDITY_WET 3
//--------------------------------------------------------------------------------
// Analog sensor
// Enable support by passing ENABLE_ANALOG=1 build flag
//--------------------------------------------------------------------------------
#define ANALOG_PIN 0
#define ANALOG_UPDATE_INTERVAL 60000
#define ANALOG_TOPIC "analog"
#if ENABLE_ANALOG
#undef ENABLE_ADC_VCC
#define ENABLE_ADC_VCC 0
#endif
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// DS18B20 temperature sensor // DS18B20 temperature sensor
// Enable support by passing ENABLE_DS18B20=1 build flag // Enable support by passing ENABLE_DS18B20=1 build flag
@ -32,7 +46,7 @@
#define DS_PIN 14 #define DS_PIN 14
#define DS_UPDATE_INTERVAL 60000 #define DS_UPDATE_INTERVAL 60000
#define DS_TEMPERATURE_TOPIC "/temperature"
#define DS_TEMPERATURE_TOPIC "temperature"
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// Custom current sensor // Custom current sensor
@ -72,9 +86,9 @@
#define EMON_INTERVAL 10000 #define EMON_INTERVAL 10000
#define EMON_MEASUREMENTS 6 #define EMON_MEASUREMENTS 6
#define EMON_MAINS_VOLTAGE 230 #define EMON_MAINS_VOLTAGE 230
#define EMON_APOWER_TOPIC "/apower"
#define EMON_ENERGY_TOPIC "/energy"
#define EMON_CURRENT_TOPIC "/current"
#define EMON_APOWER_TOPIC "apower"
#define EMON_ENERGY_TOPIC "energy"
#define EMON_CURRENT_TOPIC "current"
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// HLW8012 power sensor (Sonoff POW) // HLW8012 power sensor (Sonoff POW)
@ -82,23 +96,32 @@
// Enabled by default when selecting SONOFF_POW hardware // Enabled by default when selecting SONOFF_POW hardware
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
#define POW_SEL_PIN 5
#ifdef ESPURNA_H
#define POW_SEL_PIN 2
#else
#define POW_SEL_PIN 5
#endif
#define POW_CF1_PIN 13 #define POW_CF1_PIN 13
#define POW_CF_PIN 14 #define POW_CF_PIN 14
#define POW_USE_INTERRUPTS 1 #define POW_USE_INTERRUPTS 1
#define POW_SEL_CURRENT HIGH #define POW_SEL_CURRENT HIGH
#define POW_CURRENT_R 0.001 #define POW_CURRENT_R 0.001
#define POW_VOLTAGE_R_UP ( 5 * 470000 ) // Real: 2280k #define POW_VOLTAGE_R_UP ( 5 * 470000 ) // Real: 2280k
#define POW_VOLTAGE_R_DOWN ( 1000 ) // Real 1.009k #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_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_UPDATE_INTERVAL 5000
#define POW_REPORT_EVERY 12 #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
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// Internal power montior // Internal power montior


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

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

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


+ 17
- 4
code/espurna/debug.ino View File

@ -20,16 +20,22 @@ void debugSend(const char * format, ...) {
va_list args; va_list args;
va_start(args, format); va_start(args, format);
ets_vsnprintf(buffer, DEBUG_MESSAGE_MAX_LENGTH, format, args);
int len = ets_vsnprintf(buffer, DEBUG_MESSAGE_MAX_LENGTH, format, args);
va_end(args); va_end(args);
#ifdef DEBUG_PORT #ifdef DEBUG_PORT
DEBUG_PORT.printf(buffer); DEBUG_PORT.printf(buffer);
if (len > DEBUG_MESSAGE_MAX_LENGTH) {
DEBUG_PORT.printf(" (...)\n");
}
#endif #endif
#ifdef DEBUG_UDP_IP #ifdef DEBUG_UDP_IP
udpDebug.beginPacket(DEBUG_UDP_IP, DEBUG_UDP_PORT); udpDebug.beginPacket(DEBUG_UDP_IP, DEBUG_UDP_PORT);
udpDebug.write(buffer); udpDebug.write(buffer);
if (len > DEBUG_MESSAGE_MAX_LENGTH) {
udpDebug.write(" (...)\n");
}
udpDebug.endPacket(); udpDebug.endPacket();
#endif #endif
@ -37,24 +43,31 @@ void debugSend(const char * format, ...) {
void debugSend_P(PGM_P format, ...) { void debugSend_P(PGM_P format, ...) {
char buffer[DEBUG_MESSAGE_MAX_LENGTH+1];
char f[DEBUG_MESSAGE_MAX_LENGTH+1]; char f[DEBUG_MESSAGE_MAX_LENGTH+1];
memcpy_P(f, format, DEBUG_MESSAGE_MAX_LENGTH); memcpy_P(f, format, DEBUG_MESSAGE_MAX_LENGTH);
char buffer[DEBUG_MESSAGE_MAX_LENGTH+1];
va_list args; va_list args;
va_start(args, format); va_start(args, format);
ets_vsnprintf(buffer, DEBUG_MESSAGE_MAX_LENGTH, f, args);
int len = ets_vsnprintf(buffer, DEBUG_MESSAGE_MAX_LENGTH, f, args);
va_end(args); va_end(args);
#ifdef DEBUG_PORT #ifdef DEBUG_PORT
DEBUG_PORT.printf(buffer); DEBUG_PORT.printf(buffer);
if (len > DEBUG_MESSAGE_MAX_LENGTH) {
DEBUG_PORT.printf(" (...)\n");
}
#endif #endif
#ifdef DEBUG_UDP_IP #ifdef DEBUG_UDP_IP
udpDebug.beginPacket(DEBUG_UDP_IP, DEBUG_UDP_PORT); udpDebug.beginPacket(DEBUG_UDP_IP, DEBUG_UDP_PORT);
udpDebug.write(buffer); udpDebug.write(buffer);
if (len > DEBUG_MESSAGE_MAX_LENGTH) {
udpDebug.write(" (...)\n");
}
udpDebug.endPacket(); udpDebug.endPacket();
delay(1);
#endif #endif
} }

+ 7
- 2
code/espurna/dht.ino View File

@ -30,10 +30,10 @@ unsigned int getDHTHumidity() {
void dhtSetup() { void dhtSetup() {
dht.begin(); dht.begin();
apiRegister("/api/temperature", "temperature", [](char * buffer, size_t len) {
apiRegister(DHT_TEMPERATURE_TOPIC, DHT_TEMPERATURE_TOPIC, [](char * buffer, size_t len) {
dtostrf(_dhtTemperature, len-1, 1, buffer); dtostrf(_dhtTemperature, len-1, 1, buffer);
}); });
apiRegister("/api/humidity", "humidity", [](char * buffer, size_t len) {
apiRegister(DHT_HUMIDITY_TOPIC, DHT_HUMIDITY_TOPIC, [](char * buffer, size_t len) {
snprintf(buffer, len, "%d", _dhtHumidity); snprintf(buffer, len, "%d", _dhtHumidity);
}); });
} }
@ -93,6 +93,11 @@ void dhtLoop() {
} }
#endif #endif
#if ENABLE_INFLUXDB
influxDBSend(getSetting("dhtTmpTopic", DHT_TEMPERATURE_TOPIC).c_str(), temperature);
influxDBSend(getSetting("dhtHumTopic", DHT_HUMIDITY_TOPIC).c_str(), humidity);
#endif
// Update websocket clients // Update websocket clients
char buffer[100]; char buffer[100];
sprintf_P(buffer, PSTR("{\"dhtVisible\": 1, \"dhtTmp\": %s, \"dhtHum\": %s, \"tmpUnits\": %d}"), temperature, humidity, tmpUnits); sprintf_P(buffer, PSTR("{\"dhtVisible\": 1, \"dhtTmp\": %s, \"dhtHum\": %s, \"tmpUnits\": %d}"), temperature, humidity, tmpUnits);


+ 5
- 1
code/espurna/ds18b20.ino View File

@ -37,7 +37,7 @@ void dsSetup() {
ds18b20.begin(); ds18b20.begin();
ds18b20.setWaitForConversion(false); ds18b20.setWaitForConversion(false);
apiRegister("/api/temperature", "temperature", [](char * buffer, size_t len) {
apiRegister(DS_TEMPERATURE_TOPIC, DS_TEMPERATURE_TOPIC, [](char * buffer, size_t len) {
dtostrf(_dsTemperature, len-1, 1, buffer); dtostrf(_dsTemperature, len-1, 1, buffer);
}); });
} }
@ -99,6 +99,10 @@ void dsLoop() {
domoticzSend("dczTmpIdx", 0, _dsTemperatureStr); domoticzSend("dczTmpIdx", 0, _dsTemperatureStr);
#endif #endif
#if ENABLE_INFLUXDB
influxDBSend(getSetting("dsTmpTopic", DS_TEMPERATURE_TOPIC).c_str(), _dsTemperatureStr);
#endif
// Update websocket clients // Update websocket clients
char buffer[100]; char buffer[100];
sprintf_P(buffer, PSTR("{\"dsVisible\": 1, \"dsTmp\": %s, \"tmpUnits\": %d}"), getDSTemperatureStr(), tmpUnits); sprintf_P(buffer, PSTR("{\"dsVisible\": 1, \"dsTmp\": %s, \"tmpUnits\": %d}"), getDSTemperatureStr(), tmpUnits);


+ 7
- 1
code/espurna/emon.ino View File

@ -97,7 +97,7 @@ void powerMonitorSetup() {
brzo_i2c_end_transaction(); brzo_i2c_end_transaction();
#endif #endif
apiRegister("/api/power", "power", [](char * buffer, size_t len) {
apiRegister(EMON_APOWER_TOPIC, EMON_APOWER_TOPIC, [](char * buffer, size_t len) {
snprintf(buffer, len, "%d", _power); snprintf(buffer, len, "%d", _power);
}); });
@ -189,6 +189,12 @@ void powerMonitorLoop() {
} }
#endif #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);
#endif
// Reset counters // Reset counters
sum = measurements = 0; sum = measurements = 0;


+ 64
- 3
code/espurna/espurna.ino View File

@ -51,6 +51,7 @@ void heartbeat() {
#endif #endif
} }
#if (MQTT_REPORT_INTERVAL) #if (MQTT_REPORT_INTERVAL)
mqttSend(MQTT_TOPIC_INTERVAL, HEARTBEAT_INTERVAL / 1000); mqttSend(MQTT_TOPIC_INTERVAL, HEARTBEAT_INTERVAL / 1000);
#endif #endif
@ -69,15 +70,29 @@ void heartbeat() {
#if (MQTT_REPORT_MAC) #if (MQTT_REPORT_MAC)
mqttSend(MQTT_TOPIC_MAC, WiFi.macAddress().c_str()); mqttSend(MQTT_TOPIC_MAC, WiFi.macAddress().c_str());
#endif #endif
#if (MQTT_REPORT_RSSI)
mqttSend(MQTT_TOPIC_RSSI, String(WiFi.RSSI()).c_str());
#endif
#if (MQTT_REPORT_UPTIME) #if (MQTT_REPORT_UPTIME)
mqttSend(MQTT_TOPIC_UPTIME, String(uptime_seconds).c_str()); mqttSend(MQTT_TOPIC_UPTIME, String(uptime_seconds).c_str());
#if ENABLE_INFLUXDB
influxDBSend(MQTT_TOPIC_UPTIME, String(uptime_seconds).c_str());
#endif
#endif #endif
#if (MQTT_REPORT_FREEHEAP) #if (MQTT_REPORT_FREEHEAP)
mqttSend(MQTT_TOPIC_FREEHEAP, String(free_heap).c_str()); mqttSend(MQTT_TOPIC_FREEHEAP, String(free_heap).c_str());
#if ENABLE_INFLUXDB
influxDBSend(MQTT_TOPIC_FREEHEAP, String(free_heap).c_str());
#endif
#endif #endif
#if (MQTT_REPORT_RELAY) #if (MQTT_REPORT_RELAY)
relayMQTT(); relayMQTT();
#endif #endif
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
#if (MQTT_REPORT_COLOR)
mqttSend(MQTT_TOPIC_COLOR, lightColor().c_str());
#endif
#endif
#if (MQTT_REPORT_VCC) #if (MQTT_REPORT_VCC)
#if ENABLE_ADC_VCC #if ENABLE_ADC_VCC
mqttSend(MQTT_TOPIC_VCC, String(ESP.getVcc()).c_str()); mqttSend(MQTT_TOPIC_VCC, String(ESP.getVcc()).c_str());
@ -89,9 +104,32 @@ void heartbeat() {
} }
void customReset(unsigned char status) {
EEPROM.write(EEPROM_CUSTOM_RESET, status);
EEPROM.commit();
}
unsigned char customReset() {
static unsigned char status = 255;
if (status == 255) {
status = EEPROM.read(EEPROM_CUSTOM_RESET);
if (status > 0) customReset(0);
if (status > CUSTOM_RESET_MAX) status = 0;
}
return status;
}
void hardwareSetup() { void hardwareSetup() {
EEPROM.begin(4096); EEPROM.begin(4096);
Serial.begin(SERIAL_BAUDRATE);
#ifdef DEBUG_PORT
DEBUG_PORT.begin(SERIAL_BAUDRATE);
if (customReset() == CUSTOM_RESET_HARDWARE) {
DEBUG_PORT.setDebugOutput(true);
}
#endif
#ifdef SONOFF_DUAL
Serial.begin(SERIAL_BAUDRATE);
#endif
#if not EMBEDDED_WEB #if not EMBEDDED_WEB
SPIFFS.begin(); SPIFFS.begin();
#endif #endif
@ -118,8 +156,18 @@ void welcome() {
DEBUG_MSG_P(PSTR("%s\n%s\n\n"), (char *) APP_AUTHOR, (char *) APP_WEBSITE); DEBUG_MSG_P(PSTR("%s\n%s\n\n"), (char *) APP_AUTHOR, (char *) APP_WEBSITE);
DEBUG_MSG_P(PSTR("ChipID: %06X\n"), ESP.getChipId()); DEBUG_MSG_P(PSTR("ChipID: %06X\n"), ESP.getChipId());
DEBUG_MSG_P(PSTR("CPU frequency: %d MHz\n"), ESP.getCpuFreqMHz()); DEBUG_MSG_P(PSTR("CPU frequency: %d MHz\n"), ESP.getCpuFreqMHz());
DEBUG_MSG_P(PSTR("Last reset reason: %s\n"), (char *) ESP.getResetReason().c_str());
DEBUG_MSG_P(PSTR("Memory size: %d bytes\n"), ESP.getFlashChipSize());
unsigned char custom_reset = customReset();
if (custom_reset > 0) {
char buffer[32];
strcpy_P(buffer, custom_reset_string[custom_reset-1]);
DEBUG_MSG_P(PSTR("Last reset reason: %s\n"), buffer);
} else {
DEBUG_MSG_P(PSTR("Last reset reason: %s\n"), (char *) ESP.getResetReason().c_str());
}
DEBUG_MSG_P(PSTR("Memory size (SDK): %d bytes\n"), ESP.getFlashChipSize());
DEBUG_MSG_P(PSTR("Memory size (CHIP): %d bytes\n"), ESP.getFlashChipRealSize());
DEBUG_MSG_P(PSTR("Free heap: %d bytes\n"), ESP.getFreeHeap()); DEBUG_MSG_P(PSTR("Free heap: %d bytes\n"), ESP.getFreeHeap());
DEBUG_MSG_P(PSTR("Firmware size: %d bytes\n"), ESP.getSketchSize()); DEBUG_MSG_P(PSTR("Firmware size: %d bytes\n"), ESP.getSketchSize());
DEBUG_MSG_P(PSTR("Free firmware space: %d bytes\n"), ESP.getFreeSketchSpace()); DEBUG_MSG_P(PSTR("Free firmware space: %d bytes\n"), ESP.getFreeSketchSpace());
@ -175,12 +223,18 @@ void setup() {
#if ENABLE_NOFUSS #if ENABLE_NOFUSS
nofussSetup(); nofussSetup();
#endif #endif
#if ENABLE_INFLUXDB
influxDBSetup();
#endif
#if ENABLE_POW #if ENABLE_POW
powSetup(); powSetup();
#endif #endif
#if ENABLE_DS18B20 #if ENABLE_DS18B20
dsSetup(); dsSetup();
#endif #endif
#if ENABLE_ANALOG
analogSetup();
#endif
#if ENABLE_DHT #if ENABLE_DHT
dhtSetup(); dhtSetup();
#endif #endif
@ -191,12 +245,16 @@ void setup() {
powerMonitorSetup(); powerMonitorSetup();
#endif #endif
// Prepare configuration for version 2.0
hwUpwardsCompatibility();
} }
void loop() { void loop() {
hardwareLoop(); hardwareLoop();
buttonLoop(); buttonLoop();
relayLoop();
ledLoop(); ledLoop();
wifiLoop(); wifiLoop();
otaLoop(); otaLoop();
@ -218,6 +276,9 @@ void loop() {
#if ENABLE_DS18B20 #if ENABLE_DS18B20
dsLoop(); dsLoop();
#endif #endif
#if ENABLE_ANALOG
analogLoop();
#endif
#if ENABLE_DHT #if ENABLE_DHT
dhtLoop(); dhtLoop();
#endif #endif


+ 283
- 0
code/espurna/hardware.ino View File

@ -0,0 +1,283 @@
/*
HARDWARE MODULE
Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
*/
/*
The goal of this file is to store board configuration values in EEPROM so
the migration to future version 2 will be straigh forward.
*/
#define RELAY_PROVIDER_RELAY 0
#define RELAY_PROVIDER_DUAL 1
#define RELAY_PROVIDER_LIGHT 2
#define LIGHT_PROVIDER_NONE 0
#define LIGHT_PROVIDER_WS2812 1
#define LIGHT_PROVIDER_RGB 2
#define LIGHT_PROVIDER_RGBW 3
#define LIGHT_PROVIDER_MY9192 4
void hwUpwardsCompatibility() {
unsigned int board = getSetting("board", 0).toInt();
if (board > 0) return;
#ifdef NODEMCUV2
setSetting("board", 2);
setSetting("ledGPIO", 1, 2);
setSetting("ledLogic", 1, 1);
setSetting("btnGPIO", 1, 0);
setSetting("btnRelay", 1, 1);
setSetting("relayGPIO", 1, 12);
setSetting("relayLogic", 1, 0);
#endif
#ifdef D1_RELAYSHIELD
setSetting("board", 3);
setSetting("ledGPIO", 1, 2);
setSetting("ledLogic", 1, 1);
setSetting("relayGPIO", 1, 5);
setSetting("relayLogic", 1, 0);
#endif
#ifdef SONOFF
setSetting("board", 4);
setSetting("ledGPIO", 1, 13);
setSetting("ledLogic", 1, 1);
setSetting("btnGPIO", 1, 0);
setSetting("btnRelay", 1, 1);
setSetting("relayGPIO", 1, 12);
setSetting("relayLogic", 1, 0);
#endif
#ifdef SONOFF_TH
setSetting("board", 5);
setSetting("ledGPIO", 1, 13);
setSetting("ledLogic", 1, 1);
setSetting("btnGPIO", 1, 0);
setSetting("btnRelay", 1, 1);
setSetting("relayGPIO", 1, 12);
setSetting("relayLogic", 1, 0);
#endif
#ifdef SONOFF_SV
setSetting("board", 6);
setSetting("ledGPIO", 1, 13);
setSetting("ledLogic", 1, 1);
setSetting("btnGPIO", 1, 0);
setSetting("btnRelay", 1, 1);
setSetting("relayGPIO", 1, 12);
setSetting("relayLogic", 1, 0);
#endif
#ifdef SONOFF_TOUCH
setSetting("board", 7);
setSetting("ledGPIO", 1, 13);
setSetting("ledLogic", 1, 1);
setSetting("btnGPIO", 1, 0);
setSetting("btnRelay", 1, 1);
setSetting("relayGPIO", 1, 12);
setSetting("relayLogic", 1, 0);
#endif
#ifdef SONOFF_POW
setSetting("board", 8);
setSetting("ledGPIO", 1, 15);
setSetting("ledLogic", 1, 1);
setSetting("btnGPIO", 1, 0);
setSetting("btnRelay", 1, 1);
setSetting("relayGPIO", 1, 12);
setSetting("relayLogic", 1, 0);
#endif
#ifdef SONOFF_DUAL
setSetting("board", 9);
setSetting("ledGPIO", 1, 13);
setSetting("ledLogic", 1, 1);
setSetting("btnRelay", 3, 1);
setSetting("relayProvider", RELAY_PROVIDER_DUAL);
#endif
#ifdef ITEAD_1CH_INCHING
setSetting("board", 10);
setSetting("ledGPIO", 1, 13);
setSetting("ledLogic", 1, 1);
setSetting("btnGPIO", 1, 0);
setSetting("btnRelay", 1, 1);
setSetting("relayGPIO", 1, 12);
setSetting("relayLogic", 1, 0);
#endif
#ifdef SONOFF_4CH
setSetting("board", 11);
setSetting("ledGPIO", 1, 13);
setSetting("ledLogic", 1, 1);
setSetting("btnGPIO", 1, 0);
setSetting("btnGPIO", 2, 9);
setSetting("btnGPIO", 3, 10);
setSetting("btnGPIO", 4, 14);
setSetting("btnRelay", 1, 2);
setSetting("btnRelay", 2, 3);
setSetting("btnRelay", 3, 4);
setSetting("btnRelay", 4, 0);
setSetting("relayGPIO", 1, 12);
setSetting("relayGPIO", 2, 5);
setSetting("relayGPIO", 3, 4);
setSetting("relayGPIO", 4, 15);
setSetting("relayLogic", 1, 0);
setSetting("relayLogic", 2, 0);
setSetting("relayLogic", 3, 0);
setSetting("relayLogic", 4, 0);
#endif
#ifdef SLAMPHER
setSetting("board", 12);
setSetting("ledGPIO", 1, 13);
setSetting("ledLogic", 1, 1);
setSetting("btnGPIO", 1, 0);
setSetting("btnRelay", 1, 1);
setSetting("relayGPIO", 1, 12);
setSetting("relayLogic", 1, 0);
#endif
#ifdef S20
setSetting("board", 13);
setSetting("ledGPIO", 1, 13);
setSetting("ledLogic", 1, 1);
setSetting("btnGPIO", 1, 0);
setSetting("btnRelay", 1, 1);
setSetting("relayGPIO", 1, 12);
setSetting("relayLogic", 1, 0);
#endif
#ifdef ESP_RELAY_BOARD
setSetting("board", 14);
setSetting("ledGPIO", 1, 16);
setSetting("ledLogic", 1, 0);
setSetting("btnGPIO", 1, 0);
setSetting("btnGPIO", 2, 2);
setSetting("btnRelay", 1, 1);
setSetting("btnRelay", 2, 2);
setSetting("relayGPIO", 1, 12);
setSetting("relayGPIO", 2, 13);
setSetting("relayLogic", 1, 0);
setSetting("relayLogic", 2, 0);
#endif
#ifdef ECOPLUG
setSetting("board", 15);
setSetting("ledGPIO", 1, 2);
setSetting("ledLogic", 1, 0);
setSetting("btnGPIO", 1, 13);
setSetting("btnRelay", 1, 1);
setSetting("relayGPIO", 1, 15);
setSetting("relayLogic", 1, 0);
#endif
#ifdef WIFI_RELAY_NC
setSetting("board", 16);
setSetting("btnGPIO", 1, 12);
setSetting("btnGPIO", 2, 13);
setSetting("btnRelay", 1, 1);
setSetting("btnRelay", 2, 2);
setSetting("relayGPIO", 1, 2);
setSetting("relayGPIO", 2, 14);
setSetting("relayLogic", 1, 1);
setSetting("relayLogic", 2, 1);
#endif
#ifdef WIFI_RELAY_NO
setSetting("board", 17);
setSetting("btnGPIO", 1, 12);
setSetting("btnGPIO", 2, 13);
setSetting("btnRelay", 1, 1);
setSetting("btnRelay", 2, 2);
setSetting("relayGPIO", 1, 2);
setSetting("relayGPIO", 2, 14);
setSetting("relayLogic", 1, 0);
setSetting("relayLogic", 2, 0);
#endif
#ifdef MQTT_RELAY
setSetting("board", 18);
setSetting("ledGPIO", 1, 16);
setSetting("ledLogic", 1, 1);
setSetting("btnGPIO", 1, 0);
setSetting("btnRelay", 1, 1);
setSetting("relayGPIO", 1, 12);
setSetting("relayLogic", 1, 0);
#endif
#ifdef WIFI_RELAYS_BOARD_KIT
setSetting("board", 19);
setSetting("relayGPIO", 1, 0);
setSetting("relayLogic", 1, 1);
setSetting("relayGPIO", 2, 2);
setSetting("relayLogic", 2, 1);
#endif
#ifdef AI_LIGHT
setSetting("board", 20);
setSetting("relayProvider", RELAY_PROVIDER_LIGHT);
setSetting("lightProvider", LIGHT_PROVIDER_MY9192);
setSetting("myDIGPIO", 13);
setSetting("myDCKIGPIO", 15);
#endif
#ifdef LED_CONTROLLER
setSetting("board", 21);
setSetting("relayProvider", RELAY_PROVIDER_LIGHT);
setSetting("lightProvider", LIGHT_PROVIDER_RGB);
setSetting("ledGPIO", 1, 2);
setSetting("ledLogic", 1, 1);
setSetting("redGPIO", 14);
setSetting("greenGPIO", 5);
setSetting("blueGPIO", 12);
setSetting("whiteGPIO", 13);
setSetting("lightLogic", 1);
#endif
#ifdef ITEAD_MOTOR
setSetting("board", 22);
setSetting("ledGPIO", 1, 13);
setSetting("ledLogic", 1, 1);
setSetting("btnGPIO", 1, 0);
setSetting("btnRelay", 1, 1);
setSetting("relayGPIO", 1, 12);
setSetting("relayLogic", 1, 0);
#endif
#ifdef ESPURNA_H
setSetting("board", 23);
setSetting("ledGPIO", 1, 5);
setSetting("ledLogic", 1, 0);
setSetting("btnGPIO", 1, 4);
setSetting("btnRelay", 1, 1);
setSetting("relayGPIO", 1, 12);
setSetting("relayLogic", 1, 1);
#endif
#ifdef H801_LED_CONTROLLER
setSetting("board", 24);
setSetting("relayProvider", RELAY_PROVIDER_LIGHT);
setSetting("lightProvider", LIGHT_PROVIDER_RGB2W);
setSetting("ledGPIO", 5, 1);
setSetting("ledLogic", 1, 1);
setSetting("redGPIO", 15);
setSetting("greenGPIO", 13);
setSetting("blueGPIO", 12);
setSetting("whiteGPIO", 14);
setSetting("white2GPIO", 4);
setSetting("lightLogic", 1);
#endif
saveSettings();
}

+ 62
- 0
code/espurna/influxdb.ino View File

@ -0,0 +1,62 @@
/*
I2C MODULE
Copyright (C) 2017 by Xose Pérez <xose dot perez at gmail dot com>
*/
#if ENABLE_INFLUXDB
#include "ESPAsyncTCP.h"
#include "SyncClient.h"
bool influxDBEnabled = false;
SyncClient _influxClient;
template<typename T> bool influxDBSend(const char * topic, T payload) {
if (!influxDBEnabled) return true;
if (!wifiConnected() || (WiFi.getMode() != WIFI_STA)) return true;
DEBUG_MSG_P(("[INFLUXDB] Sending\n"));
_influxClient.setTimeout(2);
if (!_influxClient.connect(getSetting("idbHost").c_str(), getSetting("idbPort", INFLUXDB_PORT).toInt())) {
DEBUG_MSG_P(("[INFLUXDB] Connection failed\n"));
return false;
}
char data[128];
sprintf(data, "%s,device=%s value=%s", topic, getSetting("hostname", HOSTNAME).c_str(), String(payload).c_str());
DEBUG_MSG_P(("[INFLUXDB] Data: %s\n"), data);
char request[256];
sprintf(request, "POST /write?db=%s&u=%s&p=%s HTTP/1.1\r\nHost: %s:%d\r\nContent-Length: %d\r\n\r\n%s",
getSetting("idbDatabase").c_str(), getSetting("idbUsername").c_str(), getSetting("idbPassword").c_str(),
getSetting("idbHost").c_str(), getSetting("idbPort", INFLUXDB_PORT).toInt(),
strlen(data), data);
if (_influxClient.printf(request) > 0) {
while (_influxClient.connected() && _influxClient.available() == 0) delay(1);
while (_influxClient.available()) _influxClient.read();
if (_influxClient.connected()) _influxClient.stop();
return true;
}
_influxClient.stop();
DEBUG_MSG_P(("[INFLUXDB] Sent failed\n"));
while (_influxClient.connected()) delay(0);
return false;
}
void influxDBConfigure() {
influxDBEnabled = getSetting("idbHost").length() > 0;
}
void influxDBSetup() {
influxDBConfigure();
}
#endif

+ 179
- 47
code/espurna/light.ino View File

@ -18,19 +18,90 @@ unsigned int _lightColor[3] = {0};
my9291 * _my9291; my9291 * _my9291;
#endif #endif
#if ENABLE_GAMMA_CORRECTION
#define GAMMA_TABLE_SIZE (256)
#undef LIGHT_PWM_RANGE
#define LIGHT_PWM_RANGE (4095)
// Gamma Correction lookup table for gamma=2.8 and 12 bit (4095) full scale
const unsigned short gamma_table[GAMMA_TABLE_SIZE] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 8, 9, 10, 11,
12, 13, 15, 16, 17, 18, 20, 21, 23, 25, 26, 28, 30, 32, 34, 36,
38, 40, 43, 45, 48, 50, 53, 56, 59, 62, 65, 68, 71, 75, 78, 82,
85, 89, 93, 97, 101, 105, 110, 114, 119, 123, 128, 133, 138, 143, 149, 154,
159, 165, 171, 177, 183, 189, 195, 202, 208, 215, 222, 229, 236, 243, 250, 258,
266, 273, 281, 290, 298, 306, 315, 324, 332, 341, 351, 360, 369, 379, 389, 399,
409, 419, 430, 440, 451, 462, 473, 485, 496, 508, 520, 532, 544, 556, 569, 582,
594, 608, 621, 634, 648, 662, 676, 690, 704, 719, 734, 749, 764, 779, 795, 811,
827, 843, 859, 876, 893, 910, 927, 944, 962, 980, 998,1016,1034,1053,1072,1091,
1110,1130,1150,1170,1190,1210,1231,1252,1273,1294,1316,1338,1360,1382,1404,1427,
1450,1473,1497,1520,1544,1568,1593,1617,1642,1667,1693,1718,1744,1770,1797,1823,
1850,1877,1905,1932,1960,1988,2017,2045,2074,2103,2133,2162,2192,2223,2253,2284,
2315,2346,2378,2410,2442,2474,2507,2540,2573,2606,2640,2674,2708,2743,2778,2813,
2849,2884,2920,2957,2993,3030,3067,3105,3143,3181,3219,3258,3297,3336,3376,3416,
3456,3496,3537,3578,3619,3661,3703,3745,3788,3831,3874,3918,3962,4006,4050,4095 };
#endif
#ifndef LIGHT_PWM_FREQUENCY
#define LIGHT_PWM_FREQUENCY (1000)
#endif
#ifndef LIGHT_PWM_RANGE
#define LIGHT_PWM_RANGE (255)
#endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// UTILS // UTILS
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void color_rgb2array(const char * rgb, unsigned int * array) {
void color_string2array(const char * rgb, unsigned int * array) {
char * p = (char *) rgb; char * p = (char *) rgb;
if (p[0] == '#') ++p;
if (strlen(p) == 0) return;
unsigned long value = strtol(p, NULL, 16);
array[0] = (value >> 16) & 0xFF;
array[1] = (value >> 8) & 0xFF;
array[2] = (value) & 0xFF;
// if color begins with a # then assume HEX RGB
if (p[0] == '#') {
++p;
unsigned long value = strtol(p, NULL, 16);
array[0] = (value >> 16) & 0xFF;
array[1] = (value >> 8) & 0xFF;
array[2] = (value) & 0xFF;
// it's a temperature
} else if (p[strlen(p)-1] == 'K') {
p[strlen(p)-1] = 0;
unsigned int temperature = atoi(p);
color_temperature2array(temperature, array);
// otherwise assume decimal values separated by commas
} else {
char * tok;
tok = strtok(p, ",");
array[0] = atoi(tok);
tok = strtok(NULL, ",");
// if there are more than one value assume R,G,B
if (tok != NULL) {
array[1] = atoi(tok);
tok = strtok(NULL, ",");
if (tok != NULL) {
array[2] = atoi(tok);
} else {
array[2] = 0;
}
// only one value set red, green and blue to the same value
} else {
array[2] = array[1] = array[0];
}
}
} }
@ -41,15 +112,61 @@ void color_array2rgb(unsigned int * array, char * rgb) {
sprintf(rgb, "#%06X", value); sprintf(rgb, "#%06X", value);
} }
// Thanks to Sacha Telgenhof for sharing this code in his AiLight library
// https://github.com/stelgenhof/AiLight
void color_temperature2array(unsigned int temperature, unsigned int * array) {
// Force boundaries and conversion
temperature = constrain(temperature, 1000, 40000) / 100;
// Calculate colors
unsigned int red = (temperature <= 66)
? LIGHT_MAX_VALUE
: 329.698727446 * pow((temperature - 60), -0.1332047592);
unsigned int green = (temperature <= 66)
? 99.4708025861 * log(temperature) - 161.1195681661
: 288.1221695283 * pow(temperature, -0.0755148492);
unsigned int blue = (temperature >= 66)
? LIGHT_MAX_VALUE
: ((temperature <= 19)
? 0
: 138.5177312231 * log(temperature - 10) - 305.0447927307);
// Save values
array[0] = constrain(red, 0, LIGHT_MAX_VALUE);
array[1] = constrain(green, 0, LIGHT_MAX_VALUE);
array[2] = constrain(blue, 0, LIGHT_MAX_VALUE);
}
// Converts a color intensity value (0..255) to a pwm value
// This takes care of positive or negative logic
unsigned int _intensity2pwm(unsigned int intensity) {
unsigned int pwm;
#if ENABLE_GAMMA_CORRECTION
pwm = (intensity < GAMMA_TABLE_SIZE) ? gamma_table[intensity] : LIGHT_PWM_RANGE;
#else
// Support integer multiples of 256 (-1) for the LIGHT_PWM_RANGE
// The divide should happen at compile time
pwm = intensity * ( (LIGHT_PWM_RANGE+1) / (LIGHT_MAX_VALUE+1) );
#endif
#if RGBW_INVERSE_LOGIC != 1
pwm = LIGHT_PWM_RANGE - pwm;
#endif
return pwm;
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// PROVIDER // PROVIDER
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void lightColorProvider(unsigned int red, unsigned int green, unsigned int blue) {
void _lightProviderSet(bool state, unsigned int red, unsigned int green, unsigned int blue) {
#if (LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW)
unsigned int white = 0;
unsigned int white = 0;
#if (LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB2W)
// If all set to the same value use white instead // If all set to the same value use white instead
if ((red == green) && (green == blue)) { if ((red == green) && (green == blue)) {
white = red; white = red;
@ -58,25 +175,25 @@ void lightColorProvider(unsigned int red, unsigned int green, unsigned int blue)
#endif #endif
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192 #if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
_my9291->setState(state);
_my9291->setColor((my9291_color_t) { red, green, blue, white }); _my9291->setColor((my9291_color_t) { red, green, blue, white });
#endif #endif
#if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW)
if (RGBW_INVERSE_LOGIC) {
analogWrite(RGBW_RED_PIN, red);
analogWrite(RGBW_GREEN_PIN, green);
analogWrite(RGBW_BLUE_PIN, blue);
#if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW)
analogWrite(RGBW_WHITE_PIN, white);
#endif
} else {
analogWrite(RGBW_RED_PIN, 255 - red);
analogWrite(RGBW_GREEN_PIN, 255 - green);
analogWrite(RGBW_BLUE_PIN, 255 - blue);
#if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW)
analogWrite(RGBW_WHITE_PIN, 255 - white);
#endif
}
#if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB2W)
// Check state
if (!state) red = green = blue = white = 0;
analogWrite(RGBW_RED_PIN, _intensity2pwm(red));
analogWrite(RGBW_GREEN_PIN, _intensity2pwm(green));
analogWrite(RGBW_BLUE_PIN, _intensity2pwm(blue));
#if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW)
analogWrite(RGBW_WHITE_PIN, _intensity2pwm(white));
#endif
#if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB2W)
analogWrite(RGBW_WHITE_PIN, _intensity2pwm(white));
analogWrite(RGBW_WHITE2_PIN, _intensity2pwm(white));
#endif
#endif #endif
} }
@ -85,13 +202,25 @@ void lightColorProvider(unsigned int red, unsigned int green, unsigned int blue)
// LIGHT MANAGEMENT // LIGHT MANAGEMENT
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void lightColor(const char * rgb, bool save, bool forward) {
void lightState(bool state) {
_lightState = state;
_lightProviderSet(_lightState, _lightColor[0], _lightColor[1], _lightColor[2]);
}
color_rgb2array(rgb, _lightColor);
lightColorProvider(_lightColor[0], _lightColor[1], _lightColor[2]);
bool lightState() {
return _lightState;
}
void lightColor(const char * color, bool save, bool forward) {
color_string2array(color, _lightColor);
_lightProviderSet(_lightState, _lightColor[0], _lightColor[1], _lightColor[2]);
char rgb[8];
color_array2rgb(_lightColor, rgb);
// Delay saving to EEPROM 5 seconds to avoid wearing it out unnecessarily // Delay saving to EEPROM 5 seconds to avoid wearing it out unnecessarily
if (save) colorTicker.once(LIGHT_SAVE_DELAY, lightColorSave);
if (save) colorTicker.once(LIGHT_SAVE_DELAY, _lightColorSave);
// Report color to MQTT broker // Report color to MQTT broker
if (forward) mqttSend(MQTT_TOPIC_COLOR, rgb); if (forward) mqttSend(MQTT_TOPIC_COLOR, rgb);
@ -109,30 +238,18 @@ String lightColor() {
return String(rgb); return String(rgb);
} }
void lightState(bool state) {
if (state) {
lightColorProvider(_lightColor[0], _lightColor[1], _lightColor[2]);
} else {
lightColorProvider(0, 0, 0);
}
_lightState = state;
}
bool lightState() {
return _lightState;
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// PERSISTANCE // PERSISTANCE
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void lightColorSave() {
void _lightColorSave() {
setSetting("color", lightColor()); setSetting("color", lightColor());
saveSettings(); saveSettings();
} }
void lightColorRetrieve() {
lightColor(getSetting("color", LIGHT_DEFAULT_COLOR).c_str(), false, true);
void _lightColorRestore() {
String color = getSetting("color", LIGHT_DEFAULT_COLOR);
color_string2array(color.c_str(), _lightColor);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -169,16 +286,31 @@ void lightSetup() {
#endif #endif
#if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW) #if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW)
analogWriteRange(LIGHT_PWM_RANGE);
analogWriteFreq(LIGHT_PWM_FREQUENCY);
pinMode(RGBW_RED_PIN, OUTPUT); pinMode(RGBW_RED_PIN, OUTPUT);
pinMode(RGBW_GREEN_PIN, OUTPUT); pinMode(RGBW_GREEN_PIN, OUTPUT);
pinMode(RGBW_BLUE_PIN, OUTPUT); pinMode(RGBW_BLUE_PIN, OUTPUT);
#if LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW #if LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW
pinMode(RGBW_WHITE_PIN, OUTPUT); pinMode(RGBW_WHITE_PIN, OUTPUT);
#endif #endif
lightColorProvider(0, 0, 0);
#if LIGHT_PROVIDER == LIGHT_PROVIDER_RGB2W
pinMode(RGBW_WHITE_PIN, OUTPUT);
pinMode(RGBW_WHITE2_PIN, OUTPUT);
#endif
#endif #endif
lightColorRetrieve();
_lightColorRestore();
// API entry points (protected with apikey)
apiRegister(MQTT_TOPIC_COLOR, MQTT_TOPIC_COLOR,
[](char * buffer, size_t len) {
snprintf(buffer, len, "%s", lightColor().c_str());
},
[](const char * payload) {
lightColor(payload, true, mqttForward());
}
);
mqttRegister(lightMQTTCallback); mqttRegister(lightMQTTCallback);


+ 8
- 4
code/espurna/mqtt.ino View File

@ -47,6 +47,7 @@ void buildTopics() {
// Replace identifier // Replace identifier
mqttTopic = getSetting("mqttTopic", MQTT_TOPIC); mqttTopic = getSetting("mqttTopic", MQTT_TOPIC);
mqttTopic.replace("{identifier}", getSetting("hostname")); mqttTopic.replace("{identifier}", getSetting("hostname"));
if (!mqttTopic.endsWith("/")) mqttTopic = mqttTopic + "/";
} }
bool mqttForward() { bool mqttForward() {
@ -165,6 +166,7 @@ void _mqttOnMessage(char* topic, char* payload, unsigned int len) {
String t = mqttSubtopic((char *) topic); String t = mqttSubtopic((char *) topic);
if (t.equals(MQTT_TOPIC_ACTION)) { if (t.equals(MQTT_TOPIC_ACTION)) {
if (strcmp(message, MQTT_ACTION_RESET) == 0) { if (strcmp(message, MQTT_ACTION_RESET) == 0) {
customReset(CUSTOM_RESET_MQTT);
ESP.restart(); ESP.restart();
} }
} }
@ -208,6 +210,7 @@ void mqttConnect() {
unsigned int port = getSetting("mqttPort", MQTT_PORT).toInt(); unsigned int port = getSetting("mqttPort", MQTT_PORT).toInt();
_mqttUser = strdup(getSetting("mqttUser").c_str()); _mqttUser = strdup(getSetting("mqttUser").c_str());
_mqttPass = strdup(getSetting("mqttPassword").c_str()); _mqttPass = strdup(getSetting("mqttPassword").c_str());
char * will = strdup((mqttTopic + MQTT_TOPIC_STATUS).c_str());
DEBUG_MSG_P(PSTR("[MQTT] Connecting to broker at %s:%d"), host, port); DEBUG_MSG_P(PSTR("[MQTT] Connecting to broker at %s:%d"), host, port);
mqtt.setServer(host, port); mqtt.setServer(host, port);
@ -215,7 +218,7 @@ void mqttConnect() {
#if MQTT_USE_ASYNC #if MQTT_USE_ASYNC
mqtt.setKeepAlive(MQTT_KEEPALIVE).setCleanSession(false); mqtt.setKeepAlive(MQTT_KEEPALIVE).setCleanSession(false);
mqtt.setWill((mqttTopic + MQTT_TOPIC_STATUS).c_str(), MQTT_QOS, MQTT_RETAIN, "0");
mqtt.setWill(will, MQTT_QOS, MQTT_RETAIN, "0");
if ((strlen(_mqttUser) > 0) && (strlen(_mqttPass) > 0)) { if ((strlen(_mqttUser) > 0) && (strlen(_mqttPass) > 0)) {
DEBUG_MSG_P(PSTR(" as user '%s'."), _mqttUser); DEBUG_MSG_P(PSTR(" as user '%s'."), _mqttUser);
mqtt.setCredentials(_mqttUser, _mqttPass); mqtt.setCredentials(_mqttUser, _mqttPass);
@ -233,10 +236,10 @@ void mqttConnect() {
if ((strlen(_mqttUser) > 0) && (strlen(_mqttPass) > 0)) { if ((strlen(_mqttUser) > 0) && (strlen(_mqttPass) > 0)) {
DEBUG_MSG_P(PSTR(" as user '%s'\n"), _mqttUser); DEBUG_MSG_P(PSTR(" as user '%s'\n"), _mqttUser);
response = mqtt.connect(getIdentifier().c_str(), _mqttUser, _mqttPass, (mqttTopic + MQTT_TOPIC_STATUS).c_str(), MQTT_QOS, MQTT_RETAIN, "0");
response = mqtt.connect(getIdentifier().c_str(), _mqttUser, _mqttPass, will, MQTT_QOS, MQTT_RETAIN, "0");
} else { } else {
DEBUG_MSG_P(PSTR("\n")); DEBUG_MSG_P(PSTR("\n"));
response = mqtt.connect(getIdentifier().c_str(), (mqttTopic + MQTT_TOPIC_STATUS).c_str(), MQTT_QOS, MQTT_RETAIN, "0");
response = mqtt.connect(getIdentifier().c_str(), will, MQTT_QOS, MQTT_RETAIN, "0");
} }
if (response) { if (response) {
@ -249,10 +252,11 @@ void mqttConnect() {
#endif #endif
free(host); free(host);
free(will);
String mqttSetter = getSetting("mqttSetter", MQTT_USE_SETTER); String mqttSetter = getSetting("mqttSetter", MQTT_USE_SETTER);
String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER); String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER);
bool _mqttForward = !mqttGetter.equals(mqttSetter);
_mqttForward = !mqttGetter.equals(mqttSetter);
} }


+ 1
- 1
code/espurna/nofuss.ino View File

@ -35,7 +35,7 @@ NoFUSSClient.onMessage([](nofuss_t code) {
} }
if (code == NOFUSS_UPDATING) { if (code == NOFUSS_UPDATING) {
DEBUG_MSG_P(PSTR("[NoFUSS] Updating");
DEBUG_MSG_P(PSTR("[NoFUSS] Updating"));
DEBUG_MSG_P(PSTR(" New version: %s\n"), (char *) NoFUSSClient.getNewVersion().c_str()); DEBUG_MSG_P(PSTR(" New version: %s\n"), (char *) NoFUSSClient.getNewVersion().c_str());
DEBUG_MSG_P(PSTR(" Firmware: %s\n"), (char *) NoFUSSClient.getNewFirmware().c_str()); DEBUG_MSG_P(PSTR(" Firmware: %s\n"), (char *) NoFUSSClient.getNewFirmware().c_str());
DEBUG_MSG_P(PSTR(" File System: %s"), (char *) NoFUSSClient.getNewFileSystem().c_str()); DEBUG_MSG_P(PSTR(" File System: %s"), (char *) NoFUSSClient.getNewFileSystem().c_str());


+ 13
- 1
code/espurna/ntp.ino View File

@ -15,10 +15,20 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void ntpConnect() { void ntpConnect() {
NTP.begin(NTP_SERVER, NTP_TIME_OFFSET, NTP_DAY_LIGHT);
NTP.begin(
getSetting("ntpServer1", NTP_SERVER),
getSetting("ntpOffset", NTP_TIME_OFFSET).toInt(),
getSetting("ntpDST", NTP_DAY_LIGHT).toInt() == 1
);
if (getSetting("ntpServer2")) NTP.setNtpServerName(getSetting("ntpServer2"), 1);
if (getSetting("ntpServer3")) NTP.setNtpServerName(getSetting("ntpServer3"), 2);
NTP.setInterval(NTP_UPDATE_INTERVAL); NTP.setInterval(NTP_UPDATE_INTERVAL);
} }
bool ntpConnected() {
return (timeStatus() == timeSet);
}
void ntpSetup() { void ntpSetup() {
NTP.onNTPSyncEvent([](NTPSyncEvent_t error) { NTP.onNTPSyncEvent([](NTPSyncEvent_t error) {
@ -28,8 +38,10 @@ void ntpSetup() {
} else if (error == invalidAddress) { } else if (error == invalidAddress) {
DEBUG_MSG_P(PSTR("[NTP] Error: Invalid NTP server address\n")); DEBUG_MSG_P(PSTR("[NTP] Error: Invalid NTP server address\n"));
} }
wsSend("{\"ntpStatus\": false}");
} else { } 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 *) NTP.getTimeDateString(NTP.getLastNTPSync()).c_str());
wsSend("{\"ntpStatus\": true}");
} }
}); });


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

@ -28,6 +28,7 @@ void otaSetup() {
}); });
ArduinoOTA.onEnd([]() { ArduinoOTA.onEnd([]() {
customReset(CUSTOM_RESET_OTA);
DEBUG_MSG_P(PSTR("\n[OTA] End\n")); DEBUG_MSG_P(PSTR("\n[OTA] End\n"));
wsSend("{\"action\": \"reload\"}"); wsSend("{\"action\": \"reload\"}");
delay(100); delay(100);


+ 74
- 22
code/espurna/pow.ino View File

@ -23,11 +23,11 @@ bool _powEnabled = false;
// When using interrupts we have to call the library entry point // When using interrupts we have to call the library entry point
// whenever an interrupt is triggered // whenever an interrupt is triggered
void hlw8012_cf1_interrupt() {
void ICACHE_RAM_ATTR hlw8012_cf1_interrupt() {
hlw8012.cf1_interrupt(); hlw8012.cf1_interrupt();
} }
void hlw8012_cf_interrupt() {
void ICACHE_RAM_ATTR hlw8012_cf_interrupt() {
hlw8012.cf_interrupt(); hlw8012.cf_interrupt();
} }
@ -95,27 +95,35 @@ void powReset() {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
unsigned int getActivePower() { unsigned int getActivePower() {
return hlw8012.getActivePower();
unsigned int power = hlw8012.getActivePower();
if (POW_MIN_POWER > power || power > POW_MAX_POWER) power = 0;
return power;
} }
unsigned int getApparentPower() { unsigned int getApparentPower() {
return hlw8012.getApparentPower();
unsigned int power = hlw8012.getApparentPower();
if (POW_MIN_POWER > power || power > POW_MAX_POWER) power = 0;
return power;
} }
unsigned int getReactivePower() { unsigned int getReactivePower() {
return hlw8012.getReactivePower();
unsigned int power = hlw8012.getReactivePower();
if (POW_MIN_POWER > power || power > POW_MAX_POWER) power = 0;
return power;
} }
double getCurrent() { double getCurrent() {
return hlw8012.getCurrent();
double current = hlw8012.getCurrent();
if (POW_MIN_CURRENT > current || current > POW_MAX_CURRENT) current = 0;
return current;
} }
unsigned int getVoltage() { unsigned int getVoltage() {
return hlw8012.getVoltage(); return hlw8012.getVoltage();
} }
unsigned int getPowerFactor() {
return (int) (100 * hlw8012.getPowerFactor());
double getPowerFactor() {
return hlw8012.getPowerFactor();
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -145,13 +153,13 @@ void powSetup() {
powRetrieveCalibration(); powRetrieveCalibration();
// API definitions // API definitions
apiRegister("/api/power", "power", [](char * buffer, size_t len) {
apiRegister(POW_POWER_TOPIC, POW_POWER_TOPIC, [](char * buffer, size_t len) {
snprintf(buffer, len, "%d", getActivePower()); snprintf(buffer, len, "%d", getActivePower());
}); });
apiRegister("/api/current", "current", [](char * buffer, size_t len) {
dtostrf(getCurrent(), len-1, 2, buffer);
apiRegister(POW_CURRENT_TOPIC, POW_CURRENT_TOPIC, [](char * buffer, size_t len) {
dtostrf(getCurrent(), len-1, 3, buffer);
}); });
apiRegister("/api/voltage", "voltage", [](char * buffer, size_t len) {
apiRegister(POW_VOLTAGE_TOPIC, POW_VOLTAGE_TOPIC, [](char * buffer, size_t len) {
snprintf(buffer, len, "%d", getVoltage()); snprintf(buffer, len, "%d", getVoltage());
}); });
@ -162,9 +170,18 @@ void powLoop() {
static unsigned long last_update = 0; static unsigned long last_update = 0;
static unsigned char report_count = POW_REPORT_EVERY; static unsigned char report_count = POW_REPORT_EVERY;
static bool power_spike = false;
static unsigned long power_sum = 0; static unsigned long power_sum = 0;
static unsigned long power_previous = 0;
static bool current_spike = false;
static double current_sum = 0; static double current_sum = 0;
static double current_previous = 0;
static bool voltage_spike = false;
static unsigned long voltage_sum = 0; static unsigned long voltage_sum = 0;
static unsigned long voltage_previous = 0;
static bool powWasEnabled = false; static bool powWasEnabled = false;
// POW is disabled while there is no internet connection // POW is disabled while there is no internet connection
@ -186,23 +203,43 @@ void powLoop() {
unsigned int voltage = getVoltage(); unsigned int voltage = getVoltage();
double current = getCurrent(); double current = getCurrent();
unsigned int apparent = getApparentPower(); unsigned int apparent = getApparentPower();
unsigned int factor = getPowerFactor();
double factor = getPowerFactor();
unsigned int reactive = getReactivePower(); unsigned int reactive = getReactivePower();
power_sum += power;
current_sum += current;
voltage_sum += voltage;
if (power > 0) {
power_spike = (power_previous == 0);
} else if (power_spike) {
power_sum -= power_previous;
power_spike = false;
}
power_previous = power;
if (current > 0) {
current_spike = (current_previous == 0);
} else if (current_spike) {
current_sum -= current_previous;
current_spike = false;
}
current_previous = current;
if (voltage > 0) {
voltage_spike = (voltage_previous == 0);
} else if (voltage_spike) {
voltage_sum -= voltage_previous;
voltage_spike = false;
}
voltage_previous = voltage;
DynamicJsonBuffer jsonBuffer; DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject(); JsonObject& root = jsonBuffer.createObject();
root["powVisible"] = 1; root["powVisible"] = 1;
root["powActivePower"] = power; root["powActivePower"] = power;
root["powCurrent"] = current;
root["powCurrent"] = String(current, 3);
root["powVoltage"] = voltage; root["powVoltage"] = voltage;
root["powApparentPower"] = apparent; root["powApparentPower"] = apparent;
root["powReactivePower"] = reactive; root["powReactivePower"] = reactive;
root["powPowerFactor"] = factor;
root["powPowerFactor"] = String(factor, 2);
String output; String output;
root.printTo(output); root.printTo(output);
@ -215,8 +252,8 @@ void powLoop() {
voltage = voltage_sum / POW_REPORT_EVERY; voltage = voltage_sum / POW_REPORT_EVERY;
apparent = current * voltage; apparent = current * voltage;
reactive = (apparent > power) ? sqrt(apparent * apparent - power * power) : 0; reactive = (apparent > power) ? sqrt(apparent * apparent - power * power) : 0;
factor = (apparent > 0) ? 100 * power / apparent : 100;
if (factor > 100) factor = 100;
factor = (apparent > 0) ? (double) power / apparent : 1;
if (factor > 1) factor = 1;
// Calculate energy increment (ppower times time) and create C-string // 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; double energy_inc = (double) power * POW_REPORT_EVERY * POW_UPDATE_INTERVAL / 1000.0 / 3600.0;
@ -228,11 +265,11 @@ void powLoop() {
// Report values to MQTT broker // Report values to MQTT broker
mqttSend(getSetting("powPowerTopic", POW_POWER_TOPIC).c_str(), String(power).c_str()); mqttSend(getSetting("powPowerTopic", POW_POWER_TOPIC).c_str(), String(power).c_str());
mqttSend(getSetting("powEnergyTopic", POW_ENERGY_TOPIC).c_str(), e); mqttSend(getSetting("powEnergyTopic", POW_ENERGY_TOPIC).c_str(), e);
mqttSend(getSetting("powCurrentTopic", POW_CURRENT_TOPIC).c_str(), String(current).c_str());
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("powVoltageTopic", POW_VOLTAGE_TOPIC).c_str(), String(voltage).c_str());
mqttSend(getSetting("powAPowerTopic", POW_APOWER_TOPIC).c_str(), String(apparent).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("powRPowerTopic", POW_RPOWER_TOPIC).c_str(), String(reactive).c_str());
mqttSend(getSetting("powPFactorTopic", POW_PFACTOR_TOPIC).c_str(), String(factor).c_str());
mqttSend(getSetting("powPFactorTopic", POW_PFACTOR_TOPIC).c_str(), String(factor, 2).c_str());
// Report values to Domoticz // Report values to Domoticz
#if ENABLE_DOMOTICZ #if ENABLE_DOMOTICZ
@ -249,12 +286,27 @@ void powLoop() {
} }
#endif #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());
#endif
// Reset counters // Reset counters
power_sum = current_sum = voltage_sum = 0; power_sum = current_sum = voltage_sum = 0;
report_count = POW_REPORT_EVERY; report_count = POW_REPORT_EVERY;
} }
// Post - Accumulators
power_sum += power_previous;
current_sum += current_previous;
voltage_sum += voltage_previous;
// Toggle between current and voltage monitoring // Toggle between current and voltage monitoring
#if POW_USE_INTERRUPTS == 0 #if POW_USE_INTERRUPTS == 0
hlw8012.toggleMode(); hlw8012.toggleMode();


+ 93
- 32
code/espurna/relay.ino View File

@ -16,9 +16,14 @@ typedef struct {
unsigned char pin; unsigned char pin;
bool reverse; bool reverse;
unsigned char led; unsigned char led;
unsigned int floodWindowStart;
unsigned char floodWindowChanges;
unsigned int scheduledStatusTime;
bool scheduledStatus;
bool scheduledReport;
Ticker pulseTicker;
} relay_t; } relay_t;
std::vector<relay_t> _relays; std::vector<relay_t> _relays;
Ticker pulseTicker;
bool recursive = false; bool recursive = false;
#if RELAY_PROVIDER == RELAY_PROVIDER_DUAL #if RELAY_PROVIDER == RELAY_PROVIDER_DUAL
@ -31,6 +36,8 @@ unsigned char _dual_status = 0;
void relayProviderStatus(unsigned char id, bool status) { void relayProviderStatus(unsigned char id, bool status) {
if (id >= _relays.size()) return;
#if RELAY_PROVIDER == RELAY_PROVIDER_DUAL #if RELAY_PROVIDER == RELAY_PROVIDER_DUAL
_dual_status ^= (1 << id); _dual_status ^= (1 << id);
Serial.flush(); Serial.flush();
@ -53,8 +60,9 @@ void relayProviderStatus(unsigned char id, bool status) {
bool relayProviderStatus(unsigned char id) { bool relayProviderStatus(unsigned char id) {
if (id >= _relays.size()) return false;
#if RELAY_PROVIDER == RELAY_PROVIDER_DUAL #if RELAY_PROVIDER == RELAY_PROVIDER_DUAL
if (id >= 2) return false;
return ((_dual_status & (1 << id)) > 0); return ((_dual_status & (1 << id)) > 0);
#endif #endif
@ -63,7 +71,6 @@ bool relayProviderStatus(unsigned char id) {
#endif #endif
#if RELAY_PROVIDER == RELAY_PROVIDER_RELAY #if RELAY_PROVIDER == RELAY_PROVIDER_RELAY
if (id >= _relays.size()) return false;
bool status = (digitalRead(_relays[id].pin) == HIGH); bool status = (digitalRead(_relays[id].pin) == HIGH);
return _relays[id].reverse ? !status : status; return _relays[id].reverse ? !status : status;
#endif #endif
@ -94,19 +101,17 @@ void relayPulse(unsigned char id) {
byte relayPulseMode = getSetting("relayPulseMode", RELAY_PULSE_MODE).toInt(); byte relayPulseMode = getSetting("relayPulseMode", RELAY_PULSE_MODE).toInt();
if (relayPulseMode == RELAY_PULSE_NONE) return; if (relayPulseMode == RELAY_PULSE_NONE) return;
long relayPulseTime = 1000.0 * getSetting("relayPulseTime", RELAY_PULSE_TIME).toFloat();
if (relayPulseTime == 0) return;
bool status = relayStatus(id); bool status = relayStatus(id);
bool pulseStatus = (relayPulseMode == RELAY_PULSE_ON); bool pulseStatus = (relayPulseMode == RELAY_PULSE_ON);
if (pulseStatus == status) { if (pulseStatus == status) {
pulseTicker.detach();
_relays[id].pulseTicker.detach();
return; return;
} }
pulseTicker.once(
getSetting("relayPulseTime", RELAY_PULSE_TIME).toInt(),
relayToggle,
id
);
_relays[id].pulseTicker.once_ms(relayPulseTime, relayToggle, id);
} }
@ -153,31 +158,31 @@ bool relayStatus(unsigned char id, bool status, bool report) {
if (relayStatus(id) != status) { if (relayStatus(id) != status) {
DEBUG_MSG_P(PSTR("[RELAY] %d => %s\n"), id, status ? "ON" : "OFF");
changed = true;
unsigned int floodWindowEnd = _relays[id].floodWindowStart + 1000 * RELAY_FLOOD_WINDOW;
unsigned int currentTime = millis();
relayProviderStatus(id, status);
_relays[id].floodWindowChanges++;
_relays[id].scheduledStatusTime = currentTime;
if (_relays[id].led > 0) {
ledStatus(_relays[id].led - 1, status);
if (currentTime >= floodWindowEnd || currentTime < _relays[id].floodWindowStart) {
_relays[id].floodWindowStart = currentTime;
_relays[id].floodWindowChanges = 1;
} else if (_relays[id].floodWindowChanges >= RELAY_FLOOD_CHANGES) {
_relays[id].scheduledStatusTime = floodWindowEnd;
} }
if (report) relayMQTT(id);
if (!recursive) {
relayPulse(id);
relaySync(id);
relaySave();
relayWS();
}
_relays[id].scheduledStatus = status;
_relays[id].scheduledReport = (report ? true : _relays[id].scheduledReport);
#if ENABLE_DOMOTICZ
relayDomoticzSend(id);
#endif
DEBUG_MSG_P(PSTR("[RELAY] Scheduled %d => %s in %u ms\n"),
id, status ? "ON" : "OFF",
(_relays[id].scheduledStatusTime - currentTime));
changed = true;
} }
return changed; return changed;
} }
bool relayStatus(unsigned char id, bool status) { bool relayStatus(unsigned char id, bool status) {
@ -229,6 +234,7 @@ void relaySave() {
bit += bit; bit += bit;
} }
EEPROM.write(EEPROM_RELAY_STATUS, mask); EEPROM.write(EEPROM_RELAY_STATUS, mask);
DEBUG_MSG_P(PSTR("[RELAY] Saving mask: %d\n"), mask);
EEPROM.commit(); EEPROM.commit();
} }
@ -236,8 +242,10 @@ void relayRetrieve(bool invert) {
recursive = true; recursive = true;
unsigned char bit = 1; unsigned char bit = 1;
unsigned char mask = invert ? ~EEPROM.read(EEPROM_RELAY_STATUS) : EEPROM.read(EEPROM_RELAY_STATUS); unsigned char mask = invert ? ~EEPROM.read(EEPROM_RELAY_STATUS) : EEPROM.read(EEPROM_RELAY_STATUS);
for (unsigned int i=0; i < _relays.size(); i++) {
relayStatus(i, ((mask & bit) == bit));
DEBUG_MSG_P(PSTR("[RELAY] Retrieving mask: %d\n"), mask);
for (unsigned int id=0; id < _relays.size(); id++) {
_relays[id].scheduledStatus = ((mask & bit) == bit);
_relays[id].scheduledReport = true;
bit += bit; bit += bit;
} }
if (invert) { if (invert) {
@ -266,10 +274,10 @@ void relaySetupAPI() {
for (unsigned int relayID=0; relayID<relayCount(); relayID++) { for (unsigned int relayID=0; relayID<relayCount(); relayID++) {
char url[15]; char url[15];
sprintf(url, "/api/relay/%d", relayID);
sprintf(url, "%s/%d", MQTT_TOPIC_RELAY, relayID);
char key[10]; char key[10];
sprintf(key, "relay%d", relayID);
sprintf(key, "%s%d", MQTT_TOPIC_RELAY, relayID);
apiRegister(url, key, apiRegister(url, key,
[relayID](char * buffer, size_t len) { [relayID](char * buffer, size_t len) {
@ -376,6 +384,15 @@ void relayMQTT(unsigned char id) {
mqttSend(MQTT_TOPIC_RELAY, id, relayStatus(id) ? "1" : "0"); 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() { void relayMQTT() {
for (unsigned int i=0; i < _relays.size(); i++) { for (unsigned int i=0; i < _relays.size(); i++) {
relayMQTT(i); relayMQTT(i);
@ -439,15 +456,15 @@ void relaySetupMQTT() {
void relaySetup() { void relaySetup() {
#ifdef SONOFF_DUAL
#if defined(SONOFF_DUAL)
// Two dummy relays for the 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}); _relays.push_back((relay_t) {0, 0});
#elif AI_LIGHT
#elif defined(AI_LIGHT) | defined(LED_CONTROLLER) | defined(H801_LED_CONTROLLER)
// One dummy relay for the AI Thinker Light
// 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});
#else #else
@ -475,6 +492,7 @@ void relaySetup() {
} }
if (relayMode == RELAY_MODE_SAME) relayRetrieve(false); if (relayMode == RELAY_MODE_SAME) relayRetrieve(false);
if (relayMode == RELAY_MODE_TOOGLE) relayRetrieve(true); if (relayMode == RELAY_MODE_TOOGLE) relayRetrieve(true);
relayLoop();
relaySetupAPI(); relaySetupAPI();
relaySetupMQTT(); relaySetupMQTT();
@ -485,3 +503,46 @@ void relaySetup() {
DEBUG_MSG_P(PSTR("[RELAY] Number of relays: %d\n"), _relays.size()); DEBUG_MSG_P(PSTR("[RELAY] Number of relays: %d\n"), _relays.size());
} }
void relayLoop(void) {
unsigned char id;
for (id = 0; id < _relays.size(); id++) {
unsigned int currentTime = millis();
bool status = _relays[id].scheduledStatus;
if (relayStatus(id) != status && currentTime >= _relays[id].scheduledStatusTime) {
DEBUG_MSG_P(PSTR("[RELAY] %d => %s\n"), id, status ? "ON" : "OFF");
relayProviderStatus(id, status);
if (_relays[id].led > 0) {
ledStatus(_relays[id].led - 1, status);
}
if (_relays[id].scheduledReport) relayMQTT(id);
if (!recursive) {
relayPulse(id);
relaySync(id);
relaySave();
relayWS();
}
#if ENABLE_DOMOTICZ
relayDomoticzSend(id);
#endif
#if ENABLE_INFLUXDB
relayInfluxDB(id);
#endif
_relays[id].scheduledReport = false;
}
}
}

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

@ -96,6 +96,7 @@ void settingsSetup() {
Embedis::command( F("RESET"), [](Embedis* e) { Embedis::command( F("RESET"), [](Embedis* e) {
e->response(Embedis::OK); e->response(Embedis::OK);
customReset(CUSTOM_RESET_TERMINAL);
ESP.restart(); ESP.restart();
}); });
@ -172,12 +173,22 @@ void settingsLoop() {
embedis.process(); embedis.process();
} }
void moveSetting(const char * from, const char * to) {
String value = getSetting(from);
if (value.length() > 0) setSetting(to, value);
delSetting(from);
}
template<typename T> String getSetting(const String& key, T defaultValue) { template<typename T> String getSetting(const String& key, T defaultValue) {
String value; String value;
if (!Embedis::get(key, value)) value = String(defaultValue); if (!Embedis::get(key, value)) value = String(defaultValue);
return value; return value;
} }
template<typename T> String getSetting(const String& key, unsigned int index, T defaultValue) {
return getSetting(key + String(index), defaultValue);
}
String getSetting(const String& key) { String getSetting(const String& key) {
return getSetting(key, ""); return getSetting(key, "");
} }
@ -186,10 +197,26 @@ template<typename T> bool setSetting(const String& key, T value) {
return Embedis::set(key, String(value)); return Embedis::set(key, String(value));
} }
template<typename T> bool setSetting(const String& key, unsigned int index, T value) {
return setSetting(key + String(index), value);
}
bool delSetting(const String& key) { bool delSetting(const String& key) {
return Embedis::del(key); return Embedis::del(key);
} }
bool delSetting(const String& key, unsigned int index) {
return delSetting(key + String(index));
}
bool hasSetting(const String& key) {
return getSetting(key).length() != 0;
}
bool hasSetting(const String& key, unsigned int index) {
return getSetting(key, index, "").length() != 0;
}
void saveSettings() { void saveSettings() {
DEBUG_MSG_P(PSTR("[SETTINGS] Saving\n")); DEBUG_MSG_P(PSTR("[SETTINGS] Saving\n"));
#if not AUTO_SAVE #if not AUTO_SAVE


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


+ 104
- 8
code/espurna/web.ino View File

@ -86,6 +86,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
DEBUG_MSG_P(PSTR("[WEBSOCKET] Requested action: %s\n"), action.c_str()); DEBUG_MSG_P(PSTR("[WEBSOCKET] Requested action: %s\n"), action.c_str());
if (action.equals("reset")) { if (action.equals("reset")) {
customReset(CUSTOM_RESET_WEB);
ESP.restart(); ESP.restart();
} }
@ -159,7 +160,9 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
bool save = false; bool save = false;
bool changed = false; bool changed = false;
bool changedMQTT = false; bool changedMQTT = false;
bool changedNTP = false;
bool apiEnabled = false; bool apiEnabled = false;
bool dstEnabled = false;
#if ENABLE_FAUXMO #if ENABLE_FAUXMO
bool fauxmoEnabled = false; bool fauxmoEnabled = false;
#endif #endif
@ -172,6 +175,9 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
String key = config[i]["name"]; String key = config[i]["name"];
String value = config[i]["value"]; String value = config[i]["value"];
// Skip firmware filename
if (key.equals("filename")) continue;
#if ENABLE_POW #if ENABLE_POW
if (key == "powExpectedPower") { if (key == "powExpectedPower") {
@ -185,7 +191,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
} }
if (key == "powExpectedCurrent") { if (key == "powExpectedCurrent") {
powSetExpectedCurrent(value.toInt());
powSetExpectedCurrent(value.toFloat());
changed = true; changed = true;
} }
@ -246,6 +252,10 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
apiEnabled = true; apiEnabled = true;
continue; continue;
} }
if (key == "ntpDST") {
dstEnabled = true;
continue;
}
#if ENABLE_FAUXMO #if ENABLE_FAUXMO
if (key == "fauxmoEnabled") { if (key == "fauxmoEnabled") {
fauxmoEnabled = true; fauxmoEnabled = true;
@ -278,6 +288,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
setSetting(key, value); setSetting(key, value);
save = changed = true; save = changed = true;
if (key.startsWith("mqtt")) changedMQTT = true; if (key.startsWith("mqtt")) changedMQTT = true;
if (key.startsWith("ntp")) changedNTP = true;
} }
} }
@ -289,6 +300,10 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
setSetting("apiEnabled", apiEnabled); setSetting("apiEnabled", apiEnabled);
save = changed = true; save = changed = true;
} }
if (dstEnabled != (getSetting("ntpDST").toInt() == 1)) {
setSetting("ntpDST", dstEnabled);
save = changed = changedNTP = true;
}
#if ENABLE_FAUXMO #if ENABLE_FAUXMO
if (fauxmoEnabled != (getSetting("fauxmoEnabled").toInt() == 1)) { if (fauxmoEnabled != (getSetting("fauxmoEnabled").toInt() == 1)) {
setSetting("fauxmoEnabled", fauxmoEnabled); setSetting("fauxmoEnabled", fauxmoEnabled);
@ -334,6 +349,9 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
#if ENABLE_FAUXMO #if ENABLE_FAUXMO
fauxmoConfigure(); fauxmoConfigure();
#endif #endif
#if ENABLE_INFLUXDB
influxDBConfigure();
#endif
buildTopics(); buildTopics();
#if ENABLE_RF #if ENABLE_RF
@ -348,6 +366,12 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
if (changedMQTT) { if (changedMQTT) {
mqttDisconnect(); mqttDisconnect();
} }
// Check if we should reconfigure NTP connection
if (changedNTP) {
ntpConnect();
}
} }
if (changed) { if (changed) {
@ -395,6 +419,13 @@ void _wsStart(uint32_t client_id) {
root["network"] = getNetwork(); root["network"] = getNetwork();
root["deviceip"] = getIP(); root["deviceip"] = getIP();
root["ntpStatus"] = ntpConnected();
root["ntpServer1"] = getSetting("ntpServer1", NTP_SERVER);
root["ntpServer2"] = getSetting("ntpServer2");
root["ntpServer3"] = getSetting("ntpServer3");
root["ntpOffset"] = getSetting("ntpOffset", NTP_TIME_OFFSET).toInt();
root["ntpDST"] = getSetting("ntpDST", NTP_DAY_LIGHT).toInt() == 1;
root["mqttStatus"] = mqttConnected(); root["mqttStatus"] = mqttConnected();
root["mqttServer"] = getSetting("mqttServer", MQTT_SERVER); root["mqttServer"] = getSetting("mqttServer", MQTT_SERVER);
root["mqttPort"] = getSetting("mqttPort", MQTT_PORT); root["mqttPort"] = getSetting("mqttPort", MQTT_PORT);
@ -414,7 +445,7 @@ void _wsStart(uint32_t client_id) {
root["relayMode"] = getSetting("relayMode", RELAY_MODE); root["relayMode"] = getSetting("relayMode", RELAY_MODE);
root["relayPulseMode"] = getSetting("relayPulseMode", RELAY_PULSE_MODE); root["relayPulseMode"] = getSetting("relayPulseMode", RELAY_PULSE_MODE);
root["relayPulseTime"] = getSetting("relayPulseTime", RELAY_PULSE_TIME);
root["relayPulseTime"] = getSetting("relayPulseTime", RELAY_PULSE_TIME).toFloat();
if (relayCount() > 1) { if (relayCount() > 1) {
root["multirelayVisible"] = 1; root["multirelayVisible"] = 1;
root["relaySync"] = getSetting("relaySync", RELAY_SYNC); root["relaySync"] = getSetting("relaySync", RELAY_SYNC);
@ -453,6 +484,10 @@ void _wsStart(uint32_t client_id) {
root["dczCurrentIdx"] = getSetting("dczCurrentIdx").toInt(); root["dczCurrentIdx"] = getSetting("dczCurrentIdx").toInt();
#endif #endif
#if ENABLE_ANALOG
root["dczAnaIdx"] = getSetting("dczAnaIdx").toInt();
#endif
#if ENABLE_POW #if ENABLE_POW
root["dczPowIdx"] = getSetting("dczPowIdx").toInt(); root["dczPowIdx"] = getSetting("dczPowIdx").toInt();
root["dczEnergyIdx"] = getSetting("dczEnergyIdx").toInt(); root["dczEnergyIdx"] = getSetting("dczEnergyIdx").toInt();
@ -462,6 +497,15 @@ void _wsStart(uint32_t client_id) {
#endif #endif
#if ENABLE_INFLUXDB
root["idbVisible"] = 1;
root["idbHost"] = getSetting("idbHost");
root["idbPort"] = getSetting("idbPort", INFLUXDB_PORT).toInt();
root["idbDatabase"] = getSetting("idbDatabase");
root["idbUsername"] = getSetting("idbUsername");
root["idbPassword"] = getSetting("idbPassword");
#endif
#if ENABLE_FAUXMO #if ENABLE_FAUXMO
root["fauxmoVisible"] = 1; root["fauxmoVisible"] = 1;
root["fauxmoEnabled"] = getSetting("fauxmoEnabled", FAUXMO_ENABLED).toInt() == 1; root["fauxmoEnabled"] = getSetting("fauxmoEnabled", FAUXMO_ENABLED).toInt() == 1;
@ -491,14 +535,19 @@ void _wsStart(uint32_t client_id) {
root["emonRatio"] = getSetting("emonRatio", EMON_CURRENT_RATIO); root["emonRatio"] = getSetting("emonRatio", EMON_CURRENT_RATIO);
#endif #endif
#if ENABLE_ANALOG
root["analogVisible"] = 1;
root["analogValue"] = getAnalog();
#endif
#if ENABLE_POW #if ENABLE_POW
root["powVisible"] = 1; root["powVisible"] = 1;
root["powActivePower"] = getActivePower(); root["powActivePower"] = getActivePower();
root["powApparentPower"] = getApparentPower(); root["powApparentPower"] = getApparentPower();
root["powReactivePower"] = getReactivePower(); root["powReactivePower"] = getReactivePower();
root["powVoltage"] = getVoltage(); root["powVoltage"] = getVoltage();
root["powCurrent"] = getCurrent();
root["powPowerFactor"] = getPowerFactor();
root["powCurrent"] = String(getCurrent(), 3);
root["powPowerFactor"] = String(getPowerFactor(), 2);
#endif #endif
root["maxNetworks"] = WIFI_MAX_NETWORKS; root["maxNetworks"] = WIFI_MAX_NETWORKS;
@ -672,7 +721,9 @@ void apiRegister(const char * url, const char * key, apiGetCallbackFunction getF
// Store it // Store it
web_api_t api; web_api_t api;
api.url = strdup(url);
char buffer[40];
snprintf(buffer, 39, "/api/%s", url);
api.url = strdup(buffer);
api.key = strdup(key); api.key = strdup(key);
api.getFn = getFn; api.getFn = getFn;
api.putFn = putFn; api.putFn = putFn;
@ -681,7 +732,7 @@ void apiRegister(const char * url, const char * key, apiGetCallbackFunction getF
// Bind call // Bind call
unsigned int methods = HTTP_GET; unsigned int methods = HTTP_GET;
if (putFn != NULL) methods += HTTP_PUT; if (putFn != NULL) methods += HTTP_PUT;
_server->on(url, methods, _bindAPI(_apis.size() - 1));
_server->on(buffer, methods, _bindAPI(_apis.size() - 1));
} }
@ -729,7 +780,10 @@ void _onRPC(AsyncWebServerRequest *request) {
if (action.equals("reset")) { if (action.equals("reset")) {
response = 200; response = 200;
deferred.once_ms(100, []() { ESP.restart(); });
deferred.once_ms(100, []() {
customReset(CUSTOM_RESET_RPC);
ESP.restart();
});
} }
} }
@ -806,9 +860,50 @@ void _onHome(AsyncWebServerRequest *request) {
} }
} }
#endif #endif
void _onUpgrade(AsyncWebServerRequest *request) {
AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", Update.hasError() ? "FAIL" : "OK");
response->addHeader("Connection", "close");
if (!Update.hasError()) {
deferred.once_ms(100, []() {
customReset(CUSTOM_RESET_UPGRADE);
ESP.restart();
});
}
request->send(response);
}
void _onUpgradeData(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
if (!index) {
DEBUG_MSG_P(PSTR("[UPGRADE] Start: %s\n"), filename.c_str());
Update.runAsync(true);
if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)) {
#ifdef DEBUG_PORT
Update.printError(DEBUG_PORT);
#endif
}
}
if (!Update.hasError()) {
if (Update.write(data, len) != len) {
#ifdef DEBUG_PORT
Update.printError(DEBUG_PORT);
#endif
}
}
if (final) {
if (Update.end(true)){
DEBUG_MSG_P(PSTR("[UPGRADE] Success: %u bytes\n"), index + len);
} else {
#ifdef DEBUG_PORT
Update.printError(DEBUG_PORT);
#endif
}
} else {
DEBUG_MSG_P(PSTR("[UPGRADE] Progress: %u bytes\r"), index + len);
}
}
void webSetup() { void webSetup() {
// Create server // Create server
@ -835,6 +930,7 @@ void webSetup() {
_server->on("/auth", HTTP_GET, _onAuth); _server->on("/auth", HTTP_GET, _onAuth);
_server->on("/apis", HTTP_GET, _onAPIs); _server->on("/apis", HTTP_GET, _onAPIs);
_server->on("/rpc", HTTP_GET, _onRPC); _server->on("/rpc", HTTP_GET, _onRPC);
_server->on("/upgrade", HTTP_POST, _onUpgrade, _onUpgradeData);
// Serve static files // Serve static files
#if not EMBEDDED_WEB #if not EMBEDDED_WEB


+ 2
- 1
code/espurna/wifi.ino View File

@ -116,6 +116,7 @@ void wifiStatus() {
void wifiSetup() { void wifiSetup() {
WiFi.persistent(false);
wifiConfigure(); wifiConfigure();
// Message callbacks // Message callbacks
@ -181,7 +182,7 @@ void wifiSetup() {
#if ENABLE_MDNS #if ENABLE_MDNS
if (code == MESSAGE_CONNECTED || code == MESSAGE_ACCESSPOINT_CREATED) { if (code == MESSAGE_CONNECTED || code == MESSAGE_ACCESSPOINT_CREATED) {
if (MDNS.begin(WiFi.getMode() == WIFI_AP ? APP_NAME : (char *) WiFi.hostname().c_str())) { if (MDNS.begin(WiFi.getMode() == WIFI_AP ? APP_NAME : (char *) WiFi.hostname().c_str())) {
MDNS.addService("http", "tcp", 80);
MDNS.addService("http", "tcp", getSetting("webPort", WEBSERVER_PORT).toInt());
DEBUG_MSG_P(PSTR("[MDNS] OK\n")); DEBUG_MSG_P(PSTR("[MDNS] OK\n"));
} else { } else {
DEBUG_MSG_P(PSTR("[MDNS] FAIL\n")); DEBUG_MSG_P(PSTR("[MDNS] FAIL\n"));


+ 4
- 21
code/gulpfile.js View File

@ -25,19 +25,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
const fs = require('fs'); const fs = require('fs');
const gulp = require('gulp'); const gulp = require('gulp');
const plumber = require('gulp-plumber');
const htmlmin = require('gulp-htmlmin'); const htmlmin = require('gulp-htmlmin');
const cleancss = require('gulp-clean-css'); const cleancss = require('gulp-clean-css');
const uglify = require('gulp-uglify'); const uglify = require('gulp-uglify');
const gzip = require('gulp-gzip'); const gzip = require('gulp-gzip');
const del = require('del'); const del = require('del');
const useref = require('gulp-useref');
const gulpif = require('gulp-if');
const inline = require('gulp-inline'); const inline = require('gulp-inline');
const inlineImages = require('gulp-css-base64'); const inlineImages = require('gulp-css-base64');
const favicon = require('gulp-base64-favicon'); const favicon = require('gulp-base64-favicon');
const dataFolder = 'espurna/data/';
const dataFolder = 'espurna/static/';
gulp.task('clean', function() { gulp.task('clean', function() {
del([ dataFolder + '*']); del([ dataFolder + '*']);
@ -55,7 +52,7 @@ gulp.task('files', ['clean'], function() {
gulp.task('buildfs_embeded', ['buildfs_inline'], function() { gulp.task('buildfs_embeded', ['buildfs_inline'], function() {
var source = dataFolder + 'index.html.gz'; var source = dataFolder + 'index.html.gz';
var destination = dataFolder + '../static/index.html.gz.h';
var destination = dataFolder + 'index.html.gz.h';
var wstream = fs.createWriteStream(destination); var wstream = fs.createWriteStream(destination);
wstream.on('error', function (err) { wstream.on('error', function (err) {
@ -76,6 +73,8 @@ gulp.task('buildfs_embeded', ['buildfs_inline'], function() {
wstream.write('\n};') wstream.write('\n};')
wstream.end(); wstream.end();
del([source]);
}); });
gulp.task('buildfs_inline', ['clean'], function() { gulp.task('buildfs_inline', ['clean'], function() {
@ -97,20 +96,4 @@ gulp.task('buildfs_inline', ['clean'], function() {
.pipe(gulp.dest(dataFolder)); .pipe(gulp.dest(dataFolder));
}) })
gulp.task('buildfs_split', ['files'], function() {
return gulp.src('html/*.html')
.pipe(useref())
.pipe(plumber())
.pipe(gulpif('*.css', cleancss()))
.pipe(gulpif('*.js', uglify()))
.pipe(gulpif('*.html', htmlmin({
collapseWhitespace: true,
removeComments: true,
minifyCSS: true,
minifyJS: true
})))
.pipe(gzip())
.pipe(gulp.dest(dataFolder));
});
gulp.task('default', ['buildfs_embeded']); gulp.task('default', ['buildfs_embeded']);

+ 33
- 2
code/html/custom.css View File

@ -45,12 +45,15 @@
.button-update { .button-update {
background: #1f8dd6; background: #1f8dd6;
} }
.button-reset {
.button-reset,
.button-reconnect {
background: rgb(202, 60, 60); background: rgb(202, 60, 60);
} }
.button-reconnect {
.button-upgrade {
background: rgb(202, 60, 60); background: rgb(202, 60, 60);
margin-left: 5px;
} }
.button-upgrade-browse,
.button-apikey { .button-apikey {
background: rgb(0, 202, 0); background: rgb(0, 202, 0);
margin-left: 5px; margin-left: 5px;
@ -101,6 +104,15 @@ div.hint {
.template { .template {
display: none; display: none;
} }
input[name=upgrade] {
display: none;
}
#upgrade-progress {
display: none;
width: 100%;
height: 20px;
margin-top: 10px;
}
.pure-form .center { .pure-form .center {
margin: .5em 0 .2em; margin: .5em 0 .2em;
} }
@ -118,3 +130,22 @@ div.hint {
margin-top: -50px; margin-top: -50px;
margin-left: -200px; margin-left: -200px;
} }
div.state {
border-top: 1px solid #eee;
margin-top: 20px;
padding-top: 30px;
}
.state div {
font-size: 80%;
}
.state span {
font-size: 80%;
font-weight: bold;
}
.right {
text-align: right;
}

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

@ -41,6 +41,7 @@ function doUpdate() {
var form = $("#formSave"); var form = $("#formSave");
if (validateForm(form)) { if (validateForm(form)) {
var data = form.serializeArray(); var data = form.serializeArray();
delete(data['filename']);
websock.send(JSON.stringify({'config': data})); websock.send(JSON.stringify({'config': data}));
$(".powExpected").val(0); $(".powExpected").val(0);
$("input[name='powExpectedReset']") $("input[name='powExpectedReset']")
@ -50,6 +51,66 @@ function doUpdate() {
return false; return false;
} }
function doUpgrade() {
var contents = $("input[name='upgrade']")[0].files[0];
if (typeof contents == 'undefined') {
alert("First you have to select a file from your computer.");
return false;
}
var filename = $("input[name='upgrade']").val().split('\\').pop();
var data = new FormData();
data.append('upgrade', contents, filename);
$.ajax({
// Your server script to process the upload
url: 'http://' + host + ':' + port + '/upgrade',
type: 'POST',
// Form data
data: data,
// Tell jQuery not to process data or worry about content-type
// You *must* include these options!
cache: false,
contentType: false,
processData: false,
success: function(data, text) {
$("#upgrade-progress").hide();
if (data == 'OK') {
alert("Firmware image uploaded, board rebooting. This page will be refreshed in 5 seconds.");
setTimeout(function() {
window.location = "/";
}, 5000);
} else {
alert("There was an error trying to upload the new image, please try again.");
}
},
// Custom XMLHttpRequest
xhr: function() {
$("#upgrade-progress").show();
var myXhr = $.ajaxSettings.xhr();
if (myXhr.upload) {
// For handling the progress of the upload
myXhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
$('progress').attr({ value: e.loaded, max: e.total });
}
} , false);
}
return myXhr;
},
});
return false;
}
function doUpdatePassword() { function doUpdatePassword() {
var form = $("#formPassword"); var form = $("#formPassword");
if (validateForm(form)) { if (validateForm(form)) {
@ -366,6 +427,9 @@ function processData(data) {
if (key == "mqttStatus") { if (key == "mqttStatus") {
data.mqttStatus = data.mqttStatus ? "CONNECTED" : "NOT CONNECTED"; data.mqttStatus = data.mqttStatus ? "CONNECTED" : "NOT CONNECTED";
} }
if (key == "ntpStatus") {
data.ntpStatus = data.ntpStatus ? "SYNC'D" : "NOT SYNC'D";
}
if (key == "tmpUnits") { if (key == "tmpUnits") {
$("span#tmpUnit").html(data[key] == 1 ? "ºF" : "ºC"); $("span#tmpUnit").html(data[key] == 1 ? "ºF" : "ºC");
} }
@ -385,6 +449,13 @@ function processData(data) {
return; return;
} }
// Look for SPANs
var element = $("span[name=" + key + "]");
if (element.length > 0) {
element.html(data[key]);
return;
}
// Look for SELECTs // Look for SELECTs
var element = $("select[name=" + key + "]"); var element = $("select[name=" + key + "]");
if (element.length > 0) { if (element.length > 0) {
@ -448,6 +519,16 @@ function init() {
$(".button-settings-restore").on('click', restoreSettings); $(".button-settings-restore").on('click', restoreSettings);
$('#uploader').on('change', onFileUpload); $('#uploader').on('change', onFileUpload);
$(".button-apikey").on('click', doGenerateAPIKey); $(".button-apikey").on('click', doGenerateAPIKey);
$(".button-upgrade").on('click', doUpgrade);
$(".button-upgrade-browse").on('click', function() {
$("input[name='upgrade']")[0].click();
return false;
});
$("input[name='upgrade']").change(function (){
var fileName = $(this).val();
$("input[name='filename']").val(fileName.replace(/^.*[\\\/]/, ''));
});
$('progress').attr({ value: 0, max: 100 });
$(".pure-menu-link").on('click', showPanel); $(".pure-menu-link").on('click', showPanel);
$(".button-add-network").on('click', function() { $(".button-add-network").on('click', function() {
$("div.more", addNetwork()).toggle(); $("div.more", addNetwork()).toggle();


+ 165
- 51
code/html/index.html View File

@ -98,10 +98,18 @@
<a href="#" class="pure-menu-link" data="panel-mqtt">MQTT</a> <a href="#" class="pure-menu-link" data="panel-mqtt">MQTT</a>
</li> </li>
<li class="pure-menu-item">
<a href="#" class="pure-menu-link" data="panel-ntp">NTP</a>
</li>
<li class="pure-menu-item module module-dcz"> <li class="pure-menu-item module module-dcz">
<a href="#" class="pure-menu-link" data="panel-domoticz">DOMOTICZ</a> <a href="#" class="pure-menu-link" data="panel-domoticz">DOMOTICZ</a>
</li> </li>
<li class="pure-menu-item module module-idb">
<a href="#" class="pure-menu-link" data="panel-idb">INFLUXDB</a>
</li>
<li class="pure-menu-item module module-pow"> <li class="pure-menu-item module module-pow">
<a href="#" class="pure-menu-link" data="panel-power">POWER</a> <a href="#" class="pure-menu-link" data="panel-power">POWER</a>
</li> </li>
@ -143,39 +151,17 @@
<form class="pure-form pure-form-aligned"> <form class="pure-form pure-form-aligned">
<fieldset> <fieldset>
<div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="manufacturer">Manufacturer</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="manufacturer" readonly />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="device">Device</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="device" readonly />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="chipid">Chip ID</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="chipid" readonly />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="mac">MAC</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="mac" readonly />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="network">Network</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="network" readonly />
<div id="relays">
</div> </div>
<div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="deviceip">IP</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="deviceip" readonly />
<div class="pure-g module module-color">
<label class="pure-u-1 pure-u-sm-1-4">Color</label>
<input class="pure-u-1 pure-u-sm-1-4" data-wcp-layout="block" name="color" readonly />
</div> </div>
<div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="mqtt">MQTT Status</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="mqttStatus" readonly />
<div class="pure-g module module-analog">
<label class="pure-u-1 pure-u-sm-1-4" for="analogValue">Analog</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="analogValue" readonly />
</div> </div>
<div class="pure-g module module-ds"> <div class="pure-g module module-ds">
@ -199,12 +185,12 @@
</div> </div>
<div class="pure-g module module-pow module-emon"> <div class="pure-g module module-pow module-emon">
<label class="pure-u-1 pure-u-sm-1-4" for="powApparentPower">Apparent Power (W)</label>
<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 /> <input class="pure-u-1 pure-u-sm-3-4" type="text" name="powApparentPower" readonly />
</div> </div>
<div class="pure-g module module-pow"> <div class="pure-g module module-pow">
<label class="pure-u-1 pure-u-sm-1-4" for="powReactivePower">Reactive Power (W)</label>
<label class="pure-u-1 pure-u-sm-1-4" for="powReactivePower">Reactive Power (VAR)</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="powReactivePower" readonly /> <input class="pure-u-1 pure-u-sm-3-4" type="text" name="powReactivePower" readonly />
</div> </div>
@ -219,16 +205,43 @@
</div> </div>
<div class="pure-g module module-pow"> <div class="pure-g module module-pow">
<label class="pure-u-1 pure-u-sm-1-4" for="powPowerFactor">Power Factor (%)</label>
<label class="pure-u-1 pure-u-sm-1-4" for="powPowerFactor">Power Factor</label>
<input class="pure-u-1 pure-u-sm-3-4" type="text" name="powPowerFactor" readonly /> <input class="pure-u-1 pure-u-sm-3-4" type="text" name="powPowerFactor" readonly />
</div> </div>
<div id="relays">
</div>
<div class="pure-u-1 state">
<div class="pure-u-1 pure-u-sm-11-24">Manufacturer</div>
<div class="pure-u-1 pure-u-sm-11-24"><span class="right" name="manufacturer"></span></div>
<div class="pure-u-1 pure-u-sm-11-24">Device</div>
<div class="pure-u-1 pure-u-sm-11-24"><span class="right" name="device"></span></div>
<div class="pure-u-1 pure-u-sm-11-24">Chip ID</div>
<div class="pure-u-1 pure-u-sm-11-24"><span class="right" name="chipid"></span></div>
<div class="pure-u-1 pure-u-sm-11-24">MAC</div>
<div class="pure-u-1 pure-u-sm-11-24"><span class="right" name="mac"></span></div>
<div class="pure-u-1 pure-u-sm-11-24">Network</div>
<div class="pure-u-1 pure-u-sm-11-24"><span class="right" name="network"></span></div>
<div class="pure-u-1 pure-u-sm-11-24">IP</div>
<div class="pure-u-1 pure-u-sm-11-24"><span class="right" name="deviceip"></span></div>
<div class="pure-u-1 pure-u-sm-11-24">ESPurna Version</div>
<div class="pure-u-1 pure-u-sm-11-24"><span class="right" name="version"></span></div>
<div class="pure-u-1 pure-u-sm-11-24">ESPurna Build</div>
<div class="pure-u-1 pure-u-sm-11-24"><span class="right" name="buildDate"></span> <span class="right" name="buildTime"></span></div>
<div class="pure-u-1 pure-u-sm-11-24">MQTT Status</div>
<div class="pure-u-1 pure-u-sm-11-24"><span class="right" name="mqttStatus"></span></div>
<div class="pure-u-1 pure-u-sm-11-24">NTP Status</div>
<div class="pure-u-1 pure-u-sm-11-24"><span class="right" name="ntpStatus"></span></div>
<div class="pure-g module module-color">
<label class="pure-u-1 pure-u-sm-1-4">Color</label>
<input class="pure-u-1 pure-u-sm-1-4" data-wcp-layout="block" name="color" readonly />
</div> </div>
</fieldset> </fieldset>
@ -237,7 +250,7 @@
</div> </div>
</div> </div>
<form id="formSave" class="pure-form" action="/" method="post">
<form id="formSave" class="pure-form" action="/" method="post" enctype="multipart/form-data">
<input class="pure-u-1 pure-u-sm-3-4" type="hidden" name="webMode" value="0" /> <input class="pure-u-1 pure-u-sm-3-4" type="hidden" name="webMode" value="0" />
@ -302,13 +315,13 @@
<div class="pure-g"> <div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4" for="relayPulseTime">Switch pulse time</label> <label class="pure-u-1 pure-u-md-1-4" for="relayPulseTime">Switch pulse time</label>
<input name="relayPulseTime" class="pure-u-1 pure-u-md-3-4" type="number" min="1" tabindex="5" />
<input name="relayPulseTime" class="pure-u-1 pure-u-md-3-4" type="number" min="0" step="0.1" max="60" tabindex="5" />
<div class="pure-u-0 pure-u-md-1-4">&nbsp;</div> <div class="pure-u-0 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-md-3-4 hint">Pulse time in seconds.</div>
<div class="pure-u-1 pure-u-md-3-4 hint">Pulse time in seconds (maximum value is 60s, accepts decimals).</div>
</div> </div>
<div class="pure-g module module-fauxmo"> <div class="pure-g module module-fauxmo">
<label class="pure-u-1 pure-u-sm-1-4" for="fauxmoEnabled">Alexa integration</label>
<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="6" /></div>
</div> </div>
@ -380,6 +393,16 @@
</div> </div>
</div> </div>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4">Upgrade</label>
<input class="pure-u-1-2 pure-u-md-1-2" name="filename" type="text" readonly />
<div class=" pure-u-1-8 pure-u-md-1-8"><button class="pure-button button-upgrade-browse pure-u-23-24">Browse</button></div>
<div class=" pure-u-1-8 pure-u-md-1-8"><button class="pure-button button-upgrade pure-u-23-24">Upgrade</button></div>
<div class="pure-u-0 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-md-3-4"><progress id="upgrade-progress"></progress></div>
<input name="upgrade" type="file" tabindex="15" />
</div>
</fieldset> </fieldset>
</div> </div>
</div> </div>
@ -432,7 +455,7 @@
<div class="pure-g"> <div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4" for="mqttPassword">MQTT Password</label> <label class="pure-u-1 pure-u-md-1-4" for="mqttPassword">MQTT Password</label>
<input class="pure-u-1 pure-u-md-3-4" name="mqttPassword" type="text" size="20" tabindex="24" placeholder="Leave blank if no user/pass" />
<input class="pure-u-1 pure-u-md-3-4" name="mqttPassword" type="password" size="20" tabindex="24" placeholder="Leave blank if no user/pass" />
</div> </div>
<div class="pure-g"> <div class="pure-g">
@ -440,13 +463,13 @@
<input class="pure-u-1 pure-u-md-3-4" name="mqttTopic" type="text" size="20" tabindex="25" /> <input class="pure-u-1 pure-u-md-3-4" name="mqttTopic" type="text" size="20" tabindex="25" />
<div class="pure-u-0 pure-u-md-1-4">&nbsp;</div> <div class="pure-u-0 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-md-3-4 hint"> <div class="pure-u-1 pure-u-md-3-4 hint">
This is the root topic for this device. 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 />
- <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;/ip</strong> The device will report to this topic its IP.<br />
- <strong>&lt;root&gt;/version</strong> The device will report to this topic its firmware version on boot.<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.
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 />
- 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> </div>
@ -455,6 +478,48 @@
</div> </div>
<div class="panel" id="panel-ntp">
<div class="header">
<h1>NTP</h1>
<h2>Configure your NTP (Network Time Protocol) servers and local configuration to keep your device time up to the second for your location.</h2>
</div>
<div class="page">
<fieldset>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4" for="ntpServer1">NTP Server 1</label>
<input class="pure-u-1 pure-u-md-3-4" name="ntpServer1" type="text" size="20" tabindex="41" />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4" for="ntpServer2">NTP Server 2</label>
<input class="pure-u-1 pure-u-md-3-4" name="ntpServer2" type="text" size="20" tabindex="42" />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4" for="ntpServer3">NTP Server 3</label>
<input class="pure-u-1 pure-u-md-3-4" name="ntpServer3" type="text" size="20" tabindex="43" />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4" for="ntpOffset">Time offset</label>
<div class="pure-u-1 pure-u-sm-1-8"><input class="pure-u-sm-23-24" name="ntpOffset" type="number" min="-11" max="14" tabindex="44" data="0" /></div>
<div class="pure-u-1 pure-u-sm-5-8 hint center">Set to 0 for UTC time</div>
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-sm-1-4"><label for="ntpDST">Enable Daylight Saving Time</label></div>
<div class="pure-u-1 pure-u-sm-1-4"><input type="checkbox" name="ntpDST" /></div>
</div>
</fieldset>
</div>
</div>
<div class="panel" id="panel-domoticz"> <div class="panel" id="panel-domoticz">
<div class="header"> <div class="header">
@ -514,6 +579,12 @@
<div class="pure-u-1 pure-u-sm-5-8 hint center">Set to 0 to disable notifications.</div> <div class="pure-u-1 pure-u-sm-5-8 hint center">Set to 0 to disable notifications.</div>
</div> </div>
<div class="pure-g module module-analog">
<label class="pure-u-1 pure-u-sm-1-4" for="dczAnaIdx">Analog IDX</label>
<div class="pure-u-1 pure-u-sm-1-8"><input class="pure-u-sm-23-24" name="dczAnaIdx" type="number" min="0" tabindex="39" data="0" /></div>
<div class="pure-u-1 pure-u-sm-5-8 hint center">Set to 0 to disable notifications.</div>
</div>
<div id="idxs"> <div id="idxs">
</div> </div>
@ -522,6 +593,49 @@
</div> </div>
<div class="panel" id="panel-idb">
<div class="header">
<h1>INFLUXDB</h1>
<h2>
Configure the connection to your InfluxDB server. Leave the host field empty to disable InfluxDB connection.
</h2>
</div>
<div class="page">
<fieldset>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4" for="idbHost">Host</label>
<input class="pure-u-1 pure-u-md-3-4" name="idbHost" type="text" tabindex="41" />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4" for="idbPort">Port</label>
<input class="pure-u-1 pure-u-md-3-4" name="idbPort" type="text" tabindex="42" />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4" for="idbDatabase">Database</label>
<input class="pure-u-1 pure-u-md-3-4" name="idbDatabase" type="text" tabindex="43" />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4" for="idbUsername">Username</label>
<input class="pure-u-1 pure-u-md-3-4" name="idbUsername" type="text" tabindex="44" />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4" for="idbPassword">Password</label>
<input class="pure-u-1 pure-u-md-3-4" name="idbPassword" type="password" tabindex="45" />
</div>
</fieldset>
</div>
</div>
<div class="panel" id="panel-power"> <div class="panel" id="panel-power">
<div class="header"> <div class="header">
@ -623,8 +737,8 @@
<div id="relayTemplate" class="template"> <div id="relayTemplate" class="template">
<div class="pure-g"> <div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4">Switch<span class="relay_id"></span> Status</label>
<div class="pure-u-1 pure-u-sm-1-4"><input type="checkbox" class="relayStatus" data="0" /></div>
<div class="pure-u-1 pure-u-sm-1-4"><label>Switch<span class="relay_id"></span> Status</label></div>
<div class="pure-u-1 pure-u-sm-1-4"><input type="checkbox" class="relayStatus pure-u-1 pure-u-sm-1-4" data="0" /></div
</div> </div>
</div> </div>


+ 3
- 6
code/package.json View File

@ -1,6 +1,6 @@
{ {
"name": "esp8266-filesystem-builder", "name": "esp8266-filesystem-builder",
"version": "0.1.0",
"version": "0.2.0",
"description": "Gulp based build system for ESP8266 file system files", "description": "Gulp based build system for ESP8266 file system files",
"main": "gulpfile.js", "main": "gulpfile.js",
"author": "Xose Pérez <xose.perez@gmail.com>", "author": "Xose Pérez <xose.perez@gmail.com>",
@ -9,15 +9,12 @@
"del": "^2.2.1", "del": "^2.2.1",
"gulp": "^3.9.1", "gulp": "^3.9.1",
"gulp-base64-favicon": "^1.0.2", "gulp-base64-favicon": "^1.0.2",
"gulp-clean-css": "^2.0.10",
"gulp-clean-css": "^3.4.2",
"gulp-css-base64": "^1.3.4", "gulp-css-base64": "^1.3.4",
"gulp-gzip": "^1.4.0", "gulp-gzip": "^1.4.0",
"gulp-htmlmin": "^2.0.0", "gulp-htmlmin": "^2.0.0",
"gulp-if": "^2.0.1",
"gulp-inline": "^0.1.1", "gulp-inline": "^0.1.1",
"gulp-plumber": "^1.1.0",
"gulp-uglify": "^1.5.3",
"gulp-useref": "^3.1.2"
"gulp-uglify": "^1.5.3"
}, },
"dependencies": {} "dependencies": {}
} }

+ 55
- 5
code/platformio.ini View File

@ -21,7 +21,7 @@ lib_deps =
DallasTemperature DallasTemperature
Brzo I2C Brzo I2C
https://bitbucket.org/xoseperez/justwifi.git#1.1.3 https://bitbucket.org/xoseperez/justwifi.git#1.1.3
https://bitbucket.org/xoseperez/hlw8012.git#1.0.0
https://bitbucket.org/xoseperez/hlw8012.git#1.0.1
https://bitbucket.org/xoseperez/fauxmoesp.git#2.1.0 https://bitbucket.org/xoseperez/fauxmoesp.git#2.1.0
https://bitbucket.org/xoseperez/nofuss.git#0.2.2 https://bitbucket.org/xoseperez/nofuss.git#0.2.2
https://bitbucket.org/xoseperez/emonliteesp.git#0.1.2 https://bitbucket.org/xoseperez/emonliteesp.git#0.1.2
@ -31,6 +31,14 @@ lib_deps =
lib_ignore = lib_ignore =
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
[env:d1-analog]
platform = espressif8266
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] [env:d1-debug]
platform = espressif8266 platform = espressif8266
@ -83,6 +91,27 @@ upload_speed = 115200
upload_port = "192.168.4.1" upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266 upload_flags = --auth=fibonacci --port 8266
[env:espurna-debug]
platform = espressif8266
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]
platform = espressif8266
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"
upload_flags = --auth=fibonacci --port 8266
[env:sonoff-debug] [env:sonoff-debug]
platform = espressif8266 platform = espressif8266
framework = arduino framework = arduino
@ -419,20 +448,41 @@ upload_flags = --auth=fibonacci --port 8266
[env:led-controller-debug] [env:led-controller-debug]
platform = espressif8266 platform = espressif8266
framework = arduino framework = arduino
board = esp12e
board = esp01_1m
lib_deps = ${common.lib_deps} lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore} lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py extra_script = pio_hooks.py
build_flags = ${common.build_flags} -DLED_CONTROLLER
build_flags = ${common.build_flags_1m128} -DLED_CONTROLLER
[env:led-controller-debug-ota] [env:led-controller-debug-ota]
platform = espressif8266 platform = espressif8266
framework = arduino framework = arduino
board = esp12e
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
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
[env:h801-debug]
platform = espressif8266
framework = arduino
board = esp01_1m
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py
build_flags = -g -Wl,-Tesp8266.flash.1m128.ld -DH801_LED_CONTROLLER -DDEBUG_PORT=Serial1
[env:h801-debug-ota]
platform = espressif8266
framework = arduino
board = esp01_1m
lib_deps = ${common.lib_deps} lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore} lib_ignore = ${common.lib_ignore}
extra_script = pio_hooks.py extra_script = pio_hooks.py
build_flags = ${common.build_flags} -DLED_CONTROLLER
build_flags = -g -Wl,-Tesp8266.flash.1m128.ld -DH801_LED_CONTROLLER -DDEBUG_PORT=Serial1
upload_speed = 115200 upload_speed = 115200
upload_port = "192.168.4.1" upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266 upload_flags = --auth=fibonacci --port 8266

BIN
images/devices/h801.jpg View File

Before After
Width: 400  |  Height: 400  |  Size: 52 KiB

BIN
images/devices/magic-home-led-controller.jpg View File

Before After
Width: 400  |  Height: 400  |  Size: 21 KiB

BIN
images/devices/tinkerman-espurna-h.jpg View File

Before After
Width: 400  |  Height: 400  |  Size: 98 KiB

Loading…
Cancel
Save