Browse Source

Merge branch 'dev' into sensors

Conflicts:
	code/espurna/sensors/BH1750Sensor.h
	code/espurna/sensors/SI7021Sensor.h
softuart
Xose Pérez 7 years ago
parent
commit
8c463c5a70
51 changed files with 4458 additions and 3961 deletions
  1. +3
    -0
      code/espurna/alexa.ino
  2. +32
    -0
      code/espurna/broker.ino
  3. +3
    -0
      code/espurna/button.ino
  4. +12
    -13
      code/espurna/config/all.h
  5. +1
    -0
      code/espurna/config/arduino.h
  6. +45
    -3
      code/espurna/config/general.h
  7. +7
    -4
      code/espurna/config/hardware.h
  8. +5
    -0
      code/espurna/config/prototypes.h
  9. +3
    -2
      code/espurna/config/sensors.h
  10. +1
    -1
      code/espurna/config/version.h
  11. BIN
      code/espurna/data/index.html.gz
  12. +36
    -30
      code/espurna/debug.ino
  13. +3
    -2
      code/espurna/domoticz.ino
  14. +35
    -323
      code/espurna/espurna.ino
  15. +15
    -2
      code/espurna/i2c.ino
  16. +5
    -0
      code/espurna/ir.ino
  17. +4
    -0
      code/espurna/led.ino
  18. +22
    -4
      code/espurna/light.ino
  19. +7
    -0
      code/espurna/mdns.ino
  20. +57
    -3
      code/espurna/mqtt.ino
  21. +3
    -0
      code/espurna/nofuss.ino
  22. +92
    -32
      code/espurna/ntp.ino
  23. +4
    -1
      code/espurna/ota.ino
  24. +7
    -0
      code/espurna/relay.ino
  25. +3
    -0
      code/espurna/rf.ino
  26. +3
    -0
      code/espurna/rfbridge.ino
  27. +71
    -59
      code/espurna/scheduler.ino
  28. +7
    -0
      code/espurna/sensor.ino
  29. +23
    -11
      code/espurna/sensors/BH1750Sensor.h
  30. +1
    -0
      code/espurna/sensors/BMX280Sensor.h
  31. +1
    -0
      code/espurna/sensors/DHTSensor.h
  32. +5
    -0
      code/espurna/sensors/DallasSensor.h
  33. +2
    -0
      code/espurna/sensors/EmonADC121Sensor.h
  34. +1
    -0
      code/espurna/sensors/EmonADS1X15Sensor.h
  35. +2
    -0
      code/espurna/sensors/EmonAnalogSensor.h
  36. +2
    -0
      code/espurna/sensors/MHZ19Sensor.h
  37. +2
    -0
      code/espurna/sensors/SHT3XI2CSensor.h
  38. +58
    -36
      code/espurna/sensors/SI7021Sensor.h
  39. +8
    -6
      code/espurna/settings.ino
  40. +3
    -0
      code/espurna/ssdp.ino
  41. +3175
    -3162
      code/espurna/static/index.html.gz.h
  42. +122
    -0
      code/espurna/system.ino
  43. +7
    -0
      code/espurna/thinkspeak.ino
  44. +272
    -105
      code/espurna/utils.ino
  45. +3
    -0
      code/espurna/wifi.ino
  46. +22
    -4
      code/espurna/ws.ino
  47. +179
    -119
      code/html/custom.css
  48. +50
    -0
      code/html/custom.js
  49. +33
    -38
      code/html/index.html
  50. +0
    -0
      code/ota.py
  51. +1
    -1
      code/platformio.ini

+ 3
- 0
code/espurna/alexa.ino View File

@ -68,6 +68,9 @@ void alexaSetup() {
return relayStatus(device_id);
});
// Register loop
espurnaRegisterLoop(alexaLoop);
}
void alexaLoop() {


+ 32
- 0
code/espurna/broker.ino View File

@ -0,0 +1,32 @@
/*
BROKER MODULE
Copyright (C) 2017-2018 by Xose Pérez <xose dot perez at gmail dot com>
*/
#if BROKER_SUPPORT
#include <vector>
std::vector<void (*)(const char *, unsigned char, const char *)> _broker_callbacks;
// -----------------------------------------------------------------------------
void brokerRegister(void (*callback)(const char *, unsigned char, const char *)) {
_broker_callbacks.push_back(callback);
}
void brokerPublish(const char * topic, unsigned char id, const char * message) {
//DEBUG_MSG_P(PSTR("[BROKER] Message %s[%u] => %s\n"), topic, id, message);
for (unsigned char i=0; i<_broker_callbacks.size(); i++) {
(_broker_callbacks[i])(topic, id, message);
}
}
void brokerPublish(const char * topic, const char * message) {
brokerPublish(topic, 0, message);
}
#endif // BROKER_SUPPORT

+ 3
- 0
code/espurna/button.ino View File

@ -184,6 +184,9 @@ void buttonSetup() {
DEBUG_MSG_P(PSTR("[BUTTON] Number of buttons: %u\n"), _buttons.size());
// Register loop
espurnaRegisterLoop(buttonLoop);
}
void buttonLoop() {


+ 12
- 13
code/espurna/config/all.h View File

@ -1,15 +1,3 @@
#include "version.h"
#include "arduino.h"
#include "hardware.h"
#include "defaults.h"
#include "general.h"
#include "prototypes.h"
#include "sensors.h"
#ifdef USE_CORE_VERSION_H
#include "core_version.h"
#endif
/*
If you want to modify the stock configuration but you don't want to touch
the repo files you can either define USE_CUSTOM_H or remove the
@ -20,7 +8,18 @@
(Define USE_CUSTOM_H on commandline for platformio:
export PLATFORMIO_BUILD_FLAGS="'-DUSE_CUSTOM_H'" )
*/
#ifdef USE_CUSTOM_H
#include "custom.h"
#endif
#include "version.h"
#include "arduino.h"
#include "hardware.h"
#include "defaults.h"
#include "general.h"
#include "prototypes.h"
#include "sensors.h"
#ifdef USE_CORE_VERSION_H
#include "core_version.h"
#endif

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

@ -76,6 +76,7 @@
//#define LLMNR_SUPPORT 1 // Only with Arduino Core 2.4.0
//#define MDNS_SERVER_SUPPORT 0
//#define MDNS_CLIENT_SUPPORT 1
//#define BROKER_SUPPORT 0
//#define MQTT_SUPPORT 0
//#define NETBIOS_SUPPORT 1 // Only with Arduino Core 2.4.0
//#define NOFUSS_SUPPORT 1


+ 45
- 3
code/espurna/config/general.h View File

@ -64,6 +64,11 @@
#define SERIAL_BAUDRATE 115200 // Default baudrate
#endif
#ifndef DEBUG_ADD_TIMESTAMP
#define DEBUG_ADD_TIMESTAMP 1 // Add timestamp to debug messages
// (in millis overflowing every 1000 seconds)
#endif
// Second serial port (used for RX)
//#define SERIAL_RX_PORT Serial // This setting is usually defined
@ -143,12 +148,17 @@
#define EEPROM_ENERGY_COUNT 1 // Address for the energy counter (4 bytes)
#define EEPROM_CUSTOM_RESET 5 // Address for the reset reason (1 byte)
#define EEPROM_CRASH_COUNTER 6 // Address for the crash counter (1 byte)
#define EEPROM_DATA_END 7 // End of custom EEPROM data block
#define EEPROM_MESSAGE_ID 7 // Address for the MQTT message id (4 bytes)
#define EEPROM_DATA_END 11 // End of custom EEPROM data block
//------------------------------------------------------------------------------
// HEARTBEAT
//------------------------------------------------------------------------------
#ifndef HEARTBEAT_ENABLED
#define HEARTBEAT_ENABLED 1
#endif
#define HEARTBEAT_INTERVAL 300000 // Interval between heartbeat messages (in ms)
#define UPTIME_OVERFLOW 4294967295 // Uptime overflow value
@ -365,6 +375,7 @@ PROGMEM const char* const custom_reset_string[] = {
#define WS_BUFFER_SIZE 5 // Max number of secured websocket connections
#define WS_TIMEOUT 1800000 // Timeout for secured websocket
#define WS_UPDATE_INTERVAL 30000 // Update clients every 30 seconds
// -----------------------------------------------------------------------------
// API
@ -408,6 +419,11 @@ PROGMEM const char* const custom_reset_string[] = {
#define SSDP_SUPPORT 0 // Publish device using SSDP protocol by default (3.32Kb)
#endif
#if WEB_SUPPORT == 0
#undef SSDP_SUPPORT
#define SSDP_SUPPORT 0 // SSDP support requires web support
#endif
// -----------------------------------------------------------------------------
// SPIFFS
// -----------------------------------------------------------------------------
@ -490,11 +506,22 @@ PROGMEM const char* const custom_reset_string[] = {
#define MQTT_USE_JSON_DELAY 100 // Wait this many ms before grouping messages
#define MQTT_QUEUE_MAX_SIZE 10 // Size of the MQTT queue when MQTT_USE_JSON is enabled
// These are the properties that will be send when useJson is true
// These are the properties that will be sent when useJson is true
#ifndef MQTT_ENQUEUE_IP
#define MQTT_ENQUEUE_IP 1
#endif
#ifndef MQTT_ENQUEUE_MAC
#define MQTT_ENQUEUE_MAC 1
#endif
#ifndef MQTT_ENQUEUE_HOSTNAME
#define MQTT_ENQUEUE_HOSTNAME 1
#endif
#ifndef MQTT_ENQUEUE_DATETIME
#define MQTT_ENQUEUE_DATETIME 1
#endif
#ifndef MQTT_ENQUEUE_MESSAGE_ID
#define MQTT_ENQUEUE_MESSAGE_ID 1
#endif
// These particles will be concatenated to the MQTT_TOPIC base to form the actual topic
#define MQTT_TOPIC_JSON "data"
@ -511,6 +538,7 @@ PROGMEM const char* const custom_reset_string[] = {
#define MQTT_TOPIC_STATUS "status"
#define MQTT_TOPIC_MAC "mac"
#define MQTT_TOPIC_RSSI "rssi"
#define MQTT_TOPIC_MESSAGE_ID "id"
#define MQTT_TOPIC_APP "app"
#define MQTT_TOPIC_INTERVAL "interval"
#define MQTT_TOPIC_HOSTNAME "host"
@ -541,11 +569,25 @@ PROGMEM const char* const custom_reset_string[] = {
#define MQTT_DISCONNECT_EVENT 1
#define MQTT_MESSAGE_EVENT 2
#define MQTT_MESSAGE_ID_SHIFT 1000 // Store MQTT message id into EEPROM every these many
// Custom get and set postfixes
// Use something like "/status" or "/set", with leading slash
// Since 1.9.0 the default value is "" for getter and "/set" for setter
#ifndef MQTT_GETTER
#define MQTT_GETTER ""
#endif
#ifndef MQTT_SETTER
#define MQTT_SETTER "/set"
#endif
// -----------------------------------------------------------------------------
// BROKER
// -----------------------------------------------------------------------------
#ifndef BROKER_SUPPORT
#define BROKER_SUPPORT 1 // The broker is a poor-man's pubsub manager
#endif
// -----------------------------------------------------------------------------
// SETTINGS
@ -710,7 +752,6 @@ PROGMEM const char* const custom_reset_string[] = {
#define NTP_SUPPORT 1 // Scheduler needs NTP
#endif
#define SCHEDULER_UPDATE_SEC 5 // Scheduler perform switch at hh:mm:05
#define SCHEDULER_MAX_SCHEDULES 10 // Max schedules alowed
// -----------------------------------------------------------------------------
@ -725,6 +766,7 @@ PROGMEM const char* const custom_reset_string[] = {
#define NTP_TIME_OFFSET 1 // Default timezone offset (GMT+1)
#define NTP_DAY_LIGHT true // Enable daylight time saving by default
#define NTP_UPDATE_INTERVAL 1800 // NTP check every 30 minutes
#define NTP_START_DELAY 1000 // Delay NTP start 1 second
// -----------------------------------------------------------------------------
// ALEXA


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

@ -1393,9 +1393,12 @@
#define RELAY8_TYPE RELAY_TYPE_NORMAL
// -----------------------------------------------------------------------------
// Unknown hardware
// -----------------------------------------------------------------------------
#else
#error "UNSUPPORTED HARDWARE!"
#endif
// -----------------------------------------------------------------------------
// Check definitions
// -----------------------------------------------------------------------------
#if not defined(MANUFACTURER) || not defined(DEVICE)
#error "UNSUPPORTED HARDWARE!!"
#endif

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

@ -47,6 +47,11 @@ typedef std::function<void(unsigned int, const char *, const char *)> mqtt_callb
void mqttRegister(mqtt_callback_f callback);
String mqttTopicKey(char * topic);
// -----------------------------------------------------------------------------
// Broker
// -----------------------------------------------------------------------------
void brokerRegister(void (*)(const char *, unsigned char, const char *));
// -----------------------------------------------------------------------------
// Settings
// -----------------------------------------------------------------------------


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

@ -114,7 +114,7 @@
//------------------------------------------------------------------------------
#ifndef BH1750_SUPPORT
#define BH1750_SUPPORT 0
#define BH1750_SUPPORT 1
#endif
#ifndef BH1750_ADDRESS
@ -478,7 +478,8 @@
#define I2C_CLOCK_STRETCH_TIME 200 // BRZO clock stretch time
#define I2C_SCL_FREQUENCY 1000 // BRZO SCL frequency
#define I2C_CLEAR_BUS 0 // Clear I2C bus at boot
#define I2C_CLEAR_BUS 0 // Clear I2C bus on boot
#define I2C_PERFORM_SCAN 1 // Perform a bus scan on boot
//--------------------------------------------------------------------------------
// Internal power monitor


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

@ -1,5 +1,5 @@
#define APP_NAME "ESPURNA"
#define APP_VERSION "1.12.2a"
#define APP_VERSION "1.12.2b"
#define APP_AUTHOR "xose.perez@gmail.com"
#define APP_WEBSITE "http://tinkerman.cat"
#define CFG_VERSION 3

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


+ 36
- 30
code/espurna/debug.ino View File

@ -10,6 +10,7 @@ Copyright (C) 2016-2018 by Xose Pérez <xose dot perez at gmail dot com>
#include <stdio.h>
#include <stdarg.h>
#include <EEPROM.h>
extern "C" {
#include "user_interface.h"
@ -20,18 +21,20 @@ extern "C" {
WiFiUDP _udp_debug;
#endif
void debugSend(const char * format, ...) {
void _debugSend(char * message) {
va_list args;
va_start(args, format);
char test[1];
int len = ets_vsnprintf(test, 1, format, args) + 1;
char * buffer = new char[len];
ets_vsnprintf(buffer, len, format, args);
va_end(args);
#if DEBUG_ADD_TIMESTAMP
static bool add_timestamp = true;
char timestamp[10] = {0};
if (add_timestamp) snprintf_P(timestamp, sizeof(timestamp), PSTR("[%06lu] "), millis() % 1000000);
add_timestamp = (message[strlen(message)-1] == 10);
#endif
#if DEBUG_SERIAL_SUPPORT
DEBUG_PORT.printf(buffer);
#if DEBUG_ADD_TIMESTAMP
DEBUG_PORT.printf(timestamp);
#endif
DEBUG_PORT.printf(message);
#endif
#if DEBUG_UDP_SUPPORT
@ -39,7 +42,10 @@ void debugSend(const char * format, ...) {
if (systemCheck()) {
#endif
_udp_debug.beginPacket(DEBUG_UDP_IP, DEBUG_UDP_PORT);
_udp_debug.write(buffer);
#if DEBUG_ADD_TIMESTAMP
_udp_debug.write(timestamp);
#endif
_udp_debug.write(message);
_udp_debug.endPacket();
#if SYSTEM_CHECK_ENABLED
}
@ -47,9 +53,27 @@ void debugSend(const char * format, ...) {
#endif
#if DEBUG_TELNET_SUPPORT
_telnetWrite(buffer, strlen(buffer));
#if DEBUG_ADD_TIMESTAMP
_telnetWrite(timestamp, strlen(timestamp));
#endif
_telnetWrite(message, strlen(message));
#endif
}
void debugSend(const char * format, ...) {
va_list args;
va_start(args, format);
char test[1];
int len = ets_vsnprintf(test, 1, format, args) + 1;
char * buffer = new char[len];
ets_vsnprintf(buffer, len, format, args);
va_end(args);
_debugSend(buffer);
delete[] buffer;
}
@ -67,25 +91,7 @@ void debugSend_P(PGM_P format_P, ...) {
ets_vsnprintf(buffer, len, format, args);
va_end(args);
#if DEBUG_SERIAL_SUPPORT
DEBUG_PORT.printf(buffer);
#endif
#if DEBUG_UDP_SUPPORT
#if SYSTEM_CHECK_ENABLED
if (systemCheck()) {
#endif
_udp_debug.beginPacket(DEBUG_UDP_IP, DEBUG_UDP_PORT);
_udp_debug.write(buffer);
_udp_debug.endPacket();
#if SYSTEM_CHECK_ENABLED
}
#endif
#endif
#if DEBUG_TELNET_SUPPORT
_telnetWrite(buffer, strlen(buffer));
#endif
_debugSend(buffer);
delete[] buffer;


+ 3
- 2
code/espurna/domoticz.ino View File

@ -104,8 +104,9 @@ void _domoticzWebSocketOnSend(JsonObject& root) {
#endif // WEB_SUPPORT
void _domoticzConfigure() {
_dcz_enabled = getSetting("dczEnabled", DOMOTICZ_ENABLED).toInt() == 1;
_domoticzMqttSubscribe(_dcz_enabled);
bool enabled = getSetting("dczEnabled", DOMOTICZ_ENABLED).toInt() == 1;
if (enabled != _dcz_enabled) _domoticzMqttSubscribe(enabled);
_dcz_enabled = enabled;
}
//------------------------------------------------------------------------------


+ 35
- 323
code/espurna/espurna.ino View File

@ -20,300 +20,61 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config/all.h"
#include <EEPROM.h>
#include <vector>
std::vector<void (*)()> _loop_callbacks;
// -----------------------------------------------------------------------------
// METHODS
// REGISTER
// -----------------------------------------------------------------------------
unsigned long _loopDelay = 0;
void hardwareSetup() {
EEPROM.begin(EEPROM_SIZE);
#if DEBUG_SERIAL_SUPPORT
DEBUG_PORT.begin(SERIAL_BAUDRATE);
#if DEBUG_ESP_WIFI
DEBUG_PORT.setDebugOutput(true);
#endif
#elif defined(SERIAL_BAUDRATE)
Serial.begin(SERIAL_BAUDRATE);
#endif
#if SPIFFS_SUPPORT
SPIFFS.begin();
#endif
#if defined(ESPLIVE)
//The ESPLive has an ADC MUX which needs to be configured.
pinMode(16, OUTPUT);
digitalWrite(16, HIGH); //Defualt CT input (pin B, solder jumper B)
#endif
}
void hardwareLoop() {
// Heartbeat
static unsigned long last = 0;
if ((last == 0) || (millis() - last > HEARTBEAT_INTERVAL)) {
last = millis();
heartbeat();
}
void espurnaRegisterLoop(void (*callback)()) {
_loop_callbacks.push_back(callback);
}
// -----------------------------------------------------------------------------
// BOOTING
// -----------------------------------------------------------------------------
unsigned int sectors(size_t size) {
return (int) (size + SPI_FLASH_SEC_SIZE - 1) / SPI_FLASH_SEC_SIZE;
}
void welcome() {
DEBUG_MSG_P(PSTR("\n\n"));
DEBUG_MSG_P(PSTR("[INIT] %s %s\n"), (char *) APP_NAME, (char *) APP_VERSION);
DEBUG_MSG_P(PSTR("[INIT] %s\n"), (char *) APP_AUTHOR);
DEBUG_MSG_P(PSTR("[INIT] %s\n\n"), (char *) APP_WEBSITE);
DEBUG_MSG_P(PSTR("[INIT] CPU chip ID: 0x%06X\n"), ESP.getChipId());
DEBUG_MSG_P(PSTR("[INIT] CPU frequency: %u MHz\n"), ESP.getCpuFreqMHz());
DEBUG_MSG_P(PSTR("[INIT] SDK version: %s\n"), ESP.getSdkVersion());
DEBUG_MSG_P(PSTR("[INIT] Core version: %s\n"), getCoreVersion().c_str());
DEBUG_MSG_P(PSTR("[INIT] Core revision: %s\n"), getCoreRevision().c_str());
DEBUG_MSG_P(PSTR("\n"));
// -------------------------------------------------------------------------
FlashMode_t mode = ESP.getFlashChipMode();
DEBUG_MSG_P(PSTR("[INIT] Flash chip ID: 0x%06X\n"), ESP.getFlashChipId());
DEBUG_MSG_P(PSTR("[INIT] Flash speed: %u Hz\n"), ESP.getFlashChipSpeed());
DEBUG_MSG_P(PSTR("[INIT] Flash mode: %s\n"), mode == FM_QIO ? "QIO" : mode == FM_QOUT ? "QOUT" : mode == FM_DIO ? "DIO" : mode == FM_DOUT ? "DOUT" : "UNKNOWN");
DEBUG_MSG_P(PSTR("\n"));
DEBUG_MSG_P(PSTR("[INIT] Flash sector size: %8u bytes\n"), SPI_FLASH_SEC_SIZE);
DEBUG_MSG_P(PSTR("[INIT] Flash size (CHIP): %8u bytes\n"), ESP.getFlashChipRealSize());
DEBUG_MSG_P(PSTR("[INIT] Flash size (SDK): %8u bytes / %4d sectors\n"), ESP.getFlashChipSize(), sectors(ESP.getFlashChipSize()));
DEBUG_MSG_P(PSTR("[INIT] Firmware size: %8u bytes / %4d sectors\n"), ESP.getSketchSize(), sectors(ESP.getSketchSize()));
DEBUG_MSG_P(PSTR("[INIT] OTA size: %8u bytes / %4d sectors\n"), ESP.getFreeSketchSpace(), sectors(ESP.getFreeSketchSpace()));
#if SPIFFS_SUPPORT
FSInfo fs_info;
bool fs = SPIFFS.info(fs_info);
if (fs) {
DEBUG_MSG_P(PSTR("[INIT] SPIFFS size: %8u bytes / %4d sectors\n"), fs_info.totalBytes, sectors(fs_info.totalBytes));
}
#else
DEBUG_MSG_P(PSTR("[INIT] SPIFFS size: %8u bytes / %4d sectors\n"), 0, 0);
#endif
DEBUG_MSG_P(PSTR("[INIT] EEPROM size: %8u bytes / %4d sectors\n"), settingsMaxSize(), sectors(settingsMaxSize()));
DEBUG_MSG_P(PSTR("[INIT] Empty space: %8u bytes / 4 sectors\n"), 4 * SPI_FLASH_SEC_SIZE);
DEBUG_MSG_P(PSTR("\n"));
// -------------------------------------------------------------------------
#if SPIFFS_SUPPORT
if (fs) {
DEBUG_MSG_P(PSTR("[INIT] SPIFFS total size: %8u bytes\n"), fs_info.totalBytes);
DEBUG_MSG_P(PSTR("[INIT] used size: %8u bytes\n"), fs_info.usedBytes);
DEBUG_MSG_P(PSTR("[INIT] block size: %8u bytes\n"), fs_info.blockSize);
DEBUG_MSG_P(PSTR("[INIT] page size: %8u bytes\n"), fs_info.pageSize);
DEBUG_MSG_P(PSTR("[INIT] max files: %8u\n"), fs_info.maxOpenFiles);
DEBUG_MSG_P(PSTR("[INIT] max length: %8u\n"), fs_info.maxPathLength);
} else {
DEBUG_MSG_P(PSTR("[INIT] No SPIFFS partition\n"));
}
DEBUG_MSG_P(PSTR("\n"));
#endif
void setup() {
// -------------------------------------------------------------------------
DEBUG_MSG_P(PSTR("[INIT] BOARD: %s\n"), getBoardName().c_str());
DEBUG_MSG_P(PSTR("[INIT] SUPPORT:"));
#if ALEXA_SUPPORT
DEBUG_MSG_P(PSTR(" ALEXA"));
#endif
#if DEBUG_SERIAL_SUPPORT
DEBUG_MSG_P(PSTR(" DEBUG_SERIAL"));
#endif
#if DEBUG_TELNET_SUPPORT
DEBUG_MSG_P(PSTR(" DEBUG_TELNET"));
#endif
#if DEBUG_UDP_SUPPORT
DEBUG_MSG_P(PSTR(" DEBUG_UDP"));
#endif
#if DOMOTICZ_SUPPORT
DEBUG_MSG_P(PSTR(" DOMOTICZ"));
#endif
#if HOMEASSISTANT_SUPPORT
DEBUG_MSG_P(PSTR(" HOMEASSISTANT"));
#endif
#if I2C_SUPPORT
DEBUG_MSG_P(PSTR(" I2C"));
#endif
#if INFLUXDB_SUPPORT
DEBUG_MSG_P(PSTR(" INFLUXDB"));
#endif
#if LLMNR_SUPPORT
DEBUG_MSG_P(PSTR(" LLMNR"));
#endif
#if MDNS_SERVER_SUPPORT
DEBUG_MSG_P(PSTR(" MDNS"));
#endif
#if NETBIOS_SUPPORT
DEBUG_MSG_P(PSTR(" NETBIOS"));
#endif
#if NOFUSS_SUPPORT
DEBUG_MSG_P(PSTR(" NOFUSS"));
#endif
#if NTP_SUPPORT
DEBUG_MSG_P(PSTR(" NTP"));
#endif
#if RF_SUPPORT
DEBUG_MSG_P(PSTR(" RF"));
#endif
#if SCHEDULER_SUPPORT
DEBUG_MSG_P(PSTR(" SCHEDULER"));
#endif
#if SENSOR_SUPPORT
DEBUG_MSG_P(PSTR(" SENSOR"));
#endif
#if SPIFFS_SUPPORT
DEBUG_MSG_P(PSTR(" SPIFFS"));
#endif
#if SSDP_SUPPORT
DEBUG_MSG_P(PSTR(" SSDP"));
#endif
#if TELNET_SUPPORT
DEBUG_MSG_P(PSTR(" TELNET"));
#endif
#if TERMINAL_SUPPORT
DEBUG_MSG_P(PSTR(" TERMINAL"));
#endif
#if THINGSPEAK_SUPPORT
DEBUG_MSG_P(PSTR(" THINGSPEAK"));
#endif
#if WEB_SUPPORT
DEBUG_MSG_P(PSTR(" WEB"));
#endif
#if SENSOR_SUPPORT
DEBUG_MSG_P(PSTR("\n[INIT] SENSORS:"));
#if ANALOG_SUPPORT
DEBUG_MSG_P(PSTR(" ANALOG"));
#endif
#if BMX280_SUPPORT
DEBUG_MSG_P(PSTR(" BMX280"));
#endif
#if DALLAS_SUPPORT
DEBUG_MSG_P(PSTR(" DALLAS"));
#endif
#if DHT_SUPPORT
DEBUG_MSG_P(PSTR(" DHTXX"));
#endif
#if DIGITAL_SUPPORT
DEBUG_MSG_P(PSTR(" DIGITAL"));
#endif
#if ECH1560_SUPPORT
DEBUG_MSG_P(PSTR(" ECH1560"));
#endif
#if EMON_ADC121_SUPPORT
DEBUG_MSG_P(PSTR(" EMON_ADC121"));
#endif
#if EMON_ADS1X15_SUPPORT
DEBUG_MSG_P(PSTR(" EMON_ADX1X15"));
#endif
#if EMON_ANALOG_SUPPORT
DEBUG_MSG_P(PSTR(" EMON_ANALOG"));
#endif
#if EVENTS_SUPPORT
DEBUG_MSG_P(PSTR(" EVENTS"));
#endif
#if HLW8012_SUPPORT
DEBUG_MSG_P(PSTR(" HLW8012"));
#endif
#if MHZ19_SUPPORT
DEBUG_MSG_P(PSTR(" MHZ19"));
#endif
#if PMSX003_SUPPORT
DEBUG_MSG_P(PSTR(" PMSX003"));
#endif
#if SHT3X_I2C_SUPPORT
DEBUG_MSG_P(PSTR(" SHT3X_I2C"));
#endif
#if SI7021_SUPPORT
DEBUG_MSG_P(PSTR(" SI7021"));
#endif
#if V9261F_SUPPORT
DEBUG_MSG_P(PSTR(" V9261F"));
#endif
#endif // SENSOR_SUPPORT
DEBUG_MSG_P(PSTR("\n\n"));
// Basic modules, will always run
// -------------------------------------------------------------------------
unsigned char reason = resetReason();
if (reason > 0) {
char buffer[32];
strcpy_P(buffer, custom_reset_string[reason-1]);
DEBUG_MSG_P(PSTR("[INIT] Last reset reason: %s\n"), buffer);
} else {
DEBUG_MSG_P(PSTR("[INIT] Last reset reason: %s\n"), (char *) ESP.getResetReason().c_str());
}
DEBUG_MSG_P(PSTR("[INIT] Free heap: %u bytes\n"), getFreeHeap());
#if ADC_VCC_ENABLED
DEBUG_MSG_P(PSTR("[INIT] Power: %u mV\n"), ESP.getVcc());
#endif
DEBUG_MSG_P(PSTR("[INIT] Power saving delay value: %lu ms\n"), _loopDelay);
if (!systemCheck()) DEBUG_MSG_P(PSTR("\n[INIT] Device is in SAFE MODE\n"));
DEBUG_MSG_P(PSTR("\n"));
}
void setup() {
// Init EEPROM, Serial and SPIFFS
hardwareSetup();
// Question system stability
#if SYSTEM_CHECK_ENABLED
systemCheck(false);
#endif
// Init EEPROM, Serial, SPIFFS and system check
systemSetup();
// Init persistance and terminal features
settingsSetup();
// Hostname & board name initialization
if (getSetting("hostname").length() == 0) {
setSetting("hostname", getIdentifier());
}
setBoardName();
// Cache loop delay value to speed things (recommended max 250ms)
_loopDelay = atol(getSetting("loopDelay", LOOP_DELAY_TIME).c_str());
// Show welcome message and system configuration
welcome();
info();
// Basic modules, will always run
wifiSetup();
otaSetup();
#if TELNET_SUPPORT
telnetSetup();
#endif
// Do not run the next services if system is flagged stable
// -------------------------------------------------------------------------
// Check if system is stable
// -------------------------------------------------------------------------
#if SYSTEM_CHECK_ENABLED
if (!systemCheck()) return;
#endif
// -------------------------------------------------------------------------
// Next modules will be only loaded if system is flagged as stable
// -------------------------------------------------------------------------
// Init webserver required before any module that uses API
#if WEB_SUPPORT
webSetup();
@ -321,19 +82,23 @@ void setup() {
apiSetup();
#endif
// lightSetup must be called before relaySetup
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
lightSetup();
#endif
relaySetup();
buttonSetup();
ledSetup();
#if MQTT_SUPPORT
mqttSetup();
#endif
#if MDNS_SERVER_SUPPORT
mdnsServerSetup();
#endif
#if MDNS_CLIENT_SUPPORT
mdnsClientSetup();
#endif
#if LLMNR_SUPPORT
llmnrSetup();
#endif
@ -348,12 +113,7 @@ void setup() {
#endif
#if I2C_SUPPORT
i2cSetup();
#if I2C_CLEAR_BUS
i2cClearBus();
#endif
i2cScan();
#endif
#ifdef ITEAD_SONOFF_RFBRIDGE
rfbSetup();
#endif
@ -388,6 +148,11 @@ void setup() {
schSetup();
#endif
// 3rd party code hook
#if USE_EXTRA
extraSetup();
#endif
// Prepare configuration for version 2.0
migrate();
@ -397,62 +162,9 @@ void setup() {
void loop() {
hardwareLoop();
settingsLoop();
wifiLoop();
otaLoop();
#if SYSTEM_CHECK_ENABLED
systemCheckLoop();
// Do not run the next services if system is flagged stable
if (!systemCheck()) return;
#endif
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
lightLoop();
#endif
relayLoop();
buttonLoop();
ledLoop();
#if MQTT_SUPPORT
mqttLoop();
#endif
#ifdef ITEAD_SONOFF_RFBRIDGE
rfbLoop();
#endif
#if SSDP_SUPPORT
ssdpLoop();
#endif
#if NTP_SUPPORT
ntpLoop();
#endif
#if ALEXA_SUPPORT
alexaLoop();
#endif
#if NOFUSS_SUPPORT
nofussLoop();
#endif
#if RF_SUPPORT
rfLoop();
#endif
#if IR_SUPPORT
irLoop();
#endif
#if SENSOR_SUPPORT
sensorLoop();
#endif
#if THINGSPEAK_SUPPORT
tspkLoop();
#endif
#if SCHEDULER_SUPPORT
schLoop();
#endif
#if MDNS_CLIENT_SUPPORT
mdnsClientLoop();
#endif
// Power saving delay
delay(_loopDelay);
// Call registered loop callbacks
for (unsigned char i = 0; i < _loop_callbacks.size(); i++) {
(_loop_callbacks[i])();
}
}

+ 15
- 2
code/espurna/i2c.ino View File

@ -232,12 +232,17 @@ void i2c_write_uint8(uint8_t address, uint8_t reg, uint8_t value) {
}
void i2c_write_uint16(uint8_t address, uint8_t reg, uint16_t value) {
uint8_t buffer[3] = {reg, value >> 8, value & 0xFF};
uint8_t buffer[3];
buffer[0] = reg;
buffer[1] = (value >> 8) & 0xFF;
buffer[2] = (value >> 0) & 0xFF;
i2c_write_buffer(address, buffer, 3);
}
void i2c_write_uint16(uint8_t address, uint16_t value) {
uint8_t buffer[2] = {value >> 8, value & 0xFF};
uint8_t buffer[2];
buffer[0] = (value >> 8) & 0xFF;
buffer[1] = (value >> 0) & 0xFF;
i2c_write_buffer(address, buffer, 2);
}
@ -346,6 +351,14 @@ void i2cSetup() {
DEBUG_MSG_P(PSTR("[I2C] Using GPIO%u for SDA and GPIO%u for SCL\n"), sda, scl);
#if I2C_CLEAR_BUS
i2cClearBus();
#endif
#if I2C_PERFORM_SCAN
i2cScan();
#endif
}
#endif

+ 5
- 0
code/espurna/ir.ino View File

@ -92,8 +92,13 @@ void _irProcessCode(unsigned long code) {
// -----------------------------------------------------------------------------
void irSetup() {
_ir_recv = new IRrecv(IR_PIN);
_ir_recv->enableIRIn();
// Register loop
espurnaRegisterLoop(irLoop);
}
void irLoop() {


+ 4
- 0
code/espurna/led.ino View File

@ -167,6 +167,10 @@ void ledSetup() {
DEBUG_MSG_P(PSTR("[LED] Number of leds: %d\n"), _leds.size());
// Register loop
espurnaRegisterLoop(ledLoop);
}
void ledLoop() {


+ 22
- 4
code/espurna/light.ino View File

@ -571,7 +571,7 @@ void lightMQTT() {
// Channels
for (unsigned int i=0; i < _light_channel.size(); i++) {
snprintf_P(buffer, sizeof(buffer), PSTR("%d"), _light_channel[i].value);
itoa(_light_channel[i].value, buffer, 10);
mqttSend(MQTT_TOPIC_CHANNEL, i, buffer);
}
@ -588,6 +588,22 @@ void lightMQTTGroup() {
#endif
// -----------------------------------------------------------------------------
// Broker
// -----------------------------------------------------------------------------
#if BROKER_SUPPORT
void lightBroker() {
char buffer[10];
for (unsigned int i=0; i < _light_channel.size(); i++) {
itoa(_light_channel[i].value, buffer, 10);
brokerPublish(MQTT_TOPIC_CHANNEL, i, buffer);
}
}
#endif
// -----------------------------------------------------------------------------
// API
// -----------------------------------------------------------------------------
@ -610,6 +626,11 @@ void lightUpdate(bool save, bool forward, bool group_forward) {
_light_steps_left = _light_use_transitions ? LIGHT_TRANSITION_STEPS : 1;
_light_transition_ticker.attach_ms(LIGHT_TRANSITION_STEP, _lightProviderUpdate);
// Report channels to local broker
#if BROKER_SUPPORT
lightBroker();
#endif
// Report color & brightness to MQTT broker
#if MQTT_SUPPORT
if (forward) lightMQTT();
@ -1041,7 +1062,4 @@ void lightSetup() {
}
void lightLoop(){
}
#endif // LIGHT_PROVIDER != LIGHT_PROVIDER_NONE

+ 7
- 0
code/espurna/mdns.ino View File

@ -118,6 +118,13 @@ String mdnsResolve(String name) {
return mdnsResolve((char *) name.c_str());
}
void mdnsClientSetup() {
// Register loop
espurnaRegisterLoop(mdnsClientLoop);
}
void mdnsClientLoop() {
_mdns_resolver.loop();
}


+ 57
- 3
code/espurna/mqtt.ino View File

@ -8,6 +8,7 @@ Copyright (C) 2016-2018 by Xose Pérez <xose dot perez at gmail dot com>
#if MQTT_SUPPORT
#include <EEPROM.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ArduinoJson.h>
@ -244,6 +245,48 @@ void _mqttConfigure() {
}
unsigned long _mqttNextMessageId() {
static unsigned long id = 0;
// just reboot, get last count from EEPROM
if (id == 0) {
// read id from EEPROM and shift it
id = EEPROM.read(EEPROM_MESSAGE_ID);
if (id == 0xFF) {
// There was nothing in EEPROM,
// next message is first message
id = 0;
} else {
id = (id << 8) + EEPROM.read(EEPROM_MESSAGE_ID + 1);
id = (id << 8) + EEPROM.read(EEPROM_MESSAGE_ID + 2);
id = (id << 8) + EEPROM.read(EEPROM_MESSAGE_ID + 3);
// Calculate next block and start from there
id = MQTT_MESSAGE_ID_SHIFT * (1 + (id / MQTT_MESSAGE_ID_SHIFT));
}
}
// Save to EEPROM every MQTT_MESSAGE_ID_SHIFT
if (id % MQTT_MESSAGE_ID_SHIFT == 0) {
EEPROM.write(EEPROM_MESSAGE_ID + 0, (id >> 24) & 0xFF);
EEPROM.write(EEPROM_MESSAGE_ID + 1, (id >> 16) & 0xFF);
EEPROM.write(EEPROM_MESSAGE_ID + 2, (id >> 8) & 0xFF);
EEPROM.write(EEPROM_MESSAGE_ID + 3, (id >> 0) & 0xFF);
EEPROM.commit();
}
id++;
return id;
}
// -----------------------------------------------------------------------------
// WEB
// -----------------------------------------------------------------------------
@ -302,8 +345,8 @@ void _mqttCallback(unsigned int type, const char * topic, const char * payload)
// Subscribe to internal action topics
mqttSubscribe(MQTT_TOPIC_ACTION);
// Send heartbeat messages
heartbeat();
// Flag system to send heartbeat
systemSendHeartbeat();
}
@ -428,6 +471,7 @@ void mqttSendRaw(const char * topic, const char * message) {
void mqttFlush() {
if (!_mqtt.connected()) return;
if (_mqtt_queue.size() == 0) return;
DynamicJsonBuffer jsonBuffer;
@ -441,7 +485,7 @@ void mqttFlush() {
// Add extra propeties
#if NTP_SUPPORT && MQTT_ENQUEUE_DATETIME
if (ntpConnected()) root[MQTT_TOPIC_TIME] = ntpDateTime();
if (ntpSynced()) root[MQTT_TOPIC_TIME] = ntpDateTime();
#endif
#if MQTT_ENQUEUE_MAC
root[MQTT_TOPIC_MAC] = WiFi.macAddress();
@ -452,6 +496,9 @@ void mqttFlush() {
#if MQTT_ENQUEUE_IP
root[MQTT_TOPIC_IP] = getIP();
#endif
#if MQTT_ENQUEUE_MESSAGE_ID
root[MQTT_TOPIC_MESSAGE_ID] = _mqttNextMessageId();
#endif
// Send
String output;
@ -478,6 +525,10 @@ void mqttQueueTopic(const char * topic) {
void mqttEnqueue(const char * topic, const char * message) {
// Queue is not meant to send message "offline"
// We must prevent the queue does not get full while offline
if (!_mqtt.connected()) return;
// Force flusing the queue if the MQTT_QUEUE_MAX_SIZE has been reached
if (_mqtt_queue.size() >= MQTT_QUEUE_MAX_SIZE) mqttFlush();
@ -676,6 +727,9 @@ void mqttSetup() {
_mqttInitCommands();
#endif
// Register loop
espurnaRegisterLoop(mqttLoop);
}
void mqttLoop() {


+ 3
- 0
code/espurna/nofuss.ino View File

@ -153,6 +153,9 @@ void nofussSetup() {
_nofussInitCommands();
#endif
// Register loop
espurnaRegisterLoop(nofussLoop);
}
void nofussLoop() {


+ 92
- 32
code/espurna/ntp.ino View File

@ -13,63 +13,124 @@ Copyright (C) 2016-2018 by Xose Pérez <xose dot perez at gmail dot com>
#include <WiFiClient.h>
#include <Ticker.h>
Ticker _ntp_delay;
unsigned long _ntp_start = 0;
bool _ntp_update = false;
bool _ntp_configure = false;
// -----------------------------------------------------------------------------
// NTP
// -----------------------------------------------------------------------------
void _ntpWebSocketOnSend(JsonObject& root) {
root["time"] = ntpDateTime();
root["ntpVisible"] = 1;
root["ntpStatus"] = ntpConnected();
root["ntpServer1"] = getSetting("ntpServer1", NTP_SERVER);
root["ntpServer2"] = getSetting("ntpServer2");
root["ntpServer3"] = getSetting("ntpServer3");
root["ntpStatus"] = (timeStatus() == timeSet);
root["ntpServer"] = getSetting("ntpServer", NTP_SERVER);
root["ntpOffset"] = getSetting("ntpOffset", NTP_TIME_OFFSET).toInt();
root["ntpDST"] = getSetting("ntpDST", NTP_DAY_LIGHT).toInt() == 1;
if (ntpSynced()) root["now"] = now();
}
void _ntpStart() {
_ntp_start = 0;
NTP.begin(getSetting("ntpServer", NTP_SERVER));
NTP.setInterval(NTP_UPDATE_INTERVAL);
_ntpConfigure();
}
void _ntpConfigure() {
_ntp_configure = false;
int offset = getSetting("ntpOffset", NTP_TIME_OFFSET).toInt();
int sign = offset > 0 ? 1 : -1;
offset = abs(offset);
int tz_hours = sign * (offset / 60);
int tz_minutes = sign * (offset % 60);
if (NTP.getTimeZone() != tz_hours || NTP.getTimeZoneMinutes() != tz_minutes) {
NTP.setTimeZone(tz_hours, tz_minutes);
_ntp_update = true;
}
bool daylight = getSetting("ntpDST", NTP_DAY_LIGHT).toInt() == 1;
if (NTP.getDayLight() != daylight) {
NTP.setDayLight(daylight);
_ntp_update = true;
}
String server = getSetting("ntpServer", NTP_SERVER);
if (!NTP.getNtpServerName().equals(server)) {
NTP.setNtpServerName(server);
}
}
void _ntpUpdate() {
_ntp_update = false;
#if WEB_SUPPORT
wsSend(_ntpWebSocketOnSend);
#endif
DEBUG_MSG_P(PSTR("[NTP] Time: %s\n"), (char *) ntpDateTime().c_str());
}
void _ntpConfigure() {
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);
void _ntpLoop() {
if (0 < _ntp_start && _ntp_start < millis()) _ntpStart();
if (_ntp_configure) _ntpConfigure();
if (_ntp_update) _ntpUpdate();
now();
#if BROKER_SUPPORT
static unsigned char last_minute = 60;
if (ntpSynced() && (minute() != last_minute)) {
last_minute = minute();
brokerPublish(MQTT_TOPIC_DATETIME, ntpDateTime().c_str());
}
#endif
}
void _ntpBackwards() {
moveSetting("ntpServer1", "ntpServer");
delSetting("ntpServer2");
delSetting("ntpServer3");
int offset = getSetting("ntpOffset", NTP_TIME_OFFSET).toInt();
if (-30 < offset && offset < 30) {
offset *= 60;
setSetting("ntpOffset", offset);
}
}
// -----------------------------------------------------------------------------
bool ntpConnected() {
return (timeStatus() == timeSet);
bool ntpSynced() {
return (year() > 2017);
}
String ntpDateTime() {
if (!ntpConnected()) return String("Not set");
String value = NTP.getTimeDateString();
int hour = value.substring(0, 2).toInt();
int minute = value.substring(3, 5).toInt();
int second = value.substring(6, 8).toInt();
int day = value.substring(9, 11).toInt();
int month = value.substring(12, 14).toInt();
int year = value.substring(15, 19).toInt();
if (!ntpSynced()) return String();
char buffer[20];
snprintf_P(buffer, sizeof(buffer), PSTR("%04d-%02d-%02d %02d:%02d:%02d"), year, month, day, hour, minute, second);
time_t t = now();
snprintf_P(buffer, sizeof(buffer),
PSTR("%04d-%02d-%02d %02d:%02d:%02d"),
year(t), month(t), day(t), hour(t), minute(t), second(t)
);
return String(buffer);
}
// -----------------------------------------------------------------------------
void ntpSetup() {
_ntpBackwards();
NTP.onNTPSyncEvent([](NTPSyncEvent_t error) {
if (error) {
#if WEB_SUPPORT
@ -81,23 +142,22 @@ void ntpSetup() {
DEBUG_MSG_P(PSTR("[NTP] Error: Invalid NTP server address\n"));
}
} else {
_ntp_delay.once_ms(100, _ntpUpdate);
_ntp_update = true;
}
});
wifiRegister([](justwifi_messages_t code, char * parameter) {
if (code == MESSAGE_CONNECTED) _ntpConfigure();
if (code == MESSAGE_CONNECTED) _ntp_start = millis() + NTP_START_DELAY;
});
#if WEB_SUPPORT
wsOnSendRegister(_ntpWebSocketOnSend);
wsOnAfterParseRegister(_ntpConfigure);
wsOnAfterParseRegister([]() { _ntp_configure = true; });
#endif
}
// Register loop
espurnaRegisterLoop(_ntpLoop);
void ntpLoop() {
now();
}
#endif // NTP_SUPPORT

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

@ -45,7 +45,7 @@ void otaSetup() {
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
DEBUG_MSG_P(PSTR("[OTA] Progress: %u%%%%\r"), (progress / (total / 100)));
DEBUG_MSG_P(PSTR("[OTA] Progress: %u%%%% \r"), (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
@ -61,6 +61,9 @@ void otaSetup() {
ArduinoOTA.begin();
// Register loop
espurnaRegisterLoop(otaLoop);
}
void otaLoop() {


+ 7
- 0
code/espurna/relay.ino View File

@ -728,6 +728,8 @@ void relaySetup() {
_relayBoot();
relayLoop();
espurnaRegisterLoop(relayLoop);
#if WEB_SUPPORT
relaySetupAPI();
relaySetupWS();
@ -760,6 +762,11 @@ void relayLoop(void) {
// Call the provider to perform the action
_relayProviderStatus(id, status);
// Send to Broker
#if BROKER_SUPPORT
brokerPublish(MQTT_TOPIC_RELAY, id, status ? "1" : "0");
#endif
// Send MQTT
#if MQTT_SUPPORT
relayMQTT(id);


+ 3
- 0
code/espurna/rf.ino View File

@ -101,6 +101,9 @@ void rfSetup() {
wsOnAfterParseRegister(_rfBuildCodes);
#endif
// Register loop
espurnaRegisterLoop(rfLoop);
}
#endif

+ 3
- 0
code/espurna/rfbridge.ino View File

@ -505,6 +505,9 @@ void rfbSetup() {
wsOnActionRegister(_rfbWebSocketOnAction);
#endif
// Register oop
espurnaRegisterLoop(rfbLoop);
}
void rfbLoop() {


+ 71
- 59
code/espurna/scheduler.ino View File

@ -9,7 +9,9 @@ Adapted by Xose Pérez <xose dot perez at gmail dot com>
#if SCHEDULER_SUPPORT
#include <NtpClientLib.h>
#include <TimeLib.h>
// -----------------------------------------------------------------------------
#if WEB_SUPPORT
@ -30,6 +32,8 @@ void _schWebSocketOnSend(JsonObject &root){
#endif // WEB_SUPPORT
// -----------------------------------------------------------------------------
void _schConfigure() {
bool delete_flag = false;
@ -71,7 +75,7 @@ void _schConfigure() {
bool _schIsThisWeekday(String weekdays){
// Monday = 1, Tuesday = 2 ... Sunday = 7
// Convert from Sunday to Monday as day 1
int w = weekday(now()) - 1;
if (w == 0) w = 7;
@ -86,21 +90,72 @@ bool _schIsThisWeekday(String weekdays){
}
int _schMinutesLeft(unsigned char schedule_hour, unsigned char schedule_minute){
time_t t = now();
unsigned char now_hour = hour(t);
unsigned char now_minute = minute(t);
return (schedule_hour - now_hour) * 60 + schedule_minute - now_minute;
}
void _schCheck() {
// Check schedules
for (unsigned char i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) {
int sch_switch = getSetting("schSwitch", i, 0xFF).toInt();
if (sch_switch == 0xFF) break;
String sch_weekdays = getSetting("schWDs", i, "");
if (_schIsThisWeekday(sch_weekdays)) {
int sch_hour = getSetting("schHour", i, 0).toInt();
int sch_minute = getSetting("schMinute", i, 0).toInt();
int minutes_to_trigger = _schMinutesLeft(sch_hour, sch_minute);
if (minutes_to_trigger == 0) {
int sch_action = getSetting("schAction", i, 0).toInt();
if (sch_action == 2) {
relayToggle(sch_switch);
} else {
relayStatus(sch_switch, sch_action);
}
DEBUG_MSG_P(PSTR("[SCH] Schedule #%d TRIGGERED!!\n"), sch_switch);
// Show minutes to trigger every 15 minutes
// or every minute if less than 15 minutes to scheduled time.
// This only works for schedules on this same day.
// For instance, if your scheduler is set for 00:01 you will only
// get one notification before the trigger (at 00:00)
} else if (minutes_to_trigger > 0) {
#if DEBUG_SUPPORT
if ((minutes_to_trigger % 15 == 0) || (minutes_to_trigger < 15)) {
DEBUG_MSG_P(
PSTR("[SCH] %d minutes to trigger schedule #%d\n"),
minutes_to_trigger, sch_switch
);
}
#endif
}
}
unsigned char now_hour;
unsigned char now_minute;
if (ntpConnected()) {
String value = NTP.getTimeDateString();
now_hour = value.substring(0, 2).toInt();
now_minute = value.substring(3, 5).toInt();
} else {
time_t t = now();
now_hour = hour(t);
now_minute = minute(t);
}
return (schedule_hour - now_hour) * 60 + schedule_minute - now_minute;
}
void _schLoop() {
// Check time has been sync'ed
if (!ntpSynced()) return;
// Check schedules every minute at hh:mm:00
static unsigned long last_minute = 60;
unsigned char current_minute = minute();
if (current_minute != last_minute) {
last_minute = current_minute;
_schCheck();
}
}
@ -116,52 +171,9 @@ void schSetup() {
wsOnAfterParseRegister(_schConfigure);
#endif
}
void schLoop() {
static unsigned long last_update = 0;
static int update_time = 0;
// Check if we should compare scheduled and actual times
if ((millis() - last_update > update_time) || (last_update == 0)) {
last_update = millis();
// Register loop
espurnaRegisterLoop(_schLoop);
// Calculate next update time
unsigned char current_second = ntpConnected() ?
NTP.getTimeDateString().substring(6, 8).toInt() :
second(now())
;
update_time = (SCHEDULER_UPDATE_SEC + 60 - current_second) * 1000;
for (unsigned char i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) {
int sch_switch = getSetting("schSwitch", i, 0xFF).toInt();
if (sch_switch == 0xFF) break;
String sch_weekdays = getSetting("schWDs", i, "");
if (_schIsThisWeekday(sch_weekdays)) {
int sch_hour = getSetting("schHour", i, 0).toInt();
int sch_minute = getSetting("schMinute", i, 0).toInt();
int minutes_to_trigger = _schMinutesLeft(sch_hour, sch_minute);
if (minutes_to_trigger == 0) {
int sch_action = getSetting("schAction", i, 0).toInt();
if (sch_action == 2) {
relayToggle(sch_switch);
} else {
relayStatus(sch_switch, sch_action);
}
DEBUG_MSG_P(PSTR("[SCH] Schedule #%d TRIGGERED!!\n"), sch_switch);
} else if (minutes_to_trigger > 0) {
DEBUG_MSG_P(
PSTR("[SCH] %d minutes to trigger schedule #%d\n"),
minutes_to_trigger, sch_switch
);
}
}
}
}
}
#endif // SCHEDULER_SUPPORT

+ 7
- 0
code/espurna/sensor.ino View File

@ -619,6 +619,9 @@ void sensorSetup() {
_sensorInitCommands();
#endif
// Register loop
espurnaRegisterLoop(sensorLoop);
}
void sensorLoop() {
@ -689,6 +692,10 @@ void sensorLoop() {
_magnitudes[i].reported = filtered;
dtostrf(filtered, 1-sizeof(buffer), decimals, buffer);
#if BROKER_SUPPORT
brokerPublish(_magnitudeTopic(magnitude.type).c_str(), magnitude.local, buffer);
#endif
#if MQTT_SUPPORT
if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) {


+ 23
- 11
code/espurna/sensors/BH1750Sensor.h View File

@ -62,9 +62,8 @@ class BH1750Sensor : public I2CSensor {
_address = _begin_i2c(_address, sizeof(addresses), addresses);
if (_address == 0) return;
// Configure
_configure();
delay(10);
// Run configuration on next update
_run_configure = true;
}
@ -83,26 +82,29 @@ class BH1750Sensor : public I2CSensor {
return MAGNITUDE_NONE;
}
// Pre-read hook (usually to populate registers with up-to-date data)
void pre() {
_error = SENSOR_ERROR_OK;
_lux = _read();
}
// Current value for slot # index
double value(unsigned char index) {
_error = SENSOR_ERROR_OK;
if (index == 0) return _read();
if (index == 0) return _lux;
_error = SENSOR_ERROR_OUT_OF_RANGE;
return 0;
}
protected:
void _configure() {
i2c_write_uint8(_address, _mode);
}
double _read() {
// For one-shot modes reconfigure sensor & wait for conversion
if (_mode & 0x20) {
if (_run_configure) {
_configure();
// Configure mode
i2c_write_uint8(_address, _mode);
// According to datasheet
// conversion time is ~16ms for low resolution
@ -112,15 +114,25 @@ class BH1750Sensor : public I2CSensor {
unsigned long start = millis();
while (millis() - start < wait) delay(1);
// Keep on running configure each time if one-shot mode
_run_configure = _mode & 0x20;
}
double level = (double) i2c_read_uint16(_address);
if (level == 0xFFFF) {
_error = SENSOR_ERROR_CRC;
_run_configure = true;
return 0;
}
return level / 1.2;
}
unsigned char _mode;
bool _run_configure = false;
double _lux = 0;
};
#endif // SENSOR_SUPPORT && SI7021_SUPPORT
#endif // SENSOR_SUPPORT && BH1750_SUPPORT

+ 1
- 0
code/espurna/sensors/BMX280Sensor.h View File

@ -116,6 +116,7 @@ class BMX280Sensor : public I2CSensor {
_error = SENSOR_ERROR_UNKNOWN_ID;
return;
}
_error = SENSOR_ERROR_OK;
#if BMX280_MODE == 1
_forceRead();


+ 1
- 0
code/espurna/sensors/DHTSensor.h View File

@ -80,6 +80,7 @@ class DHTSensor : public BaseSensor {
// Pre-read hook (usually to populate registers with up-to-date data)
void pre() {
_error = SENSOR_ERROR_OK;
_read();
}


+ 5
- 0
code/espurna/sensors/DallasSensor.h View File

@ -209,6 +209,11 @@ class DallasSensor : public BaseSensor {
return MAGNITUDE_NONE;
}
// Pre-read hook (usually to populate registers with up-to-date data)
void pre() {
_error = SENSOR_ERROR_OK;
}
// Current value for slot # index
double value(unsigned char index) {


+ 2
- 0
code/espurna/sensors/EmonADC121Sensor.h View File

@ -94,6 +94,8 @@ class EmonADC121Sensor : public EmonSensor {
last = millis();
#endif
_error = SENSOR_ERROR_OK;
}
// Type for slot # index


+ 1
- 0
code/espurna/sensors/EmonADS1X15Sensor.h View File

@ -235,6 +235,7 @@ class EmonADS1X15Sensor : public EmonSensor {
#endif
}
last = millis();
_error = SENSOR_ERROR_OK;
}
// Current value for slot # index


+ 2
- 0
code/espurna/sensors/EmonAnalogSensor.h View File

@ -94,6 +94,8 @@ class EmonAnalogSensor : public EmonSensor {
last = millis();
#endif
_error = SENSOR_ERROR_OK;
}
// Current value for slot # index


+ 2
- 0
code/espurna/sensors/MHZ19Sensor.h View File

@ -197,6 +197,8 @@ class MHZ19Sensor : public BaseSensor {
_error = SENSOR_ERROR_OUT_OF_RANGE;
}
} else {
_error = SENSOR_ERROR_CRC;
}
}


+ 2
- 0
code/espurna/sensors/SHT3XI2CSensor.h View File

@ -61,6 +61,8 @@ class SHT3XI2CSensor : public I2CSensor {
// Pre-read hook (usually to populate registers with up-to-date data)
void pre() {
_error = SENSOR_ERROR_OK;
unsigned char buffer[6];
i2c_write_uint8(_address, 0x2C, 0x06);
delay(500);


+ 58
- 36
code/espurna/sensors/SI7021Sensor.h View File

@ -20,6 +20,9 @@
#define SI7021_CMD_TMP_NOHOLD 0xF3
#define SI7021_CMD_HUM_NOHOLD 0xF5
PROGMEM const char si7021_chip_si7021_name[] = "SI7021";
PROGMEM const char si7021_chip_htu21d_name[] = "HTU21D";
class SI7021Sensor : public I2CSensor {
public:
@ -47,23 +50,22 @@ class SI7021Sensor : public I2CSensor {
_address = _begin_i2c(_address, sizeof(addresses), addresses);
if (_address == 0) return;
// Check device
i2c_write_uint8(_address, 0xFC, 0xC9);
_chip = i2c_read_uint8(_address);
if ((_chip != SI7021_CHIP_SI7021) & (_chip != SI7021_CHIP_HTU21D)) {
i2cReleaseLock(_address);
_error = SENSOR_ERROR_UNKNOWN_ID;
} else {
_count = 2;
}
// Initialize sensor
_init();
}
// Descriptive name of the sensor
String description() {
char name[10];
strncpy_P(name,
_chip == SI7021_CHIP_SI7021 ?
si7021_chip_si7021_name :
si7021_chip_htu21d_name,
sizeof(name)
);
char buffer[25];
snprintf(buffer, sizeof(buffer), "%s @ I2C (0x%02X)", chipAsString().c_str(), _address);
snprintf(buffer, sizeof(buffer), "%s @ I2C (0x%02X)", name, _address);
return String(buffer);
}
@ -74,30 +76,38 @@ class SI7021Sensor : public I2CSensor {
// Type for slot # index
unsigned char type(unsigned char index) {
if (index < _count) {
_error = SENSOR_ERROR_OK;
if (index == 0) return MAGNITUDE_TEMPERATURE;
if (index == 1) return MAGNITUDE_HUMIDITY;
}
_error = SENSOR_ERROR_OK;
if (index == 0) return MAGNITUDE_TEMPERATURE;
if (index == 1) return MAGNITUDE_HUMIDITY;
_error = SENSOR_ERROR_OUT_OF_RANGE;
return MAGNITUDE_NONE;
}
// Pre-read hook (usually to populate registers with up-to-date data)
void pre() {
_error = SENSOR_ERROR_UNKNOWN_ID;
if (_chip == 0) return;
_error = SENSOR_ERROR_OK;
double value;
value = _read(SI7021_CMD_TMP_NOHOLD);
if (_error != SENSOR_ERROR_OK) return;
_temperature = (175.72 * value / 65536) - 46.85;
value = _read(SI7021_CMD_HUM_NOHOLD);
if (_error != SENSOR_ERROR_OK) return;
value = (125.0 * value / 65536) - 6;
_humidity = constrain(value, 0, 100);
}
// Current value for slot # index
double value(unsigned char index) {
if (index < _count) {
double value;
if (index == 0) {
value = read(SI7021_CMD_TMP_NOHOLD);
value = (175.72 * value / 65536) - 46.85;
}
if (index == 1) {
value = read(SI7021_CMD_HUM_NOHOLD);
value = (125.0 * value / 65536) - 6;
value = constrain(value, 0, 100);
}
return value;
}
_error = SENSOR_ERROR_OK;
if (index == 0) return _temperature;
if (index == 1) return _humidity;
_error = SENSOR_ERROR_OUT_OF_RANGE;
return 0;
}
@ -108,7 +118,23 @@ class SI7021Sensor : public I2CSensor {
// Protected
// ---------------------------------------------------------------------
unsigned int read(uint8_t command) {
void _init() {
// Check device
i2c_write_uint8(_address, 0xFC, 0xC9);
_chip = i2c_read_uint8(_address);
if ((_chip != SI7021_CHIP_SI7021) & (_chip != SI7021_CHIP_HTU21D)) {
i2cReleaseLock(_address);
_error = SENSOR_ERROR_UNKNOWN_ID;
_count = 0;
} else {
_count = 2;
}
}
unsigned int _read(uint8_t command) {
// Request measurement
i2c_write_uint8(_address, command);
@ -131,13 +157,9 @@ class SI7021Sensor : public I2CSensor {
}
String chipAsString() {
if (_chip == SI7021_CHIP_SI7021) return String("SI7021");
if (_chip == SI7021_CHIP_HTU21D) return String("HTU21D");
return String("Unknown");
}
unsigned char _chip;
double _temperature = 0;
double _humidity = 0;
};


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

@ -41,7 +41,7 @@ bool _settings_save = false;
// Reverse engineering EEPROM storage format
// -----------------------------------------------------------------------------
unsigned long _settingsSize() {
unsigned long settingsSize() {
unsigned pos = SPI_FLASH_SEC_SIZE - 1;
while (size_t len = EEPROM.read(pos)) {
pos = pos - len - 2;
@ -49,6 +49,8 @@ unsigned long _settingsSize() {
return SPI_FLASH_SEC_SIZE - pos;
}
// -----------------------------------------------------------------------------
unsigned int _settingsKeyCount() {
unsigned count = 0;
unsigned pos = SPI_FLASH_SEC_SIZE - 1;
@ -156,7 +158,7 @@ void _settingsKeys() {
DEBUG_MSG_P(PSTR("> %s => %s\n"), (keys[i]).c_str(), value.c_str());
}
unsigned long freeEEPROM = SPI_FLASH_SEC_SIZE - _settingsSize();
unsigned long freeEEPROM = SPI_FLASH_SEC_SIZE - settingsSize();
DEBUG_MSG_P(PSTR("Number of keys: %d\n"), keys.size());
DEBUG_MSG_P(PSTR("Free EEPROM: %d bytes (%d%%)\n"), freeEEPROM, 100 * freeEEPROM / SPI_FLASH_SEC_SIZE);
@ -243,7 +245,7 @@ void _settingsInitCommands() {
});
settingsRegisterCommand(F("INFO"), [](Embedis* e) {
welcome();
info();
wifiStatus();
//StreamString s;
//WiFi.printDiag(s);
@ -407,15 +409,15 @@ void settingsSetup() {
_settingsInitCommands();
DEBUG_MSG_P(PSTR("[SETTINGS] EEPROM size: %d bytes\n"), SPI_FLASH_SEC_SIZE);
DEBUG_MSG_P(PSTR("[SETTINGS] Settings size: %d bytes\n"), _settingsSize());
#if TERMINAL_SUPPORT
#ifdef SERIAL_RX_PORT
SERIAL_RX_PORT.begin(SERIAL_RX_BAUDRATE);
#endif
#endif
// Register loop
espurnaRegisterLoop(settingsLoop);
}
void settingsLoop() {


+ 3
- 0
code/espurna/ssdp.ino View File

@ -31,6 +31,9 @@ void ssdpSetup() {
});
#endif
// Register loop
espurnaRegisterLoop(ssdpLoop);
}
void ssdpLoop() {


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


+ 122
- 0
code/espurna/system.ino View File

@ -0,0 +1,122 @@
/*
SYSTEM MODULE
Copyright (C) 2018 by Xose Pérez <xose dot perez at gmail dot com>
*/
#include <EEPROM.h>
// -----------------------------------------------------------------------------
unsigned long _loopDelay = 0;
bool _system_send_heartbeat = false;
// -----------------------------------------------------------------------------
#if SYSTEM_CHECK_ENABLED
// Call this method on boot with start=true to increase the crash counter
// Call it again once the system is stable to decrease the counter
// If the counter reaches SYSTEM_CHECK_MAX then the system is flagged as unstable
// setting _systemOK = false;
//
// An unstable system will only have serial access, WiFi in AP mode and OTA
bool _systemStable = true;
void systemCheck(bool stable) {
unsigned char value = EEPROM.read(EEPROM_CRASH_COUNTER);
if (stable) {
value = 0;
DEBUG_MSG_P(PSTR("[MAIN] System OK\n"));
} else {
if (++value > SYSTEM_CHECK_MAX) {
_systemStable = false;
value = 0;
DEBUG_MSG_P(PSTR("[MAIN] System UNSTABLE\n"));
}
}
EEPROM.write(EEPROM_CRASH_COUNTER, value);
EEPROM.commit();
}
bool systemCheck() {
return _systemStable;
}
void systemCheckLoop() {
static bool checked = false;
if (!checked && (millis() > SYSTEM_CHECK_TIME)) {
// Check system as stable
systemCheck(true);
checked = true;
}
}
#endif
// -----------------------------------------------------------------------------
void systemSendHeartbeat() {
_system_send_heartbeat = true;
}
void systemLoop() {
// Check system stability
#if SYSTEM_CHECK_ENABLED
systemCheckLoop();
#endif
#if HEARTBEAT_ENABLED
// Heartbeat
static unsigned long last = 0;
if (_system_send_heartbeat || (last == 0) || (millis() - last > HEARTBEAT_INTERVAL)) {
_system_send_heartbeat = false;
last = millis();
heartbeat();
}
#endif // HEARTBEAT_ENABLED
// Power saving delay
delay(_loopDelay);
}
void systemSetup() {
EEPROM.begin(EEPROM_SIZE);
#if DEBUG_SERIAL_SUPPORT
DEBUG_PORT.begin(SERIAL_BAUDRATE);
#if DEBUG_ESP_WIFI
DEBUG_PORT.setDebugOutput(true);
#endif
#elif defined(SERIAL_BAUDRATE)
Serial.begin(SERIAL_BAUDRATE);
#endif
#if SPIFFS_SUPPORT
SPIFFS.begin();
#endif
// Question system stability
#if SYSTEM_CHECK_ENABLED
systemCheck(false);
#endif
#if defined(ESPLIVE)
//The ESPLive has an ADC MUX which needs to be configured.
pinMode(16, OUTPUT);
digitalWrite(16, HIGH); //Defualt CT input (pin B, solder jumper B)
#endif
// Cache loop delay value to speed things (recommended max 250ms)
_loopDelay = atol(getSetting("loopDelay", LOOP_DELAY_TIME).c_str());
// Register Loop
espurnaRegisterLoop(systemLoop);
}

+ 7
- 0
code/espurna/thinkspeak.ino View File

@ -242,15 +242,22 @@ bool tspkEnabled() {
}
void tspkSetup() {
_tspkConfigure();
#if WEB_SUPPORT
wsOnSendRegister(_tspkWebSocketOnSend);
wsOnAfterParseRegister(_tspkConfigure);
#endif
DEBUG_MSG_P(PSTR("[THINGSPEAK] Async %s, SSL %s\n"),
THINGSPEAK_USE_ASYNC ? "ENABLED" : "DISABLED",
THINGSPEAK_USE_SSL ? "ENABLED" : "DISABLED"
);
// Register loop
espurnaRegisterLoop(tspkLoop);
}
void tspkLoop() {


+ 272
- 105
code/espurna/utils.ino View File

@ -94,60 +94,14 @@ unsigned long getUptime() {
}
#if HEARTBEAT_ENABLED
void heartbeat() {
unsigned long uptime_seconds = getUptime();
unsigned int free_heap = getFreeHeap();
// -------------------------------------------------------------------------
// MQTT
// -------------------------------------------------------------------------
#if MQTT_SUPPORT
#if (HEARTBEAT_REPORT_INTERVAL)
mqttSend(MQTT_TOPIC_INTERVAL, HEARTBEAT_INTERVAL / 1000);
#endif
#if (HEARTBEAT_REPORT_APP)
mqttSend(MQTT_TOPIC_APP, APP_NAME);
#endif
#if (HEARTBEAT_REPORT_VERSION)
mqttSend(MQTT_TOPIC_VERSION, APP_VERSION);
#endif
#if (HEARTBEAT_REPORT_HOSTNAME)
mqttSend(MQTT_TOPIC_HOSTNAME, getSetting("hostname").c_str());
#endif
#if (HEARTBEAT_REPORT_IP)
mqttSend(MQTT_TOPIC_IP, getIP().c_str());
#endif
#if (HEARTBEAT_REPORT_MAC)
mqttSend(MQTT_TOPIC_MAC, WiFi.macAddress().c_str());
#endif
#if (HEARTBEAT_REPORT_RSSI)
mqttSend(MQTT_TOPIC_RSSI, String(WiFi.RSSI()).c_str());
#endif
#if (HEARTBEAT_REPORT_UPTIME)
mqttSend(MQTT_TOPIC_UPTIME, String(uptime_seconds).c_str());
#endif
#if (HEARTBEAT_REPORT_DATETIME) & (NTP_SUPPORT)
mqttSend(MQTT_TOPIC_DATETIME, String(ntpDateTime()).c_str());
#endif
#if (HEARTBEAT_REPORT_FREEHEAP)
mqttSend(MQTT_TOPIC_FREEHEAP, String(free_heap).c_str());
#endif
#if (HEARTBEAT_REPORT_RELAY)
relayMQTT();
#endif
#if (LIGHT_PROVIDER != LIGHT_PROVIDER_NONE) & (HEARTBEAT_REPORT_LIGHT)
lightMQTT();
#endif
#if (HEARTBEAT_REPORT_VCC)
#if ADC_VCC_ENABLED
mqttSend(MQTT_TOPIC_VCC, String(ESP.getVcc()).c_str());
#endif
#endif
#if (HEARTBEAT_REPORT_STATUS)
mqttSend(MQTT_TOPIC_STATUS, MQTT_STATUS_ONLINE, true);
#endif
bool serial = !mqttConnected();
#else
bool serial = true;
@ -163,9 +117,62 @@ void heartbeat() {
#if ADC_VCC_ENABLED
DEBUG_MSG_P(PSTR("[MAIN] Power: %lu mV\n"), ESP.getVcc());
#endif
#if NTP_SUPPORT
if (ntpSynced()) DEBUG_MSG_P(PSTR("[MAIN] Time: %s\n"), (char *) ntpDateTime().c_str());
#endif
}
#if NTP_SUPPORT
DEBUG_MSG_P(PSTR("[MAIN] Time: %s\n"), (char *) ntpDateTime().c_str());
// -------------------------------------------------------------------------
// MQTT
// -------------------------------------------------------------------------
#if MQTT_SUPPORT
if (!serial) {
#if (HEARTBEAT_REPORT_INTERVAL)
mqttSend(MQTT_TOPIC_INTERVAL, HEARTBEAT_INTERVAL / 1000);
#endif
#if (HEARTBEAT_REPORT_APP)
mqttSend(MQTT_TOPIC_APP, APP_NAME);
#endif
#if (HEARTBEAT_REPORT_VERSION)
mqttSend(MQTT_TOPIC_VERSION, APP_VERSION);
#endif
#if (HEARTBEAT_REPORT_HOSTNAME)
mqttSend(MQTT_TOPIC_HOSTNAME, getSetting("hostname").c_str());
#endif
#if (HEARTBEAT_REPORT_IP)
mqttSend(MQTT_TOPIC_IP, getIP().c_str());
#endif
#if (HEARTBEAT_REPORT_MAC)
mqttSend(MQTT_TOPIC_MAC, WiFi.macAddress().c_str());
#endif
#if (HEARTBEAT_REPORT_RSSI)
mqttSend(MQTT_TOPIC_RSSI, String(WiFi.RSSI()).c_str());
#endif
#if (HEARTBEAT_REPORT_UPTIME)
mqttSend(MQTT_TOPIC_UPTIME, String(uptime_seconds).c_str());
#endif
#if (HEARTBEAT_REPORT_DATETIME) && (NTP_SUPPORT)
if (ntpSynced()) mqttSend(MQTT_TOPIC_DATETIME, ntpDateTime().c_str());
#endif
#if (HEARTBEAT_REPORT_FREEHEAP)
mqttSend(MQTT_TOPIC_FREEHEAP, String(free_heap).c_str());
#endif
#if (HEARTBEAT_REPORT_RELAY)
relayMQTT();
#endif
#if (LIGHT_PROVIDER != LIGHT_PROVIDER_NONE) & (HEARTBEAT_REPORT_LIGHT)
lightMQTT();
#endif
#if (HEARTBEAT_REPORT_VCC)
#if ADC_VCC_ENABLED
mqttSend(MQTT_TOPIC_VCC, String(ESP.getVcc()).c_str());
#endif
#endif
#if (HEARTBEAT_REPORT_STATUS)
mqttSend(MQTT_TOPIC_STATUS, MQTT_STATUS_ONLINE, true);
#endif
}
#endif
// -------------------------------------------------------------------------
@ -181,24 +188,228 @@ void heartbeat() {
#endif
#endif
}
#endif /// HEARTBEAT_ENABLED
unsigned int sectors(size_t size) {
return (int) (size + SPI_FLASH_SEC_SIZE - 1) / SPI_FLASH_SEC_SIZE;
}
void info() {
DEBUG_MSG_P(PSTR("\n\n"));
DEBUG_MSG_P(PSTR("[INIT] %s %s\n"), (char *) APP_NAME, (char *) APP_VERSION);
DEBUG_MSG_P(PSTR("[INIT] %s\n"), (char *) APP_AUTHOR);
DEBUG_MSG_P(PSTR("[INIT] %s\n\n"), (char *) APP_WEBSITE);
DEBUG_MSG_P(PSTR("[INIT] CPU chip ID: 0x%06X\n"), ESP.getChipId());
DEBUG_MSG_P(PSTR("[INIT] CPU frequency: %u MHz\n"), ESP.getCpuFreqMHz());
DEBUG_MSG_P(PSTR("[INIT] SDK version: %s\n"), ESP.getSdkVersion());
DEBUG_MSG_P(PSTR("[INIT] Core version: %s\n"), getCoreVersion().c_str());
DEBUG_MSG_P(PSTR("[INIT] Core revision: %s\n"), getCoreRevision().c_str());
DEBUG_MSG_P(PSTR("\n"));
// -------------------------------------------------------------------------
// WebSockets
FlashMode_t mode = ESP.getFlashChipMode();
DEBUG_MSG_P(PSTR("[INIT] Flash chip ID: 0x%06X\n"), ESP.getFlashChipId());
DEBUG_MSG_P(PSTR("[INIT] Flash speed: %u Hz\n"), ESP.getFlashChipSpeed());
DEBUG_MSG_P(PSTR("[INIT] Flash mode: %s\n"), mode == FM_QIO ? "QIO" : mode == FM_QOUT ? "QOUT" : mode == FM_DIO ? "DIO" : mode == FM_DOUT ? "DOUT" : "UNKNOWN");
DEBUG_MSG_P(PSTR("\n"));
DEBUG_MSG_P(PSTR("[INIT] Flash sector size: %8u bytes\n"), SPI_FLASH_SEC_SIZE);
DEBUG_MSG_P(PSTR("[INIT] Flash size (CHIP): %8u bytes\n"), ESP.getFlashChipRealSize());
DEBUG_MSG_P(PSTR("[INIT] Flash size (SDK): %8u bytes / %4d sectors\n"), ESP.getFlashChipSize(), sectors(ESP.getFlashChipSize()));
DEBUG_MSG_P(PSTR("[INIT] Firmware size: %8u bytes / %4d sectors\n"), ESP.getSketchSize(), sectors(ESP.getSketchSize()));
DEBUG_MSG_P(PSTR("[INIT] OTA size: %8u bytes / %4d sectors\n"), ESP.getFreeSketchSpace(), sectors(ESP.getFreeSketchSpace()));
#if SPIFFS_SUPPORT
FSInfo fs_info;
bool fs = SPIFFS.info(fs_info);
if (fs) {
DEBUG_MSG_P(PSTR("[INIT] SPIFFS size: %8u bytes / %4d sectors\n"), fs_info.totalBytes, sectors(fs_info.totalBytes));
}
#else
DEBUG_MSG_P(PSTR("[INIT] SPIFFS size: %8u bytes / %4d sectors\n"), 0, 0);
#endif
DEBUG_MSG_P(PSTR("[INIT] EEPROM size: %8u bytes / %4d sectors\n"), settingsMaxSize(), sectors(settingsMaxSize()));
DEBUG_MSG_P(PSTR("[INIT] Empty space: %8u bytes / 4 sectors\n"), 4 * SPI_FLASH_SEC_SIZE);
DEBUG_MSG_P(PSTR("\n"));
// -------------------------------------------------------------------------
#if WEB_SUPPORT
#if SPIFFS_SUPPORT
if (fs) {
DEBUG_MSG_P(PSTR("[INIT] SPIFFS total size: %8u bytes\n"), fs_info.totalBytes);
DEBUG_MSG_P(PSTR("[INIT] used size: %8u bytes\n"), fs_info.usedBytes);
DEBUG_MSG_P(PSTR("[INIT] block size: %8u bytes\n"), fs_info.blockSize);
DEBUG_MSG_P(PSTR("[INIT] page size: %8u bytes\n"), fs_info.pageSize);
DEBUG_MSG_P(PSTR("[INIT] max files: %8u\n"), fs_info.maxOpenFiles);
DEBUG_MSG_P(PSTR("[INIT] max length: %8u\n"), fs_info.maxPathLength);
} else {
DEBUG_MSG_P(PSTR("[INIT] No SPIFFS partition\n"));
}
DEBUG_MSG_P(PSTR("\n"));
#endif
// -------------------------------------------------------------------------
DEBUG_MSG_P(PSTR("[INIT] BOARD: %s\n"), getBoardName().c_str());
DEBUG_MSG_P(PSTR("[INIT] SUPPORT:"));
#if ALEXA_SUPPORT
DEBUG_MSG_P(PSTR(" ALEXA"));
#endif
#if BROKER_SUPPORT
DEBUG_MSG_P(PSTR(" BROKER"));
#endif
#if DEBUG_SERIAL_SUPPORT
DEBUG_MSG_P(PSTR(" DEBUG_SERIAL"));
#endif
#if DEBUG_TELNET_SUPPORT
DEBUG_MSG_P(PSTR(" DEBUG_TELNET"));
#endif
#if DEBUG_UDP_SUPPORT
DEBUG_MSG_P(PSTR(" DEBUG_UDP"));
#endif
#if DOMOTICZ_SUPPORT
DEBUG_MSG_P(PSTR(" DOMOTICZ"));
#endif
#if HOMEASSISTANT_SUPPORT
DEBUG_MSG_P(PSTR(" HOMEASSISTANT"));
#endif
#if I2C_SUPPORT
DEBUG_MSG_P(PSTR(" I2C"));
#endif
#if INFLUXDB_SUPPORT
DEBUG_MSG_P(PSTR(" INFLUXDB"));
#endif
#if LLMNR_SUPPORT
DEBUG_MSG_P(PSTR(" LLMNR"));
#endif
#if MDNS_SERVER_SUPPORT
DEBUG_MSG_P(PSTR(" MDNS_SERVER"));
#endif
#if MDNS_CLIENT_SUPPORT
DEBUG_MSG_P(PSTR(" MDNS_CLIENT"));
#endif
#if NETBIOS_SUPPORT
DEBUG_MSG_P(PSTR(" NETBIOS"));
#endif
#if NOFUSS_SUPPORT
DEBUG_MSG_P(PSTR(" NOFUSS"));
#endif
#if NTP_SUPPORT
{
char buffer[200];
snprintf_P(
buffer,
sizeof(buffer) - 1,
PSTR("{\"time\": \"%s\", \"uptime\": %lu, \"heap\": %lu}"),
ntpDateTime().c_str(), uptime_seconds, free_heap
);
wsSend(buffer);
DEBUG_MSG_P(PSTR(" NTP"));
#endif
#if RF_SUPPORT
DEBUG_MSG_P(PSTR(" RF"));
#endif
#if SCHEDULER_SUPPORT
DEBUG_MSG_P(PSTR(" SCHEDULER"));
#endif
#if SENSOR_SUPPORT
DEBUG_MSG_P(PSTR(" SENSOR"));
#endif
#if SPIFFS_SUPPORT
DEBUG_MSG_P(PSTR(" SPIFFS"));
#endif
#if SSDP_SUPPORT
DEBUG_MSG_P(PSTR(" SSDP"));
#endif
#if TELNET_SUPPORT
DEBUG_MSG_P(PSTR(" TELNET"));
#endif
#if TERMINAL_SUPPORT
DEBUG_MSG_P(PSTR(" TERMINAL"));
#endif
#if THINGSPEAK_SUPPORT
DEBUG_MSG_P(PSTR(" THINGSPEAK"));
#endif
#if WEB_SUPPORT
DEBUG_MSG_P(PSTR(" WEB"));
#endif
#if SENSOR_SUPPORT
DEBUG_MSG_P(PSTR("\n[INIT] SENSORS:"));
#if ANALOG_SUPPORT
DEBUG_MSG_P(PSTR(" ANALOG"));
#endif
#if BMX280_SUPPORT
DEBUG_MSG_P(PSTR(" BMX280"));
#endif
#if DALLAS_SUPPORT
DEBUG_MSG_P(PSTR(" DALLAS"));
#endif
#if DHT_SUPPORT
DEBUG_MSG_P(PSTR(" DHTXX"));
#endif
#if DIGITAL_SUPPORT
DEBUG_MSG_P(PSTR(" DIGITAL"));
#endif
#if ECH1560_SUPPORT
DEBUG_MSG_P(PSTR(" ECH1560"));
#endif
#if EMON_ADC121_SUPPORT
DEBUG_MSG_P(PSTR(" EMON_ADC121"));
#endif
#if EMON_ADS1X15_SUPPORT
DEBUG_MSG_P(PSTR(" EMON_ADX1X15"));
#endif
#if EMON_ANALOG_SUPPORT
DEBUG_MSG_P(PSTR(" EMON_ANALOG"));
#endif
#if EVENTS_SUPPORT
DEBUG_MSG_P(PSTR(" EVENTS"));
#endif
#if HLW8012_SUPPORT
DEBUG_MSG_P(PSTR(" HLW8012"));
#endif
#if MHZ19_SUPPORT
DEBUG_MSG_P(PSTR(" MHZ19"));
#endif
#if PMSX003_SUPPORT
DEBUG_MSG_P(PSTR(" PMSX003"));
#endif
#if SHT3X_I2C_SUPPORT
DEBUG_MSG_P(PSTR(" SHT3X_I2C"));
#endif
#if SI7021_SUPPORT
DEBUG_MSG_P(PSTR(" SI7021"));
#endif
#if V9261F_SUPPORT
DEBUG_MSG_P(PSTR(" V9261F"));
#endif
#endif // SENSOR_SUPPORT
DEBUG_MSG_P(PSTR("\n\n"));
// -------------------------------------------------------------------------
unsigned char reason = resetReason();
if (reason > 0) {
char buffer[32];
strcpy_P(buffer, custom_reset_string[reason-1]);
DEBUG_MSG_P(PSTR("[INIT] Last reset reason: %s\n"), buffer);
} else {
DEBUG_MSG_P(PSTR("[INIT] Last reset reason: %s\n"), (char *) ESP.getResetReason().c_str());
}
DEBUG_MSG_P(PSTR("[INIT] Settings size: %u bytes\n"), settingsSize());
DEBUG_MSG_P(PSTR("[INIT] Free heap: %u bytes\n"), getFreeHeap());
#if ADC_VCC_ENABLED
DEBUG_MSG_P(PSTR("[INIT] Power: %u mV\n"), ESP.getVcc());
#endif
DEBUG_MSG_P(PSTR("[INIT] Power saving delay value: %lu ms\n"), _loopDelay);
#if SYSTEM_CHECK_ENABLED
if (!systemCheck()) DEBUG_MSG_P(PSTR("\n[INIT] Device is in SAFE MODE\n"));
#endif
DEBUG_MSG_P(PSTR("\n"));
}
// -----------------------------------------------------------------------------
@ -274,50 +485,6 @@ void deferredReset(unsigned long delay, unsigned char reason) {
// -----------------------------------------------------------------------------
#if SYSTEM_CHECK_ENABLED
// Call this method on boot with start=true to increase the crash counter
// Call it again once the system is stable to decrease the counter
// If the counter reaches SYSTEM_CHECK_MAX then the system is flagged as unstable
// setting _systemOK = false;
//
// An unstable system will only have serial access, WiFi in AP mode and OTA
bool _systemStable = true;
void systemCheck(bool stable) {
unsigned char value = EEPROM.read(EEPROM_CRASH_COUNTER);
if (stable) {
value = 0;
DEBUG_MSG_P(PSTR("[MAIN] System OK\n"));
} else {
if (++value > SYSTEM_CHECK_MAX) {
_systemStable = false;
value = 0;
DEBUG_MSG_P(PSTR("[MAIN] System UNSTABLE\n"));
}
}
EEPROM.write(EEPROM_CRASH_COUNTER, value);
EEPROM.commit();
}
bool systemCheck() {
return _systemStable;
}
void systemCheckLoop() {
static bool checked = false;
if (!checked && (millis() > SYSTEM_CHECK_TIME)) {
// Check system as stable
systemCheck(true);
checked = true;
}
}
#endif
// -----------------------------------------------------------------------------
char * ltrim(char * s) {
char *p = s;
while ((unsigned char) *p == ' ') ++p;


+ 3
- 0
code/espurna/wifi.ino View File

@ -447,6 +447,9 @@ void wifiSetup() {
_wifiInitCommands();
#endif
// Register loop
espurnaRegisterLoop(wifiLoop);
}
void wifiLoop() {


+ 22
- 4
code/espurna/ws.ino View File

@ -200,6 +200,16 @@ void _wsParse(AsyncWebSocketClient *client, uint8_t * payload, size_t length) {
}
void _wsUpdate(JsonObject& root) {
root["heap"] = getFreeHeap();
root["uptime"] = getUptime();
root["rssi"] = WiFi.RSSI();
root["distance"] = wifiDistance(WiFi.RSSI());
#if NTP_SUPPORT
if (ntpSynced()) root["now"] = now();
#endif
}
void _wsOnStart(JsonObject& root) {
#if USE_PASSWORD && WEB_FORCE_PASS_CHANGE
@ -234,17 +244,15 @@ void _wsOnStart(JsonObject& root) {
root["mac"] = WiFi.macAddress();
root["bssid"] = String(bssid_str);
root["channel"] = WiFi.channel();
root["rssi"] = WiFi.RSSI();
root["distance"] = wifiDistance(WiFi.RSSI());
root["device"] = DEVICE;
root["hostname"] = getSetting("hostname");
root["network"] = getNetwork();
root["deviceip"] = getIP();
root["uptime"] = getUptime();
root["heap"] = getFreeHeap();
root["sketch_size"] = ESP.getSketchSize();
root["free_size"] = ESP.getFreeSketchSpace();
_wsUpdate(root);
root["btnDelay"] = getSetting("btnDelay", BUTTON_DBLCLICK_DELAY).toInt();
root["webPort"] = getSetting("webPort", WEB_PORT).toInt();
root["tmpUnits"] = getSetting("tmpUnits", SENSOR_TEMPERATURE_UNITS).toInt();
@ -292,6 +300,15 @@ void _wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTy
}
void _wsLoop() {
static unsigned long last = 0;
if (!wsConnected()) return;
if (millis() - last > WS_UPDATE_INTERVAL) {
last = millis();
wsSend(_wsUpdate);
}
}
// -----------------------------------------------------------------------------
// Piblic API
// -----------------------------------------------------------------------------
@ -371,6 +388,7 @@ void wsSetup() {
#endif
wsOnSendRegister(_wsOnStart);
wsOnAfterParseRegister(wsConfigure);
espurnaRegisterLoop(_wsLoop);
}
#endif // WEB_SUPPORT

+ 179
- 119
code/html/custom.css View File

@ -1,145 +1,99 @@
/* -----------------------------------------------------------------------------
General
-------------------------------------------------------------------------- */
#menu .pure-menu-heading {
font-size: 100%;
padding: .5em .5em;
}
.header h2 {
font-size: 1em;
}
.panel {
display: none;
}
.footer {
padding: 10px;
font-size: 80%;
color: #999;
}
#menu .footer a {
text-decoration: none;
padding: 0px;
}
.content {
margin: 0px;
}
.page {
margin-top: 10px;
}
.pure-button {
color: white;
padding: 8px 8px;
border-radius: 4px;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
.main-buttons {
margin: 50px auto;
text-align: center;
}
.main-buttons button {
width: 100px;
margin: 5px auto;
}
.button-update-password,
.button-update {
background: #1f8dd6;
}
.button-reboot,
.button-reconnect,
.button-ha-del,
.button-rfb-forget {
background: rgb(202, 60, 60);
}
.button-upgrade {
background: rgb(202, 60, 60);
margin-left: 5px;
}
.button-upgrade-browse,
.button-ha-add,
.button-apikey {
background: rgb(0, 202, 0);
margin-left: 5px;
}
.button-add-network,
.button-add-schedule,
.button-rfb-learn {
background: rgb(28, 184, 65);
}
.button-del-network,
.button-del-schedule {
background: rgb(202, 60, 60);
}
.pure-button {
letter-spacing: 0;
}
.button-more-network,
.button-more-schedule,
.button-wifi-scan,
.button-rfb-send {
background: rgb(223, 117, 20);
}
.button-settings-backup,
.button-settings-restore {
background: rgb(0, 202, 0);
margin-bottom: 10px;
}
.pure-g {
margin-bottom: 0px;
}
.pure-form legend {
font-weight: bold;
letter-spacing: 0;
margin: 10px 0 1em 0;
}
.l-box {
padding-right: 1px;
.pure-form .pure-g > label {
margin: .4em 0 .2em;
}
.pure-form input {
margin-bottom: 10px;
}
.pure-form input[type=text][disabled] {
color: #777777;
}
.header h2 {
font-size: 1em;
}
.panel {
display: none;
}
.content {
margin: 0px;
}
.page {
margin-top: 10px;
}
div.hint {
font-size: 80%;
color: #ccc;
margin: -10px 0 10px 0;
}
.break {
margin-top: 5px;
}
#networks .pure-g,
#schedules .pure-g {
padding: 10px 0 10px 0;
margin-bottom: 10px;
border-bottom: 1px solid #eee;
}
#networks .more {
display: none;
}
legend.module,
.module {
display: none;
}
.template {
display: none;
}
input[name=upgrade] {
display: none;
}
#upgrade-progress {
display: none;
.panel.block {
display: block;
}
select {
width: 100%;
height: 20px;
margin-top: 10px;
margin-bottom: 10px;
}
hr {
display: block;
height: 1px;
border: 0;
border-top: 1px solid #eee;
margin: 1em 0;
padding: 0;
}
input.center {
margin-bottom: 0px;
}
div.center {
margin: .5em 0 1em;
}
.webmode {
display: none;
}
#credentials {
font-size: 200%;
text-align: center;
@ -151,6 +105,7 @@ div.center {
margin-top: -50px;
margin-left: -200px;
}
div.state {
border-top: 1px solid #eee;
margin-top: 20px;
@ -165,65 +120,170 @@ div.state {
font-size: 80%;
font-weight: bold;
}
.right {
text-align: right;
}
/* -----------------------------------------------------------------------------
Buttons
-------------------------------------------------------------------------- */
.pure-button {
color: white;
padding: 8px 8px;
border-radius: 4px;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
letter-spacing: 0;
margin-bottom: 10px;
}
.main-buttons {
margin: 20px auto;
text-align: center;
}
.main-buttons button {
width: 100px;
}
.button-reboot,
.button-reconnect,
.button-ha-del,
.button-rfb-forget,
.button-del-network,
.button-del-schedule,
.button-upgrade {
background: rgb(192, 0, 0); /* redish */
}
.button-update,
.button-update-password,
.button-add-network,
.button-add-schedule,
.button-rfb-learn,
.button-upgrade-browse,
.button-ha-add,
.button-settings-backup,
.button-settings-restore,
.button-apikey {
background: rgb(0, 192, 0); /* green */
}
.button-more-network,
.button-more-schedule,
.button-wifi-scan,
.button-rfb-send {
background: rgb(255, 128, 0); /* orange */
}
.button-upgrade-browse,
.button-ha-add,
.button-apikey,
.button-upgrade {
margin-left: 5px;
}
/* -----------------------------------------------------------------------------
Sliders
-------------------------------------------------------------------------- */
input.slider {
margin-top: 10px;
}
span.slider {
font-size: 70%;
margin-left: 10px;
margin-top: 7px;
letter-spacing: 0;
}
/* -----------------------------------------------------------------------------
Loading
-------------------------------------------------------------------------- */
div.loading {
background-image: url('images/loading.gif');
width: 20px;
height: 20px;
margin: 8px 0 0 10px;
display: none;
}
/* -----------------------------------------------------------------------------
Menu
-------------------------------------------------------------------------- */
#menu span.small {
font-size: 60%;
padding-left: 9px;
}
#menu div.footer {
padding: 10px;
font-size: 80%;
color: #999;
}
#menu div.footer a {
text-decoration: none;
padding: 0px;
}
/* -----------------------------------------------------------------------------
RF Bridge panel
-------------------------------------------------------------------------- */
#panel-rfb fieldset {
margin: 10px 2px;
padding: 20px;
}
#panel-rfb input {
margin-right: 5px;
}
#panel-rfb label {
padding-top: 5px;
}
#panel-rfb input {
text-align: center;
}
/* -----------------------------------------------------------------------------
Admin panel
-------------------------------------------------------------------------- */
#upgrade-progress {
display: none;
width: 100%;
height: 20px;
margin-top: 10px;
}
#uploader,
#downloader {
display: none;
}
.panel.block {
display: block;
}
select {
width: 100%;
/* -----------------------------------------------------------------------------
Wifi panel
-------------------------------------------------------------------------- */
#networks .pure-g,
#schedules .pure-g {
padding: 10px 0 10px 0;
margin-bottom: 10px;
border-bottom: 1px solid #eee;
}
hr {
display: block;
height: 1px;
border: 0;
border-top: 1px solid #eee;
margin: 1em 0;
padding: 0;
#networks .more {
display: none;
}
#scanResult {
margin-top: 10px;
font-size: 60%;
color: #888;
font-family: 'Courier New', monospace;
}
div.loading {
background-image: url('images/loading.gif');
width: 20px;
height: 20px;
margin: 8px 0 0 10px;
display: none;
}
#menu span.small {
font-size: 60%;
padding-left: 9px;
}

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

@ -13,6 +13,9 @@ var numReload = 0;
var useWhite = false;
var manifest;
var now = 0;
var ago = 0;
// -----------------------------------------------------------------------------
// Messages
// -----------------------------------------------------------------------------
@ -71,6 +74,17 @@ function magnitudeError(error) {
// Utils
// -----------------------------------------------------------------------------
function keepTime() {
if (now === 0) return;
var date = new Date(now * 1000);
var text = date.toISOString().substring(0, 19).replace("T", " ");
$("input[name='now']").val(text);
$("span[name='now']").html(text);
$("span[name='ago']").html(ago);
now++;
ago++;
}
// http://www.the-art-of-web.com/javascript/validate-password/
function checkPassword(str) {
// at least one lowercase and one uppercase letter or number
@ -87,6 +101,34 @@ function zeroPad(number, positions) {
return (zeros + number).slice(-positions);
}
function loadTimeZones() {
var time_zones = [
-720, -660, -600, -570, -540,
-480, -420, -360, -300, -240,
-210, -180, -120, -60, 0,
60, 120, 180, 210, 240,
270, 300, 330, 345, 360,
390, 420, 480, 510, 525,
540, 570, 600, 630, 660,
720, 765, 780, 840
];
for (var i in time_zones) {
var value = parseInt(time_zones[i], 10);
var offset = value >= 0 ? value : -value;
var text = "GMT" + (value >= 0 ? "+" : "-") +
zeroPad(parseInt(offset / 60, 10), 2) + ":" +
zeroPad(offset % 60, 2);
$("select[name='ntpOffset']").append(
$("<option></option>").
attr("value",value).
text(text)
);
}
}
function validateForm(form) {
// password
@ -1037,6 +1079,12 @@ function processData(data) {
return;
}
if (key === "now") {
now = data[key];
ago = 0;
return;
}
// Pre-process
if (key === "network") {
data.network = data.network.toUpperCase();
@ -1169,6 +1217,8 @@ function connect(host) {
$(function() {
initMessages();
loadTimeZones();
setInterval(function() { keepTime(); }, 1000);
$("#menuLink").on("click", toggleMenu);
$(".pure-menu-link").on("click", showPanel);


+ 33
- 38
code/html/index.html View File

@ -51,7 +51,8 @@
<input name="adminPass" class="pure-u-1 pure-u-lg-3-4" type="password" tabindex="2" autocomplete="false" />
</div>
<button class="pure-button button-update-password">Update</button>
<div class="pure-u-0 pure-u-lg-1-4 more"></div>
<button class="pure-button button-update-password" type="button">Update</button>
</fieldset>
</div>
@ -213,7 +214,7 @@
<div class="pure-u-11-24 pure-u-lg-17-24"><span class="right" name="app_build"></span></div>
<div class="pure-u-1-2 pure-u-lg-1-4">Current time</div>
<div class="pure-u-11-24 pure-u-lg-17-24"><span class="right" name="time"></span></div>
<div class="pure-u-11-24 pure-u-lg-17-24"><span class="right" name="now"></span></div>
<div class="pure-u-1-2 pure-u-lg-1-4">Uptime</div>
<div class="pure-u-11-24 pure-u-lg-17-24"><span class="right" name="uptime"></span></div>
@ -233,6 +234,8 @@
<div class="pure-u-1-2 pure-u-lg-1-4">NTP Status</div>
<div class="pure-u-11-24 pure-u-lg-17-24"><span class="right" name="ntpStatus">NOT AVAILABLE</span></div>
<div class="pure-u-1-2 pure-u-lg-1-4">Last update</div>
<div class="pure-u-11-24 pure-u-lg-17-24"><span class="right" name="ago">?</span><span> seconds ago</span></div>
</div>
@ -302,12 +305,12 @@
</div>
<div class="pure-g module module-alexa">
<div class="pure-u-1 pure-u-lg-1-4"><label>Alexa integration</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Alexa integration</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="alexaEnabled" tabindex="13" /></div>
</div>
<div class="pure-g module module-ha">
<div class="pure-u-1 pure-u-lg-1-4"><label>Home Assistant</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Home Assistant</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="haEnabled" tabindex="14" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
@ -370,7 +373,7 @@
<fieldset>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Use colorpicker</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Use colorpicker</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="useColor" action="reload" tabindex="8" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
@ -378,7 +381,7 @@
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Use RGB picker</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Use RGB picker</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="useRGB" action="reload" tabindex="11" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
@ -386,7 +389,7 @@
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Use white channel</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Use white channel</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="useWhite" action="reload" tabindex="9" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
@ -394,7 +397,7 @@
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Use gamma correction</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Use gamma correction</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="useGamma" tabindex="10" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
@ -402,7 +405,7 @@
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Use CSS style</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Use CSS style</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="useCSS" tabindex="11" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
@ -410,7 +413,7 @@
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Color transitions</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Color transitions</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="useTransitions" tabindex="12" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
@ -465,12 +468,12 @@
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Enable HTTP API</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Enable HTTP API</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="apiEnabled" /></div>
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Real time API</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Real time API</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="apiRealTime" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
@ -493,7 +496,7 @@
</div>
<div class="pure-g module module-telnet">
<div class="pure-u-1 pure-u-lg-1-4"><label>Enable TELNET</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Enable TELNET</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="telnetSTA" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
@ -502,7 +505,7 @@
<div class="pure-g module module-nofuss">
<div class="pure-u-1 pure-u-lg-1-4"><label>Automatic remote updates (NoFUSS)</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Automatic remote updates (NoFUSS)</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="nofussEnabled" /></div>
</div>
@ -516,7 +519,7 @@
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Settings</label>
<div class="pure-u-1-2 pure-u-lg-1-8"><button class="pure-button button-settings-backup pure-u-23-24">Backup</button></div>
<div class="pure-u-1-2 pure-u-lg-1-8"><button class="pure-button button-settings-restore pure-u-23-24">Restore</button></div>
<div class="pure-u-1-2 pure-u-lg-1-8"><button class="pure-button button-settings-restore pure-u-1">Restore</button></div>
</div>
<div class="pure-g">
@ -547,7 +550,7 @@
<legend>General</legend>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Scan networks</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Scan networks</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="wifiScan" tabindex="1" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
@ -610,7 +613,7 @@
<fieldset>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Enable MQTT</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Enable MQTT</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="mqttEnabled" tabindex="30" /></div>
</div>
@ -697,7 +700,7 @@
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Use JSON payload</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Use JSON payload</label>
<div class="pure-u-1 pure-u-lg-3-4"><input type="checkbox" name="mqttUseJson" tabindex="32" /></div>
<div class="pure-u-1 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">
@ -725,30 +728,22 @@
<fieldset>
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">NTP Server #0</label>
<input class="pure-u-1 pure-u-lg-3-4" name="ntpServer1" type="text" tabindex="41" />
<label class="pure-u-1 pure-u-lg-1-4">Device Current Time</label>
<input class="pure-u-1 pure-u-lg-3-4" name="now" type="text" readonly />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">NTP Server #1</label>
<input class="pure-u-1 pure-u-lg-3-4" name="ntpServer2" type="text" tabindex="42" />
<label class="pure-u-1 pure-u-lg-1-4">NTP Server</label>
<input class="pure-u-1 pure-u-lg-3-4" name="ntpServer" type="text" tabindex="41" />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">NTP Server #2</label>
<input class="pure-u-1 pure-u-lg-3-4" name="ntpServer3" type="text" tabindex="43" />
<label class="pure-u-1 pure-u-lg-1-4">Time Zone</label>
<select class="pure-u-1 pure-u-lg-1-4" name="ntpOffset" tabindex="42"></select>
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Time offset</label>
<div class="pure-u-1 pure-u-lg-1-4"><input class="pure-u-1 pure-u-lg-23-24" name="ntpOffset" type="number" min="-11" max="14" tabindex="44" data="0" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-1-2 hint">Set to 0 for UTC time</div>
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Enable DST</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Enable DST</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="ntpDST" /></div>
</div>
@ -773,7 +768,7 @@
<legend>General</legend>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Enable Domoticz</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Enable Domoticz</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="dczEnabled" tabindex="30" /></div>
</div>
@ -818,7 +813,7 @@
<legend>General</legend>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Enable Thingspeak</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Enable Thingspeak</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="tspkEnabled" tabindex="30" /></div>
</div>
@ -856,7 +851,7 @@
<fieldset>
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Enable InfluxDB</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Enable InfluxDB</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="idbEnabled" tabindex="40" /></div>
</div>
@ -981,7 +976,7 @@
</div>
<div class="pure-g module module-hlw module-emon">
<div class="pure-u-1 pure-u-lg-1-4"><label>Reset calibration</label></div>
<label class="pure-u-1 pure-u-lg-1-4">Reset calibration</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="pwrResetCalibration" tabindex="55" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
@ -1122,7 +1117,7 @@
<div id="relayTemplate" class="template">
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Switch #<span class="id"></span></label></div>
<label class="pure-u-1 pure-u-lg-1-4">Switch #<span class="id"></span></label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" class="relayStatus pure-u-1 pure-u-lg-1-4" data="0" /></div>
</div>
</div>


+ 0
- 0
code/ota.py View File


+ 1
- 1
code/platformio.ini View File

@ -18,7 +18,7 @@ lib_deps =
https://github.com/marvinroger/async-mqtt-client#v0.8.1
PubSubClient
Embedis
NtpClientLib
https://github.com/xoseperez/NtpClient.git#b35e249
OneWire
Brzo I2C
https://github.com/krosk93/espsoftwareserial#a770677


Loading…
Cancel
Save