mirror of
https://github.com/xoseperez/espurna.git
synced 2026-03-10 02:07:03 +01:00
Convert .ino -> .cpp (#2228)
- general conversion from .ino modules into a separate .cpp files - clean-up internal headers, place libraries into .h. guard .cpp with _SUPPORT flags - fix some instances of shared variables instead of public methods - tweak build system to still build a single source file via os environment variable ESPURNA_BUILD_SINGLE_SOURCE
This commit is contained in:
@@ -6,18 +6,27 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "alexa.h"
|
||||
|
||||
#if ALEXA_SUPPORT
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "alexa.h"
|
||||
#include "broker.h"
|
||||
#include "light.h"
|
||||
#include "relay.h"
|
||||
#include "ws.h"
|
||||
#include "web.h"
|
||||
#include "ws.h"
|
||||
|
||||
struct alexa_queue_element_t {
|
||||
unsigned char device_id;
|
||||
bool state;
|
||||
unsigned char value;
|
||||
};
|
||||
|
||||
static std::queue<alexa_queue_element_t> _alexa_queue;
|
||||
|
||||
fauxmoESP _alexa;
|
||||
static std::queue<alexa_queue_element_t> _alexa_queue;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ALEXA
|
||||
@@ -76,6 +85,32 @@ bool alexaEnabled() {
|
||||
return getSetting<bool>("alexaEnabled", 1 == ALEXA_ENABLED);
|
||||
}
|
||||
|
||||
void alexaLoop() {
|
||||
|
||||
_alexa.handle();
|
||||
|
||||
while (!_alexa_queue.empty()) {
|
||||
|
||||
alexa_queue_element_t element = _alexa_queue.front();
|
||||
DEBUG_MSG_P(PSTR("[ALEXA] Device #%u state: %s value: %d\n"), element.device_id, element.state ? "ON" : "OFF", element.value);
|
||||
|
||||
#if RELAY_PROVIDER == RELAY_PROVIDER_LIGHT
|
||||
if (0 == element.device_id) {
|
||||
relayStatus(0, element.state);
|
||||
} else {
|
||||
lightState(element.device_id - 1, element.state);
|
||||
lightChannel(element.device_id - 1, element.value);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
#else
|
||||
relayStatus(element.device_id, element.state);
|
||||
#endif
|
||||
|
||||
_alexa_queue.pop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void alexaSetup() {
|
||||
|
||||
// Backwards compatibility
|
||||
@@ -154,30 +189,4 @@ void alexaSetup() {
|
||||
|
||||
}
|
||||
|
||||
void alexaLoop() {
|
||||
|
||||
_alexa.handle();
|
||||
|
||||
while (!_alexa_queue.empty()) {
|
||||
|
||||
alexa_queue_element_t element = _alexa_queue.front();
|
||||
DEBUG_MSG_P(PSTR("[ALEXA] Device #%u state: %s value: %d\n"), element.device_id, element.state ? "ON" : "OFF", element.value);
|
||||
|
||||
#if RELAY_PROVIDER == RELAY_PROVIDER_LIGHT
|
||||
if (0 == element.device_id) {
|
||||
relayStatus(0, element.state);
|
||||
} else {
|
||||
lightState(element.device_id - 1, element.state);
|
||||
lightChannel(element.device_id - 1, element.value);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
#else
|
||||
relayStatus(element.device_id, element.state);
|
||||
#endif
|
||||
|
||||
_alexa_queue.pop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -8,13 +8,7 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "web.h"
|
||||
|
||||
struct alexa_queue_element_t {
|
||||
unsigned char device_id;
|
||||
bool state;
|
||||
unsigned char value;
|
||||
};
|
||||
#include "espurna.h"
|
||||
|
||||
#if ALEXA_SUPPORT
|
||||
|
||||
|
||||
@@ -6,19 +6,22 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "api.h"
|
||||
|
||||
#if API_SUPPORT
|
||||
|
||||
#include "api.h"
|
||||
#include <vector>
|
||||
|
||||
#include "system.h"
|
||||
#include "web.h"
|
||||
#include "rpc.h"
|
||||
#include "ws.h"
|
||||
|
||||
typedef struct {
|
||||
struct web_api_t {
|
||||
char * key;
|
||||
api_get_callback_f getFn = NULL;
|
||||
api_put_callback_f putFn = NULL;
|
||||
} web_api_t;
|
||||
};
|
||||
std::vector<web_api_t> _apis;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -8,24 +8,21 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
#include "web.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
// TODO: need these prototypes for .ino
|
||||
#if WEB_SUPPORT && API_SUPPORT
|
||||
|
||||
#include <ESPAsyncTCP.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
using api_get_callback_f = std::function<void(char * buffer, size_t size)>;
|
||||
using api_put_callback_f = std::function<void(const char * payload)> ;
|
||||
|
||||
#if API_SUPPORT
|
||||
void apiRegister(const char * key, api_get_callback_f getFn, api_put_callback_f putFn = nullptr);
|
||||
|
||||
#include <ESPAsyncTCP.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#if WEB_SUPPORT
|
||||
void apiRegister(const char * key, api_get_callback_f getFn, api_put_callback_f putFn = nullptr);
|
||||
#endif
|
||||
void apiSetup();
|
||||
|
||||
#endif // API_SUPPORT == 1
|
||||
|
||||
@@ -5,6 +5,8 @@ BOARD MODULE
|
||||
*/
|
||||
|
||||
#include "board.h"
|
||||
#include "relay.h"
|
||||
#include "sensor.h"
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
@@ -6,6 +6,8 @@ BOARD MODULE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
const String& getChipId();
|
||||
const String& getIdentifier();
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#if BROKER_SUPPORT
|
||||
#include "espurna.h"
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
@@ -46,14 +46,9 @@ struct TBroker {
|
||||
template <TBrokerType type, typename... TArgs>
|
||||
TBrokerCallbacks<TArgs...> TBroker<type, TArgs...>::callbacks;
|
||||
|
||||
|
||||
// --- Some known types. Bind them here to avoid .ino screwing with order ---
|
||||
|
||||
using StatusBroker = TBroker<TBrokerType::Status, const String&, unsigned char, unsigned int>;
|
||||
|
||||
using SensorReadBroker = TBroker<TBrokerType::SensorRead, const String&, unsigned char, double, const char*>;
|
||||
using SensorReportBroker = TBroker<TBrokerType::SensorReport, const String&, unsigned char, double, const char*>;
|
||||
|
||||
using ConfigBroker = TBroker<TBrokerType::Config, const String&, const String&>;
|
||||
|
||||
#endif // BROKER_SUPPORT == 1
|
||||
|
||||
@@ -6,6 +6,8 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "button.h"
|
||||
|
||||
#if BUTTON_SUPPORT
|
||||
|
||||
#include <bitset>
|
||||
@@ -15,10 +17,11 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
#include "compat.h"
|
||||
#include "gpio.h"
|
||||
#include "system.h"
|
||||
#include "mqtt.h"
|
||||
#include "relay.h"
|
||||
#include "light.h"
|
||||
#include "ws.h"
|
||||
|
||||
#include "button.h"
|
||||
#include "button_config.h"
|
||||
|
||||
#include "libs/DebounceEvent.h"
|
||||
@@ -125,6 +128,10 @@ debounce_event::types::Config _buttonConfig(unsigned char index) {
|
||||
};
|
||||
}
|
||||
|
||||
int _buttonEventNumber(button_event_t event) {
|
||||
return static_cast<int>(event);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
button_event_delays_t::button_event_delays_t() :
|
||||
@@ -335,10 +342,6 @@ button_action_t buttonAction(unsigned char id, const button_event_t event) {
|
||||
return _buttonDecodeEventAction(_buttons[id].actions, event);
|
||||
}
|
||||
|
||||
int _buttonEventNumber(button_event_t event) {
|
||||
return static_cast<int>(event);
|
||||
}
|
||||
|
||||
// Approach based on https://github.com/esp8266/Arduino/pull/6950
|
||||
// "PROGMEM footprint cleanup for responseCodeToString (#6950)"
|
||||
// In this particular case, saves 76 bytes (120 vs 44)
|
||||
@@ -493,6 +496,90 @@ unsigned long _buttonGetSetting(const char* key, unsigned char index, T default_
|
||||
return getSetting({key, index}, getSetting(key, default_value));
|
||||
}
|
||||
|
||||
// Sonoff Dual does not do real GPIO readings and we
|
||||
// depend on the external MCU to send us relay / button events
|
||||
// Lightfox uses the same protocol as Dual, but has slightly different actions
|
||||
// TODO: move this to a separate 'hardware' setup file?
|
||||
|
||||
void _buttonLoopSonoffDual() {
|
||||
|
||||
if (Serial.available() < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char bytes[4] = {0};
|
||||
Serial.readBytes(bytes, 4);
|
||||
if ((bytes[0] != 0xA0) && (bytes[1] != 0x04) && (bytes[3] != 0xA1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned char value [[gnu::unused]] = bytes[2];
|
||||
|
||||
#if BUTTON_EVENTS_SOURCE == BUTTON_EVENTS_SOURCE_ITEAD_SONOFF_DUAL
|
||||
|
||||
// RELAYs and BUTTONs are synchonized in the SIL F330
|
||||
// The on-board BUTTON2 should toggle RELAY0 value
|
||||
// Since we are not passing back RELAY2 value
|
||||
// (in the relayStatus method) it will only be present
|
||||
// here if it has actually been pressed
|
||||
if ((value & 4) == 4) {
|
||||
buttonEvent(2, button_event_t::Click);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise check if any of the other two BUTTONs
|
||||
// (in the header) has been pressed, but we should
|
||||
// ensure that we only toggle one of them to avoid
|
||||
// the synchronization going mad
|
||||
// This loop is generic for any PSB-04 module
|
||||
for (unsigned int i=0; i<relayCount(); i++) {
|
||||
|
||||
const bool status = (value & (1 << i)) > 0;
|
||||
|
||||
// Check if the status for that relay has changed
|
||||
if (relayStatus(i) != status) {
|
||||
buttonEvent(i, button_event_t::Click);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#elif BUTTON_EVENTS_SOURCE == BUTTON_EVENTS_SOURCE_FOXEL_LIGHTFOX_DUAL
|
||||
|
||||
DEBUG_MSG_P(PSTR("[BUTTON] [LIGHTFOX] Received buttons mask: %u\n"), value);
|
||||
|
||||
for (unsigned int i=0; i<_buttons.size(); i++) {
|
||||
if ((value & (1 << i)) > 0) {
|
||||
buttonEvent(i, button_event_t::Click);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BUTTON_EVENTS_SOURCE == BUTTON_EVENTS_SOURCE_ITEAD_SONOFF_DUAL
|
||||
|
||||
}
|
||||
|
||||
void _buttonLoopGeneric() {
|
||||
for (size_t id = 0; id < _buttons.size(); ++id) {
|
||||
auto event = _buttons[id].loop();
|
||||
if (event != button_event_t::None) {
|
||||
buttonEvent(id, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void buttonLoop() {
|
||||
|
||||
#if BUTTON_EVENTS_SOURCE == BUTTON_EVENTS_SOURCE_GENERIC
|
||||
_buttonLoopGeneric();
|
||||
#elif (BUTTON_EVENTS_SOURCE == BUTTON_EVENTS_SOURCE_ITEAD_SONOFF_DUAL) || \
|
||||
(BUTTON_EVENTS_SOURCE == BUTTON_EVENTS_SOURCE_FOXEL_LIGHTFOX_DUAL)
|
||||
_buttonLoopSonoffDual();
|
||||
#else
|
||||
#warning "Unknown value for BUTTON_EVENTS_SOURCE"
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void buttonSetup() {
|
||||
|
||||
// Backwards compatibility
|
||||
@@ -624,88 +711,4 @@ void buttonSetup() {
|
||||
|
||||
}
|
||||
|
||||
// Sonoff Dual does not do real GPIO readings and we
|
||||
// depend on the external MCU to send us relay / button events
|
||||
// Lightfox uses the same protocol as Dual, but has slightly different actions
|
||||
// TODO: move this to a separate 'hardware' setup file?
|
||||
|
||||
void _buttonLoopSonoffDual() {
|
||||
|
||||
if (Serial.available() < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char bytes[4] = {0};
|
||||
Serial.readBytes(bytes, 4);
|
||||
if ((bytes[0] != 0xA0) && (bytes[1] != 0x04) && (bytes[3] != 0xA1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned char value [[gnu::unused]] = bytes[2];
|
||||
|
||||
#if BUTTON_EVENTS_SOURCE == BUTTON_EVENTS_SOURCE_ITEAD_SONOFF_DUAL
|
||||
|
||||
// RELAYs and BUTTONs are synchonized in the SIL F330
|
||||
// The on-board BUTTON2 should toggle RELAY0 value
|
||||
// Since we are not passing back RELAY2 value
|
||||
// (in the relayStatus method) it will only be present
|
||||
// here if it has actually been pressed
|
||||
if ((value & 4) == 4) {
|
||||
buttonEvent(2, button_event_t::Click);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise check if any of the other two BUTTONs
|
||||
// (in the header) has been pressed, but we should
|
||||
// ensure that we only toggle one of them to avoid
|
||||
// the synchronization going mad
|
||||
// This loop is generic for any PSB-04 module
|
||||
for (unsigned int i=0; i<relayCount(); i++) {
|
||||
|
||||
const bool status = (value & (1 << i)) > 0;
|
||||
|
||||
// Check if the status for that relay has changed
|
||||
if (relayStatus(i) != status) {
|
||||
buttonEvent(i, button_event_t::Click);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#elif BUTTON_EVENTS_SOURCE == BUTTON_EVENTS_SOURCE_FOXEL_LIGHTFOX_DUAL
|
||||
|
||||
DEBUG_MSG_P(PSTR("[BUTTON] [LIGHTFOX] Received buttons mask: %u\n"), value);
|
||||
|
||||
for (unsigned int i=0; i<_buttons.size(); i++) {
|
||||
if ((value & (1 << i)) > 0) {
|
||||
buttonEvent(i, button_event_t::Click);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BUTTON_EVENTS_SOURCE == BUTTON_EVENTS_SOURCE_ITEAD_SONOFF_DUAL
|
||||
|
||||
}
|
||||
|
||||
void _buttonLoopGeneric() {
|
||||
for (size_t id = 0; id < _buttons.size(); ++id) {
|
||||
auto event = _buttons[id].loop();
|
||||
if (event != button_event_t::None) {
|
||||
buttonEvent(id, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void buttonLoop() {
|
||||
|
||||
#if BUTTON_EVENTS_SOURCE == BUTTON_EVENTS_SOURCE_GENERIC
|
||||
_buttonLoopGeneric();
|
||||
#elif (BUTTON_EVENTS_SOURCE == BUTTON_EVENTS_SOURCE_ITEAD_SONOFF_DUAL) || \
|
||||
(BUTTON_EVENTS_SOURCE == BUTTON_EVENTS_SOURCE_FOXEL_LIGHTFOX_DUAL)
|
||||
_buttonLoopSonoffDual();
|
||||
#else
|
||||
#warning "Unknown value for BUTTON_EVENTS_SOURCE"
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif // BUTTON_SUPPORT
|
||||
@@ -8,6 +8,8 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#include "libs/BasePin.h"
|
||||
#include "libs/DebounceEvent.h"
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ BUTTON MODULE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
namespace ButtonMask {
|
||||
|
||||
enum {
|
||||
|
||||
@@ -6,6 +6,8 @@ COMPATIBILITY BETWEEN 2.3.0 and latest versions
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Core version 2.4.2 and higher changed the cont_t structure to a pointer:
|
||||
// https://github.com/esp8266/Arduino/commit/5d5ea92a4d004ab009d5f642629946a0cb8893dd#diff-3fa12668b289ccb95b7ab334833a4ba8L35
|
||||
@@ -71,16 +73,42 @@ extern "C" {
|
||||
long __attribute__((deprecated("Please avoid using map() with Core 2.3.0"))) map(long x, long in_min, long in_max, long out_min, long out_max);
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Proxy min & max same as the latest Arduino.h
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#if defined(ARDUINO_ESP8266_RELEASE_2_3_0)
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
#undef _min
|
||||
#undef _max
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using std::min;
|
||||
using std::max;
|
||||
using std::isinf;
|
||||
using std::isnan;
|
||||
|
||||
#define _min(a,b) ({ decltype(a) _a = (a); decltype(b) _b = (b); _a < _b? _a : _b; })
|
||||
#define _max(a,b) ({ decltype(a) _a = (a); decltype(b) _b = (b); _a > _b? _a : _b; })
|
||||
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// std::make_unique backport for C++11, since we still use it
|
||||
// -----------------------------------------------------------------------------
|
||||
#if 201103L >= __cplusplus
|
||||
|
||||
#include <memory>
|
||||
namespace std {
|
||||
template<typename T, typename... Args>
|
||||
std::unique_ptr<T> make_unique(Args&&... args) {
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
// https://github.com/krzychb/EspSaveCrash
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "crash.h"
|
||||
|
||||
#if DEBUG_SUPPORT
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -11,7 +13,6 @@
|
||||
|
||||
#include "system.h"
|
||||
#include "storage_eeprom.h"
|
||||
#include "crash.h"
|
||||
|
||||
uint16_t _save_crash_stack_trace_max = SAVE_CRASH_STACK_TRACE_MAX;
|
||||
bool _save_crash_enabled = true;
|
||||
@@ -6,6 +6,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <cstdint>
|
||||
|
||||
#define SAVE_CRASH_EEPROM_OFFSET 0x0100 // initial address for crash data
|
||||
|
||||
/**
|
||||
@@ -42,7 +47,6 @@ constexpr size_t crashUsedSpace() {
|
||||
return (SAVE_CRASH_EEPROM_OFFSET + SAVE_CRASH_STACK_SIZE + 2);
|
||||
}
|
||||
|
||||
|
||||
void crashClear();
|
||||
void crashDump();
|
||||
void crashSetup();
|
||||
|
||||
@@ -6,14 +6,17 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#if DEBUG_SUPPORT
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "debug.h"
|
||||
#include "settings.h"
|
||||
#include "telnet.h"
|
||||
#include "web.h"
|
||||
#include "ws.h"
|
||||
|
||||
#if DEBUG_UDP_SUPPORT
|
||||
@@ -335,10 +338,10 @@ void debugConfigure() {
|
||||
{
|
||||
#if defined(DEBUG_ESP_PORT)
|
||||
#if not defined(NDEBUG)
|
||||
constexpr const bool debug_sdk = true;
|
||||
constexpr bool debug_sdk = true;
|
||||
#endif // !defined(NDEBUG)
|
||||
#else
|
||||
constexpr const bool debug_sdk = false;
|
||||
constexpr bool debug_sdk = false;
|
||||
#endif // defined(DEBUG_ESP_PORT)
|
||||
|
||||
DEBUG_PORT.setDebugOutput(getSetting("dbgSDK", debug_sdk));
|
||||
@@ -6,7 +6,11 @@ DEBUG MODULE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pgmspace.h>
|
||||
#include "espurna.h"
|
||||
|
||||
#if DEBUG_WEB_SUPPORT
|
||||
#include <ArduinoJson.h>
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
void custom_crash_callback(struct rst_info*, uint32_t, uint32_t);
|
||||
@@ -25,7 +29,7 @@ bool debugLogBuffer();
|
||||
|
||||
void debugWebSetup();
|
||||
void debugConfigure();
|
||||
void debugConfigueBoot();
|
||||
void debugConfigureBoot();
|
||||
void debugSetup();
|
||||
|
||||
void debugSend(const char* format, ...);
|
||||
|
||||
@@ -6,13 +6,16 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "domoticz.h"
|
||||
|
||||
#if DOMOTICZ_SUPPORT
|
||||
|
||||
#include "broker.h"
|
||||
#include "domoticz.h"
|
||||
#include "sensor.h"
|
||||
#include "light.h"
|
||||
#include "mqtt.h"
|
||||
#include "relay.h"
|
||||
#include "sensor.h"
|
||||
#include "ws.h"
|
||||
|
||||
bool _dcz_enabled = false;
|
||||
std::bitset<RELAYS_MAX> _dcz_relay_state;
|
||||
@@ -171,6 +174,23 @@ void _domoticzMqtt(unsigned int type, const char * topic, char * payload) {
|
||||
|
||||
};
|
||||
|
||||
void _domoticzRelayConfigure(size_t size) {
|
||||
for (size_t n = 0; n < size; ++n) {
|
||||
_dcz_relay_state[n] = relayStatus(n);
|
||||
}
|
||||
}
|
||||
|
||||
void _domoticzConfigure() {
|
||||
const bool enabled = getSetting("dczEnabled", 1 == DOMOTICZ_ENABLED);
|
||||
if (enabled != _dcz_enabled) _domoticzMqttSubscribe(enabled);
|
||||
|
||||
#if RELAY_SUPPORT
|
||||
_domoticzRelayConfigure(relayCount());
|
||||
#endif
|
||||
|
||||
_dcz_enabled = enabled;
|
||||
}
|
||||
|
||||
#if BROKER_SUPPORT
|
||||
|
||||
void _domoticzConfigCallback(const String& key, const String& value) {
|
||||
@@ -259,30 +279,13 @@ void _domoticzWebSocketOnConnected(JsonObject& root) {
|
||||
}
|
||||
|
||||
#if SENSOR_SUPPORT
|
||||
_sensorWebSocketMagnitudes(root, "dcz");
|
||||
sensorWebSocketMagnitudes(root, "dcz");
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif // WEB_SUPPORT
|
||||
|
||||
void _domoticzRelayConfigure(size_t size) {
|
||||
for (size_t n = 0; n < size; ++n) {
|
||||
_dcz_relay_state[n] = relayStatus(n);
|
||||
}
|
||||
}
|
||||
|
||||
void _domoticzConfigure() {
|
||||
const bool enabled = getSetting("dczEnabled", 1 == DOMOTICZ_ENABLED);
|
||||
if (enabled != _dcz_enabled) _domoticzMqttSubscribe(enabled);
|
||||
|
||||
#if RELAY_SUPPORT
|
||||
_domoticzRelayConfigure(relayCount());
|
||||
#endif
|
||||
|
||||
_dcz_enabled = enabled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public API
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -8,10 +8,11 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#if DOMOTICZ_SUPPORT
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <bitset>
|
||||
|
||||
template<typename T>
|
||||
|
||||
49
code/espurna/espurna.h
Normal file
49
code/espurna/espurna.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
|
||||
ESPurna
|
||||
|
||||
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "config/all.h"
|
||||
|
||||
#include "board.h"
|
||||
#include "debug.h"
|
||||
#include "compat.h"
|
||||
#include "wifi.h"
|
||||
#include "storage_eeprom.h"
|
||||
#include "gpio.h"
|
||||
#include "settings.h"
|
||||
#include "system.h"
|
||||
#include "terminal.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
using void_callback_f = void (*)();
|
||||
|
||||
void espurnaRegisterLoop(void_callback_f callback);
|
||||
void espurnaRegisterReload(void_callback_f callback);
|
||||
void espurnaReload();
|
||||
unsigned long espurnaLoopDelay();
|
||||
|
||||
@@ -19,305 +19,48 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include "config/all.h"
|
||||
// !!! NOTICE !!!
|
||||
//
|
||||
// This file is only for compatibility with Arduino IDE / arduino-cli
|
||||
// See main.cpp
|
||||
//
|
||||
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "board.h"
|
||||
#include "compat.h"
|
||||
#include "storage_eeprom.h"
|
||||
#include "gpio.h"
|
||||
#include "settings.h"
|
||||
#include "system.h"
|
||||
#include "terminal.h"
|
||||
#include "utils.h"
|
||||
#include "wifi.h"
|
||||
#include "espurna.h"
|
||||
|
||||
#include "alexa.h"
|
||||
#include "api.h"
|
||||
#include "broker.h"
|
||||
#include "button.h"
|
||||
#include "crash.h"
|
||||
#include "debug.h"
|
||||
#include "domoticz.h"
|
||||
#include "homeassistant.h"
|
||||
#include "i2c.h"
|
||||
#include "influxdb.h"
|
||||
#include "ir.h"
|
||||
#include "led.h"
|
||||
#include "light.h"
|
||||
#include "llmnr.h"
|
||||
#include "mdns.h"
|
||||
#include "mqtt.h"
|
||||
#include "netbios.h"
|
||||
#include "nofuss.h"
|
||||
#include "ntp.h"
|
||||
#include "ota.h"
|
||||
#include "relay.h"
|
||||
#include "rfbridge.h"
|
||||
#include "rfm69.h"
|
||||
#include "rpc.h"
|
||||
#include "rpnrules.h"
|
||||
#include "rtcmem.h"
|
||||
#include "scheduler.h"
|
||||
#include "sensor.h"
|
||||
#include "ssdp.h"
|
||||
#include "telnet.h"
|
||||
#include "thermostat.h"
|
||||
#include "thingspeak.h"
|
||||
#include "tuya.h"
|
||||
#include "uartmqtt.h"
|
||||
#include "web.h"
|
||||
#include "ws.h"
|
||||
|
||||
#include "libs/URL.h"
|
||||
#include "libs/HeapStats.h"
|
||||
|
||||
using void_callback_f = void (*)();
|
||||
|
||||
std::vector<void_callback_f> _loop_callbacks;
|
||||
std::vector<void_callback_f> _reload_callbacks;
|
||||
|
||||
bool _reload_config = false;
|
||||
unsigned long _loop_delay = 0;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// GENERAL CALLBACKS
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void espurnaRegisterLoop(void_callback_f callback) {
|
||||
_loop_callbacks.push_back(callback);
|
||||
}
|
||||
|
||||
void espurnaRegisterReload(void_callback_f callback) {
|
||||
_reload_callbacks.push_back(callback);
|
||||
}
|
||||
|
||||
void espurnaReload() {
|
||||
_reload_config = true;
|
||||
}
|
||||
|
||||
void _espurnaReload() {
|
||||
for (const auto& callback : _reload_callbacks) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long espurnaLoopDelay() {
|
||||
return _loop_delay;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// BOOTING
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void setup() {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Basic modules, will always run
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// Cache initial free heap value
|
||||
setInitialFreeHeap();
|
||||
|
||||
// Init logging module
|
||||
#if DEBUG_SUPPORT
|
||||
debugSetup();
|
||||
#endif
|
||||
|
||||
// Init GPIO functions
|
||||
gpioSetup();
|
||||
|
||||
// Init RTCMEM
|
||||
rtcmemSetup();
|
||||
|
||||
// Init EEPROM
|
||||
eepromSetup();
|
||||
|
||||
// Init persistance
|
||||
settingsSetup();
|
||||
|
||||
// Configure logger and crash recorder
|
||||
#if DEBUG_SUPPORT
|
||||
debugConfigureBoot();
|
||||
crashSetup();
|
||||
#endif
|
||||
|
||||
// Return bogus free heap value for broken devices
|
||||
// XXX: device is likely to trigger other bugs! tread carefuly
|
||||
wtfHeap(getSetting<int>("wtfHeap", 0));
|
||||
|
||||
// Init Serial, SPIFFS and system check
|
||||
systemSetup();
|
||||
|
||||
// Init terminal features
|
||||
#if TERMINAL_SUPPORT
|
||||
terminalSetup();
|
||||
#endif
|
||||
|
||||
// Hostname & board name initialization
|
||||
if (getSetting("hostname").length() == 0) {
|
||||
setDefaultHostname();
|
||||
}
|
||||
setBoardName();
|
||||
|
||||
// Show welcome message and system configuration
|
||||
info(true);
|
||||
|
||||
wifiSetup();
|
||||
#if OTA_ARDUINOOTA_SUPPORT
|
||||
arduinoOtaSetup();
|
||||
#endif
|
||||
#if TELNET_SUPPORT
|
||||
telnetSetup();
|
||||
#endif
|
||||
#if OTA_CLIENT != OTA_CLIENT_NONE
|
||||
otaClientSetup();
|
||||
#endif
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 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();
|
||||
wsSetup();
|
||||
#if DEBUG_WEB_SUPPORT
|
||||
debugWebSetup();
|
||||
#endif
|
||||
#if OTA_WEB_SUPPORT
|
||||
otaWebSetup();
|
||||
#endif
|
||||
#endif
|
||||
#if API_SUPPORT
|
||||
apiSetup();
|
||||
#endif
|
||||
|
||||
// lightSetup must be called before relaySetup
|
||||
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
|
||||
lightSetup();
|
||||
#endif
|
||||
#if RELAY_SUPPORT
|
||||
relaySetup();
|
||||
#endif
|
||||
#if BUTTON_SUPPORT
|
||||
buttonSetup();
|
||||
#endif
|
||||
#if ENCODER_SUPPORT && (LIGHT_PROVIDER != LIGHT_PROVIDER_NONE)
|
||||
encoderSetup();
|
||||
#endif
|
||||
#if LED_SUPPORT
|
||||
ledSetup();
|
||||
#endif
|
||||
|
||||
#if MQTT_SUPPORT
|
||||
mqttSetup();
|
||||
#endif
|
||||
#if MDNS_SERVER_SUPPORT
|
||||
mdnsServerSetup();
|
||||
#endif
|
||||
#if MDNS_CLIENT_SUPPORT
|
||||
mdnsClientSetup();
|
||||
#endif
|
||||
#if LLMNR_SUPPORT
|
||||
llmnrSetup();
|
||||
#endif
|
||||
#if NETBIOS_SUPPORT
|
||||
netbiosSetup();
|
||||
#endif
|
||||
#if SSDP_SUPPORT
|
||||
ssdpSetup();
|
||||
#endif
|
||||
#if NTP_SUPPORT
|
||||
ntpSetup();
|
||||
#endif
|
||||
#if I2C_SUPPORT
|
||||
i2cSetup();
|
||||
#endif
|
||||
#if RF_SUPPORT
|
||||
rfbSetup();
|
||||
#endif
|
||||
#if ALEXA_SUPPORT
|
||||
alexaSetup();
|
||||
#endif
|
||||
#if NOFUSS_SUPPORT
|
||||
nofussSetup();
|
||||
#endif
|
||||
#if SENSOR_SUPPORT
|
||||
sensorSetup();
|
||||
#endif
|
||||
#if INFLUXDB_SUPPORT
|
||||
idbSetup();
|
||||
#endif
|
||||
#if THINGSPEAK_SUPPORT
|
||||
tspkSetup();
|
||||
#endif
|
||||
#if RFM69_SUPPORT
|
||||
rfm69Setup();
|
||||
#endif
|
||||
#if IR_SUPPORT
|
||||
irSetup();
|
||||
#endif
|
||||
#if DOMOTICZ_SUPPORT
|
||||
domoticzSetup();
|
||||
#endif
|
||||
#if HOMEASSISTANT_SUPPORT
|
||||
haSetup();
|
||||
#endif
|
||||
#if SCHEDULER_SUPPORT
|
||||
schSetup();
|
||||
#endif
|
||||
#if RPN_RULES_SUPPORT
|
||||
rpnSetup();
|
||||
#endif
|
||||
#if UART_MQTT_SUPPORT
|
||||
uartmqttSetup();
|
||||
#endif
|
||||
#ifdef FOXEL_LIGHTFOX_DUAL
|
||||
lightfoxSetup();
|
||||
#endif
|
||||
#if THERMOSTAT_SUPPORT
|
||||
thermostatSetup();
|
||||
#endif
|
||||
#if THERMOSTAT_DISPLAY_SUPPORT
|
||||
displaySetup();
|
||||
#endif
|
||||
#if TUYA_SUPPORT
|
||||
tuyaSetup();
|
||||
#endif
|
||||
|
||||
// 3rd party code hook
|
||||
#if USE_EXTRA
|
||||
extraSetup();
|
||||
#endif
|
||||
|
||||
// Prepare configuration for version 2.0
|
||||
migrate();
|
||||
|
||||
// Set up delay() after loop callbacks are finished
|
||||
// Note: should be after settingsSetup()
|
||||
_loop_delay = constrain(
|
||||
getSetting("loopDelay", LOOP_DELAY_TIME), 0, 300
|
||||
);
|
||||
|
||||
saveSettings();
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
// Reload config before running any callbacks
|
||||
if (_reload_config) {
|
||||
_espurnaReload();
|
||||
_reload_config = false;
|
||||
}
|
||||
|
||||
// Call registered loop callbacks
|
||||
for (unsigned char i = 0; i < _loop_callbacks.size(); i++) {
|
||||
(_loop_callbacks[i])();
|
||||
}
|
||||
|
||||
// Power saving delay
|
||||
if (_loop_delay) delay(_loop_delay);
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "espurna.h"
|
||||
#include "libs/BasePin.h"
|
||||
|
||||
constexpr const size_t GpioPins = 17;
|
||||
|
||||
@@ -6,15 +6,18 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "homeassistant.h"
|
||||
|
||||
#if HOMEASSISTANT_SUPPORT
|
||||
|
||||
#include <Ticker.h>
|
||||
#include <Schedule.h>
|
||||
|
||||
#include "homeassistant.h"
|
||||
#include "light.h"
|
||||
#include "mqtt.h"
|
||||
#include "relay.h"
|
||||
#include "rpc.h"
|
||||
#include "sensor.h"
|
||||
#include "utils.h"
|
||||
#include "ws.h"
|
||||
|
||||
@@ -8,6 +8,8 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#if HOMEASSISTANT_SUPPORT
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
@@ -6,6 +6,8 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "i2c.h"
|
||||
|
||||
#if I2C_SUPPORT
|
||||
|
||||
unsigned int _i2c_locked[16] = {0};
|
||||
@@ -8,6 +8,8 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#if I2C_SUPPORT
|
||||
|
||||
#if I2C_USE_BRZO
|
||||
@@ -38,5 +40,9 @@ bool i2cGetLock(unsigned char address);
|
||||
bool i2cReleaseLock(unsigned char address);
|
||||
|
||||
unsigned char i2cFindAndLock(size_t size, unsigned char * addresses);
|
||||
unsigned char i2cFind(size_t size, unsigned char * addresses, unsigned char &start);
|
||||
unsigned char i2cFind(size_t size, unsigned char * addresses);
|
||||
|
||||
void i2cSetup();
|
||||
|
||||
#endif // I2C_SUPPORT == 1
|
||||
|
||||
@@ -6,12 +6,16 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "influxdb.h"
|
||||
|
||||
#if INFLUXDB_SUPPORT
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "broker.h"
|
||||
#include "ws.h"
|
||||
#include "terminal.h"
|
||||
#include "libs/AsyncClientHelpers.h"
|
||||
|
||||
const char InfluxDb_http_success[] = "HTTP/1.1 204";
|
||||
21
code/espurna/influxdb.h
Normal file
21
code/espurna/influxdb.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
|
||||
INFLUXDB MODULE
|
||||
|
||||
Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#if INFLUXDB_SUPPORT
|
||||
|
||||
#include <ESPAsyncTCP.h>
|
||||
|
||||
bool idbSend(const char * topic, unsigned char id, const char * payload);
|
||||
bool idbSend(const char * topic, const char * payload);
|
||||
bool idbEnabled();
|
||||
void idbSetup();
|
||||
|
||||
#endif // INFLUXDB_SUPPORT
|
||||
|
||||
@@ -46,9 +46,11 @@ Raw messages:
|
||||
--------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "ir.h"
|
||||
|
||||
#if IR_SUPPORT
|
||||
|
||||
#include "ir.h"
|
||||
#include "light.h"
|
||||
#include "mqtt.h"
|
||||
#include "relay.h"
|
||||
|
||||
@@ -10,6 +10,8 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#if IR_SUPPORT
|
||||
|
||||
#include "ir_button.h"
|
||||
|
||||
@@ -10,6 +10,8 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
// Remote Buttons SET 1 (for the original Remote shipped with the controller)
|
||||
#if IR_BUTTON_SET == 1
|
||||
|
||||
|
||||
@@ -6,15 +6,18 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "led.h"
|
||||
|
||||
#if LED_SUPPORT
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "broker.h"
|
||||
#include "mqtt.h"
|
||||
#include "relay.h"
|
||||
#include "rpc.h"
|
||||
#include "ws.h"
|
||||
|
||||
#include "led.h"
|
||||
#include "led_pattern.h"
|
||||
#include "led_config.h"
|
||||
|
||||
@@ -305,76 +308,6 @@ void ledUpdate(bool do_update) {
|
||||
_led_update = do_update;
|
||||
}
|
||||
|
||||
void ledSetup() {
|
||||
|
||||
size_t leds = 0;
|
||||
|
||||
#if LED1_PIN != GPIO_NONE
|
||||
++leds;
|
||||
#endif
|
||||
#if LED2_PIN != GPIO_NONE
|
||||
++leds;
|
||||
#endif
|
||||
#if LED3_PIN != GPIO_NONE
|
||||
++leds;
|
||||
#endif
|
||||
#if LED4_PIN != GPIO_NONE
|
||||
++leds;
|
||||
#endif
|
||||
#if LED5_PIN != GPIO_NONE
|
||||
++leds;
|
||||
#endif
|
||||
#if LED6_PIN != GPIO_NONE
|
||||
++leds;
|
||||
#endif
|
||||
#if LED7_PIN != GPIO_NONE
|
||||
++leds;
|
||||
#endif
|
||||
#if LED8_PIN != GPIO_NONE
|
||||
++leds;
|
||||
#endif
|
||||
|
||||
_leds.reserve(leds);
|
||||
|
||||
for (unsigned char index=0; index < LedsMax; ++index) {
|
||||
const auto pin = getSetting({"ledGPIO", index}, _ledPin(index));
|
||||
if (!gpioValid(pin)) {
|
||||
break;
|
||||
}
|
||||
_leds.emplace_back(
|
||||
pin,
|
||||
getSetting({"ledInv", index}, _ledInverse(index)),
|
||||
getSetting({"ledMode", index}, _ledMode(index)),
|
||||
getSetting({"ledRelay", index}, _ledRelay(index))
|
||||
);
|
||||
}
|
||||
|
||||
_led_update = true;
|
||||
|
||||
#if MQTT_SUPPORT
|
||||
mqttRegister(_ledMQTTCallback);
|
||||
#endif
|
||||
|
||||
#if WEB_SUPPORT
|
||||
wsRegister()
|
||||
.onVisible(_ledWebSocketOnVisible)
|
||||
.onConnected(_ledWebSocketOnConnected)
|
||||
.onKeyCheck(_ledWebSocketOnKeyCheck);
|
||||
#endif
|
||||
|
||||
#if BROKER_SUPPORT
|
||||
StatusBroker::Register(_ledBrokerCallback);
|
||||
#endif
|
||||
|
||||
|
||||
DEBUG_MSG_P(PSTR("[LED] Number of leds: %d\n"), _leds.size());
|
||||
|
||||
// Main callbacks
|
||||
espurnaRegisterLoop(ledLoop);
|
||||
espurnaRegisterReload(_ledConfigure);
|
||||
|
||||
}
|
||||
|
||||
void ledLoop() {
|
||||
|
||||
const auto wifi_state = wifiState();
|
||||
@@ -498,4 +431,75 @@ void ledLoop() {
|
||||
|
||||
}
|
||||
|
||||
void ledSetup() {
|
||||
|
||||
size_t leds = 0;
|
||||
|
||||
#if LED1_PIN != GPIO_NONE
|
||||
++leds;
|
||||
#endif
|
||||
#if LED2_PIN != GPIO_NONE
|
||||
++leds;
|
||||
#endif
|
||||
#if LED3_PIN != GPIO_NONE
|
||||
++leds;
|
||||
#endif
|
||||
#if LED4_PIN != GPIO_NONE
|
||||
++leds;
|
||||
#endif
|
||||
#if LED5_PIN != GPIO_NONE
|
||||
++leds;
|
||||
#endif
|
||||
#if LED6_PIN != GPIO_NONE
|
||||
++leds;
|
||||
#endif
|
||||
#if LED7_PIN != GPIO_NONE
|
||||
++leds;
|
||||
#endif
|
||||
#if LED8_PIN != GPIO_NONE
|
||||
++leds;
|
||||
#endif
|
||||
|
||||
_leds.reserve(leds);
|
||||
|
||||
for (unsigned char index=0; index < LedsMax; ++index) {
|
||||
const auto pin = getSetting({"ledGPIO", index}, _ledPin(index));
|
||||
if (!gpioValid(pin)) {
|
||||
break;
|
||||
}
|
||||
_leds.emplace_back(
|
||||
pin,
|
||||
getSetting({"ledInv", index}, _ledInverse(index)),
|
||||
getSetting({"ledMode", index}, _ledMode(index)),
|
||||
getSetting({"ledRelay", index}, _ledRelay(index))
|
||||
);
|
||||
}
|
||||
|
||||
_led_update = true;
|
||||
|
||||
#if MQTT_SUPPORT
|
||||
mqttRegister(_ledMQTTCallback);
|
||||
#endif
|
||||
|
||||
#if WEB_SUPPORT
|
||||
wsRegister()
|
||||
.onVisible(_ledWebSocketOnVisible)
|
||||
.onConnected(_ledWebSocketOnConnected)
|
||||
.onKeyCheck(_ledWebSocketOnKeyCheck);
|
||||
#endif
|
||||
|
||||
#if BROKER_SUPPORT
|
||||
StatusBroker::Register(_ledBrokerCallback);
|
||||
#endif
|
||||
|
||||
|
||||
DEBUG_MSG_P(PSTR("[LED] Number of leds: %d\n"), _leds.size());
|
||||
|
||||
// Main callbacks
|
||||
espurnaRegisterLoop(ledLoop);
|
||||
espurnaRegisterReload(_ledConfigure);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // LED_SUPPORT
|
||||
@@ -8,10 +8,12 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
constexpr const size_t LedsMax = 8;
|
||||
constexpr size_t LedsMax = 8;
|
||||
|
||||
enum class LedMode {
|
||||
NetworkAutoconfig,
|
||||
|
||||
@@ -6,6 +6,8 @@ LED MODULE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
constexpr const unsigned char _ledPin(unsigned char index) {
|
||||
return (
|
||||
(index == 0) ? LED1_PIN :
|
||||
|
||||
@@ -10,10 +10,10 @@ Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "led.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
// Scans input string with format
|
||||
// '<on1>,<off1>,<repeats1> <on2>,<off2>,<repeats2> ...'
|
||||
// Directly changing `led.pattern.delays` contents
|
||||
|
||||
@@ -8,10 +8,10 @@ Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "led.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
// Scans input string with format
|
||||
// '<on1>,<off1>,<repeats1> <on2>,<off2>,<repeats2> ...'
|
||||
// Directly changing `led.pattern.delays` contents
|
||||
|
||||
@@ -8,6 +8,8 @@ Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// base interface for generic pin handler.
|
||||
class BasePin {
|
||||
public:
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
|
||||
Show extended heap stats when EspClass::getHeapStats() is available
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TypeChecks.h"
|
||||
|
||||
struct heap_stats_t {
|
||||
uint32_t available;
|
||||
uint16_t usable;
|
||||
uint8_t frag_pct;
|
||||
};
|
||||
|
||||
namespace heap_stats {
|
||||
template <typename T>
|
||||
using has_getHeapStats_t = decltype(std::declval<T>().getHeapStats(0,0,0));
|
||||
|
||||
template <typename T>
|
||||
using has_getHeapStats = is_detected<has_getHeapStats_t, T>;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void _getHeapStats(const std::true_type&, T& instance, heap_stats_t& stats) {
|
||||
instance.getHeapStats(&stats.available, &stats.usable, &stats.frag_pct);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void _getHeapStats(const std::false_type&, T& instance, heap_stats_t& stats) {
|
||||
stats.available = instance.getFreeHeap();
|
||||
stats.usable = 0;
|
||||
stats.frag_pct = 0;
|
||||
}
|
||||
|
||||
void getHeapStats(heap_stats_t& stats) {
|
||||
_getHeapStats(heap_stats::has_getHeapStats<decltype(ESP)>{}, ESP, stats);
|
||||
}
|
||||
|
||||
// WTF
|
||||
// Calling ESP.getFreeHeap() is making the system crash on a specific
|
||||
// AiLight bulb, but anywhere else it should work as expected
|
||||
static bool _heap_value_wtf = false;
|
||||
|
||||
heap_stats_t getHeapStats() {
|
||||
heap_stats_t stats;
|
||||
if (_heap_value_wtf) {
|
||||
stats.available = 9999;
|
||||
stats.usable = 9999;
|
||||
stats.frag_pct = 0;
|
||||
return stats;
|
||||
}
|
||||
getHeapStats(stats);
|
||||
return stats;
|
||||
}
|
||||
|
||||
void wtfHeap(bool value) {
|
||||
_heap_value_wtf = value;
|
||||
}
|
||||
|
||||
unsigned int getFreeHeap() {
|
||||
return ESP.getFreeHeap();
|
||||
}
|
||||
|
||||
static unsigned int _initial_heap_value = 0;
|
||||
void setInitialFreeHeap() {
|
||||
_initial_heap_value = getFreeHeap();
|
||||
}
|
||||
|
||||
unsigned int getInitialFreeHeap() {
|
||||
if (0 == _initial_heap_value) {
|
||||
setInitialFreeHeap();
|
||||
}
|
||||
return _initial_heap_value;
|
||||
}
|
||||
|
||||
void infoMemory(const char* name, const heap_stats_t& stats) {
|
||||
infoMemory(name, getInitialFreeHeap(), stats.available);
|
||||
}
|
||||
|
||||
void infoHeapStats(const char* name, const heap_stats_t& stats) {
|
||||
DEBUG_MSG_P(
|
||||
PSTR("[MAIN] %-6s: %5u contiguous bytes available (%u%% fragmentation)\n"),
|
||||
name,
|
||||
stats.usable,
|
||||
stats.frag_pct
|
||||
);
|
||||
}
|
||||
|
||||
void infoHeapStats(bool show_frag_stats = true) {
|
||||
const auto stats = getHeapStats();
|
||||
infoMemory("Heap", stats);
|
||||
if (show_frag_stats && heap_stats::has_getHeapStats<decltype(ESP)>{}) {
|
||||
infoHeapStats("Heap", stats);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// NtpClient overrides to avoid triggering network sync
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#if NTP_LEGACY_SUPPORT
|
||||
|
||||
#include <WiFiUdp.h>
|
||||
#include <NtpClientLib.h>
|
||||
|
||||
class NTPClientWrap : public NTPClient {
|
||||
|
||||
public:
|
||||
|
||||
NTPClientWrap() : NTPClient() {
|
||||
udp = new WiFiUDP();
|
||||
_lastSyncd = 0;
|
||||
}
|
||||
|
||||
bool setInterval(int shortInterval, int longInterval) {
|
||||
_shortInterval = shortInterval;
|
||||
_longInterval = longInterval;
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// NOTE: original NTP should be discarded by the linker
|
||||
// TODO: allow NTP client object to be destroyed
|
||||
NTPClientWrap NTPw;
|
||||
|
||||
#endif
|
||||
@@ -26,40 +26,3 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include <RFM69_ATC.h>
|
||||
#include <SPI.h>
|
||||
|
||||
class RFM69Wrap: public RFM69_ATC {
|
||||
|
||||
public:
|
||||
|
||||
RFM69Wrap(uint8_t slaveSelectPin=RF69_SPI_CS, uint8_t interruptPin=RF69_IRQ_PIN, bool isRFM69HW=false, uint8_t interruptNum=0):
|
||||
RFM69_ATC(slaveSelectPin, interruptPin, isRFM69HW, interruptNum) {};
|
||||
|
||||
protected:
|
||||
|
||||
// overriding SPI_CLOCK for ESP8266
|
||||
void select() {
|
||||
|
||||
noInterrupts();
|
||||
|
||||
#if defined (SPCR) && defined (SPSR)
|
||||
// save current SPI settings
|
||||
_SPCR = SPCR;
|
||||
_SPSR = SPSR;
|
||||
#endif
|
||||
|
||||
// set RFM69 SPI settings
|
||||
SPI.setDataMode(SPI_MODE0);
|
||||
SPI.setBitOrder(MSBFIRST);
|
||||
|
||||
#if defined(__arm__)
|
||||
SPI.setClockDivider(SPI_CLOCK_DIV16);
|
||||
#elif defined(ARDUINO_ARCH_ESP8266)
|
||||
SPI.setClockDivider(SPI_CLOCK_DIV2); // speeding it up for the ESP8266
|
||||
#else
|
||||
SPI.setClockDivider(SPI_CLOCK_DIV4);
|
||||
#endif
|
||||
|
||||
digitalWrite(_slaveSelectPin, LOW);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -4,8 +4,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../espurna.h"
|
||||
|
||||
#if SECURE_CLIENT != SECURE_CLIENT_NONE
|
||||
|
||||
#include "../ntp.h"
|
||||
|
||||
#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
|
||||
#include <WiFiClientSecureBearSSL.h>
|
||||
#elif SECURE_CLIENT == SECURE_CLIENT_AXTLS
|
||||
@@ -20,7 +24,8 @@ using fp_callback_f = std::function<String()>;
|
||||
using cert_callback_f = std::function<const char*()>;
|
||||
using mfln_callback_f = std::function<uint16_t()>;
|
||||
|
||||
const char * _secureClientCheckAsString(int check) {
|
||||
// TODO: workaround for `multiple definition of `SecureClientHelpers::_secureClientCheckAsString(int);'
|
||||
inline const char * _secureClientCheckAsString(int check) {
|
||||
switch (check) {
|
||||
case SECURE_CLIENT_CHECK_NONE: return "no validation";
|
||||
case SECURE_CLIENT_CHECK_FINGERPRINT: return "fingerprint validation";
|
||||
|
||||
@@ -10,70 +10,66 @@
|
||||
|
||||
class URL {
|
||||
public:
|
||||
URL();
|
||||
URL(const String&);
|
||||
|
||||
String protocol;
|
||||
String host;
|
||||
String path;
|
||||
uint16_t port;
|
||||
URL() :
|
||||
protocol(),
|
||||
host(),
|
||||
path(),
|
||||
port(0)
|
||||
{}
|
||||
|
||||
URL(const String& string) {
|
||||
_parse(string);
|
||||
}
|
||||
|
||||
String protocol;
|
||||
String host;
|
||||
String path;
|
||||
uint16_t port;
|
||||
|
||||
private:
|
||||
void _parse(String);
|
||||
|
||||
void _parse(String buffer) {
|
||||
// cut the protocol part
|
||||
int index = buffer.indexOf("://");
|
||||
if (index > 0) {
|
||||
this->protocol = buffer.substring(0, index);
|
||||
buffer.remove(0, (index + 3));
|
||||
}
|
||||
|
||||
if (this->protocol == "http") {
|
||||
this->port = 80;
|
||||
} else if (this->protocol == "https") {
|
||||
this->port = 443;
|
||||
}
|
||||
|
||||
// cut the host part
|
||||
String _host;
|
||||
|
||||
index = buffer.indexOf('/');
|
||||
if (index >= 0) {
|
||||
_host = buffer.substring(0, index);
|
||||
} else {
|
||||
_host = buffer;
|
||||
}
|
||||
|
||||
// store the remaining part as path
|
||||
if (index >= 0) {
|
||||
buffer.remove(0, index);
|
||||
this->path = buffer;
|
||||
} else {
|
||||
this->path = "/";
|
||||
}
|
||||
|
||||
// separate host from port, when present
|
||||
index = _host.indexOf(':');
|
||||
if (index >= 0) {
|
||||
this->port = _host.substring(index + 1).toInt();
|
||||
this->host = _host.substring(0, index);
|
||||
} else {
|
||||
this->host = _host;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
URL::URL() :
|
||||
protocol(),
|
||||
host(),
|
||||
path(),
|
||||
port(0)
|
||||
{}
|
||||
|
||||
URL::URL(const String& string) {
|
||||
_parse(string);
|
||||
}
|
||||
|
||||
void URL::_parse(String buffer) {
|
||||
|
||||
// cut the protocol part
|
||||
int index = buffer.indexOf("://");
|
||||
if (index > 0) {
|
||||
this->protocol = buffer.substring(0, index);
|
||||
buffer.remove(0, (index + 3));
|
||||
}
|
||||
|
||||
if (this->protocol == "http") {
|
||||
this->port = 80;
|
||||
} else if (this->protocol == "https") {
|
||||
this->port = 443;
|
||||
}
|
||||
|
||||
// cut the host part
|
||||
String _host;
|
||||
|
||||
index = buffer.indexOf('/');
|
||||
if (index >= 0) {
|
||||
_host = buffer.substring(0, index);
|
||||
} else {
|
||||
_host = buffer;
|
||||
}
|
||||
|
||||
// store the remaining part as path
|
||||
if (index >= 0) {
|
||||
buffer.remove(0, index);
|
||||
this->path = buffer;
|
||||
} else {
|
||||
this->path = "/";
|
||||
}
|
||||
|
||||
// separate host from port, when present
|
||||
index = _host.indexOf(':');
|
||||
if (index >= 0) {
|
||||
this->port = _host.substring(index + 1).toInt();
|
||||
this->host = _host.substring(0, index);
|
||||
} else {
|
||||
this->host = _host;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,17 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "light.h"
|
||||
|
||||
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
|
||||
|
||||
#include "api.h"
|
||||
#include "broker.h"
|
||||
#include "mqtt.h"
|
||||
#include "rtcmem.h"
|
||||
#include "tuya.h"
|
||||
#include "ws.h"
|
||||
|
||||
#include "light.h"
|
||||
#include "light_config.h"
|
||||
|
||||
#include <Ticker.h>
|
||||
@@ -25,7 +28,6 @@ extern "C" {
|
||||
#include "libs/fs_math.h"
|
||||
}
|
||||
|
||||
#define ARRAYINIT(type, name, ...) type name[] = {__VA_ARGS__};
|
||||
#if LIGHT_PROVIDER == LIGHT_PROVIDER_DIMMER
|
||||
|
||||
// default is 8, we only need up to 5
|
||||
@@ -38,6 +40,21 @@ extern "C" {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
struct channel_t {
|
||||
|
||||
channel_t();
|
||||
channel_t(unsigned char pin, bool inverse);
|
||||
|
||||
unsigned char pin; // real GPIO pin
|
||||
bool inverse; // whether we should invert the value before using it
|
||||
bool state; // is the channel ON
|
||||
unsigned char inputValue; // raw value, without the brightness
|
||||
unsigned char value; // normalized value, including brightness
|
||||
unsigned char target; // target value
|
||||
double current; // transition value
|
||||
|
||||
};
|
||||
|
||||
Ticker _light_comms_ticker;
|
||||
Ticker _light_save_ticker;
|
||||
Ticker _light_transition_ticker;
|
||||
@@ -74,7 +91,9 @@ light_brightness_func_t* _light_brightness_func = nullptr;
|
||||
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY92XX
|
||||
#include <my92xx.h>
|
||||
my92xx * _my92xx;
|
||||
ARRAYINIT(unsigned char, _light_channel_map, MY92XX_MAPPING);
|
||||
unsigned char _light_channel_map[] {
|
||||
MY92XX_MAPPING
|
||||
};
|
||||
#endif
|
||||
|
||||
// UI hint about channel distribution
|
||||
@@ -566,6 +585,8 @@ void _lightTransition(unsigned long step) {
|
||||
|
||||
}
|
||||
|
||||
void _lightProviderScheduleUpdate(unsigned long steps);
|
||||
|
||||
void _lightProviderUpdate(unsigned long steps) {
|
||||
|
||||
if (_light_provider_update) return;
|
||||
@@ -829,6 +850,255 @@ void lightBroker() {
|
||||
// API
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#if API_SUPPORT
|
||||
|
||||
void _lightAPISetup() {
|
||||
|
||||
if (_light_has_color) {
|
||||
|
||||
apiRegister(MQTT_TOPIC_COLOR_RGB,
|
||||
[](char * buffer, size_t len) {
|
||||
if (getSetting("useCSS", 1 == LIGHT_USE_CSS)) {
|
||||
_toRGB(buffer, len, true);
|
||||
} else {
|
||||
_toLong(buffer, len, true);
|
||||
}
|
||||
},
|
||||
[](const char * payload) {
|
||||
lightColor(payload, true);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
|
||||
apiRegister(MQTT_TOPIC_COLOR_HSV,
|
||||
[](char * buffer, size_t len) {
|
||||
_toHSV(buffer, len);
|
||||
},
|
||||
[](const char * payload) {
|
||||
lightColor(payload, false);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
|
||||
apiRegister(MQTT_TOPIC_KELVIN,
|
||||
[](char * buffer, size_t len) {},
|
||||
[](const char * payload) {
|
||||
_lightAdjustKelvin(payload);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
|
||||
apiRegister(MQTT_TOPIC_MIRED,
|
||||
[](char * buffer, size_t len) {},
|
||||
[](const char * payload) {
|
||||
_lightAdjustMireds(payload);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
for (unsigned int id=0; id<_light_channels.size(); id++) {
|
||||
|
||||
char key[15];
|
||||
snprintf_P(key, sizeof(key), PSTR("%s/%d"), MQTT_TOPIC_CHANNEL, id);
|
||||
apiRegister(key,
|
||||
[id](char * buffer, size_t len) {
|
||||
snprintf_P(buffer, len, PSTR("%d"), _light_channels[id].target);
|
||||
},
|
||||
[id](const char * payload) {
|
||||
_lightAdjustChannel(id, payload);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
apiRegister(MQTT_TOPIC_TRANSITION,
|
||||
[](char * buffer, size_t len) {
|
||||
snprintf_P(buffer, len, PSTR("%d"), lightTransitionTime());
|
||||
},
|
||||
[](const char * payload) {
|
||||
lightTransitionTime(atol(payload));
|
||||
}
|
||||
);
|
||||
|
||||
apiRegister(MQTT_TOPIC_BRIGHTNESS,
|
||||
[](char * buffer, size_t len) {
|
||||
snprintf_P(buffer, len, PSTR("%d"), _light_brightness);
|
||||
},
|
||||
[](const char * payload) {
|
||||
_lightAdjustBrightness(payload);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
#endif // API_SUPPORT
|
||||
|
||||
|
||||
#if WEB_SUPPORT
|
||||
|
||||
bool _lightWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
|
||||
if (strncmp(key, "light", 5) == 0) return true;
|
||||
if (strncmp(key, "use", 3) == 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void _lightWebSocketStatus(JsonObject& root) {
|
||||
if (_light_has_color) {
|
||||
if (getSetting("useRGB", 1 == LIGHT_USE_RGB)) {
|
||||
root["rgb"] = lightColor(true);
|
||||
} else {
|
||||
root["hsv"] = lightColor(false);
|
||||
}
|
||||
}
|
||||
if (_light_use_cct) {
|
||||
JsonObject& mireds = root.createNestedObject("mireds");
|
||||
mireds["value"] = _light_mireds;
|
||||
mireds["cold"] = _light_cold_mireds;
|
||||
mireds["warm"] = _light_warm_mireds;
|
||||
root["useCCT"] = _light_use_cct;
|
||||
}
|
||||
JsonArray& channels = root.createNestedArray("channels");
|
||||
for (unsigned char id=0; id < _light_channels.size(); id++) {
|
||||
channels.add(lightChannel(id));
|
||||
}
|
||||
root["brightness"] = lightBrightness();
|
||||
}
|
||||
|
||||
void _lightWebSocketOnVisible(JsonObject& root) {
|
||||
root["colorVisible"] = 1;
|
||||
}
|
||||
|
||||
void _lightWebSocketOnConnected(JsonObject& root) {
|
||||
root["mqttGroupColor"] = getSetting("mqttGroupColor");
|
||||
root["useColor"] = _light_has_color;
|
||||
root["useWhite"] = _light_use_white;
|
||||
root["useGamma"] = _light_use_gamma;
|
||||
root["useTransitions"] = _light_use_transitions;
|
||||
root["useCSS"] = getSetting("useCSS", 1 == LIGHT_USE_CSS);
|
||||
root["useRGB"] = getSetting("useRGB", 1 == LIGHT_USE_RGB);
|
||||
root["lightTime"] = _light_transition_time;
|
||||
|
||||
_lightWebSocketStatus(root);
|
||||
}
|
||||
|
||||
void _lightWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
|
||||
|
||||
if (_light_has_color) {
|
||||
if (strcmp(action, "color") == 0) {
|
||||
if (data.containsKey("rgb")) {
|
||||
lightColor(data["rgb"], true);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
if (data.containsKey("hsv")) {
|
||||
lightColor(data["hsv"], false);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_light_use_cct) {
|
||||
if (strcmp(action, "mireds") == 0) {
|
||||
_fromMireds(data["mireds"]);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (strcmp(action, "channel") == 0) {
|
||||
if (data.containsKey("id") && data.containsKey("value")) {
|
||||
lightChannel(data["id"].as<unsigned char>(), data["value"].as<int>());
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(action, "brightness") == 0) {
|
||||
if (data.containsKey("value")) {
|
||||
lightBrightness(data["value"].as<int>());
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if TERMINAL_SUPPORT
|
||||
|
||||
void _lightChannelDebug(unsigned char id) {
|
||||
DEBUG_MSG_P(PSTR("Channel #%u (%s): %d\n"), id, lightDesc(id).c_str(), lightChannel(id));
|
||||
}
|
||||
|
||||
void _lightInitCommands() {
|
||||
|
||||
terminalRegisterCommand(F("BRIGHTNESS"), [](Embedis* e) {
|
||||
if (e->argc > 1) {
|
||||
_lightAdjustBrightness(e->argv[1]);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
DEBUG_MSG_P(PSTR("Brightness: %u\n"), lightBrightness());
|
||||
terminalOK();
|
||||
});
|
||||
|
||||
terminalRegisterCommand(F("CHANNEL"), [](Embedis* e) {
|
||||
if (!lightChannels()) return;
|
||||
|
||||
auto id = -1;
|
||||
if (e->argc > 1) {
|
||||
id = String(e->argv[1]).toInt();
|
||||
}
|
||||
|
||||
if (id < 0 || id >= static_cast<decltype(id)>(lightChannels())) {
|
||||
for (unsigned char index = 0; index < lightChannels(); ++index) {
|
||||
_lightChannelDebug(index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (e->argc > 2) {
|
||||
_lightAdjustChannel(id, e->argv[2]);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
|
||||
_lightChannelDebug(id);
|
||||
|
||||
terminalOK();
|
||||
});
|
||||
|
||||
terminalRegisterCommand(F("COLOR"), [](Embedis* e) {
|
||||
if (e->argc > 1) {
|
||||
lightColor(e->argv[1]);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
DEBUG_MSG_P(PSTR("Color: %s\n"), lightColor().c_str());
|
||||
terminalOK();
|
||||
});
|
||||
|
||||
terminalRegisterCommand(F("KELVIN"), [](Embedis* e) {
|
||||
if (e->argc > 1) {
|
||||
_lightAdjustKelvin(e->argv[1]);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
DEBUG_MSG_P(PSTR("Color: %s\n"), lightColor().c_str());
|
||||
terminalOK();
|
||||
});
|
||||
|
||||
terminalRegisterCommand(F("MIRED"), [](Embedis* e) {
|
||||
if (e->argc > 1) {
|
||||
_lightAdjustMireds(e->argv[1]);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
DEBUG_MSG_P(PSTR("Color: %s\n"), lightColor().c_str());
|
||||
terminalOK();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
#endif // TERMINAL_SUPPORT
|
||||
|
||||
size_t lightChannels() {
|
||||
return _light_channels.size();
|
||||
}
|
||||
@@ -841,7 +1111,7 @@ bool lightUseCCT() {
|
||||
return _light_use_cct;
|
||||
}
|
||||
|
||||
void _lightComms(const unsigned char mask) {
|
||||
void _lightComms(unsigned char mask) {
|
||||
|
||||
// Report color and brightness to MQTT broker
|
||||
#if MQTT_SUPPORT
|
||||
@@ -995,12 +1265,12 @@ unsigned int lightTransitionTime() {
|
||||
}
|
||||
}
|
||||
|
||||
void lightTransitionTime(unsigned long m) {
|
||||
if (0 == m) {
|
||||
void lightTransitionTime(unsigned long ms) {
|
||||
if (0 == ms) {
|
||||
_light_use_transitions = false;
|
||||
} else {
|
||||
_light_use_transitions = true;
|
||||
_light_transition_time = m;
|
||||
_light_transition_time = ms;
|
||||
}
|
||||
setSetting("useTransitions", _light_use_transitions);
|
||||
setSetting("lightTime", _light_transition_time);
|
||||
@@ -1011,254 +1281,6 @@ void lightTransitionTime(unsigned long m) {
|
||||
// SETUP
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#if WEB_SUPPORT
|
||||
|
||||
bool _lightWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
|
||||
if (strncmp(key, "light", 5) == 0) return true;
|
||||
if (strncmp(key, "use", 3) == 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void _lightWebSocketStatus(JsonObject& root) {
|
||||
if (_light_has_color) {
|
||||
if (getSetting("useRGB", 1 == LIGHT_USE_RGB)) {
|
||||
root["rgb"] = lightColor(true);
|
||||
} else {
|
||||
root["hsv"] = lightColor(false);
|
||||
}
|
||||
}
|
||||
if (_light_use_cct) {
|
||||
JsonObject& mireds = root.createNestedObject("mireds");
|
||||
mireds["value"] = _light_mireds;
|
||||
mireds["cold"] = _light_cold_mireds;
|
||||
mireds["warm"] = _light_warm_mireds;
|
||||
root["useCCT"] = _light_use_cct;
|
||||
}
|
||||
JsonArray& channels = root.createNestedArray("channels");
|
||||
for (unsigned char id=0; id < _light_channels.size(); id++) {
|
||||
channels.add(lightChannel(id));
|
||||
}
|
||||
root["brightness"] = lightBrightness();
|
||||
}
|
||||
|
||||
void _lightWebSocketOnVisible(JsonObject& root) {
|
||||
root["colorVisible"] = 1;
|
||||
}
|
||||
|
||||
void _lightWebSocketOnConnected(JsonObject& root) {
|
||||
root["mqttGroupColor"] = getSetting("mqttGroupColor");
|
||||
root["useColor"] = _light_has_color;
|
||||
root["useWhite"] = _light_use_white;
|
||||
root["useGamma"] = _light_use_gamma;
|
||||
root["useTransitions"] = _light_use_transitions;
|
||||
root["useCSS"] = getSetting("useCSS", 1 == LIGHT_USE_CSS);
|
||||
root["useRGB"] = getSetting("useRGB", 1 == LIGHT_USE_RGB);
|
||||
root["lightTime"] = _light_transition_time;
|
||||
|
||||
_lightWebSocketStatus(root);
|
||||
}
|
||||
|
||||
void _lightWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
|
||||
|
||||
if (_light_has_color) {
|
||||
if (strcmp(action, "color") == 0) {
|
||||
if (data.containsKey("rgb")) {
|
||||
lightColor(data["rgb"], true);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
if (data.containsKey("hsv")) {
|
||||
lightColor(data["hsv"], false);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_light_use_cct) {
|
||||
if (strcmp(action, "mireds") == 0) {
|
||||
_fromMireds(data["mireds"]);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (strcmp(action, "channel") == 0) {
|
||||
if (data.containsKey("id") && data.containsKey("value")) {
|
||||
lightChannel(data["id"].as<unsigned char>(), data["value"].as<int>());
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(action, "brightness") == 0) {
|
||||
if (data.containsKey("value")) {
|
||||
lightBrightness(data["value"].as<int>());
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if API_SUPPORT
|
||||
|
||||
void _lightAPISetup() {
|
||||
|
||||
if (_light_has_color) {
|
||||
|
||||
apiRegister(MQTT_TOPIC_COLOR_RGB,
|
||||
[](char * buffer, size_t len) {
|
||||
if (getSetting("useCSS", 1 == LIGHT_USE_CSS)) {
|
||||
_toRGB(buffer, len, true);
|
||||
} else {
|
||||
_toLong(buffer, len, true);
|
||||
}
|
||||
},
|
||||
[](const char * payload) {
|
||||
lightColor(payload, true);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
|
||||
apiRegister(MQTT_TOPIC_COLOR_HSV,
|
||||
[](char * buffer, size_t len) {
|
||||
_toHSV(buffer, len);
|
||||
},
|
||||
[](const char * payload) {
|
||||
lightColor(payload, false);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
|
||||
apiRegister(MQTT_TOPIC_KELVIN,
|
||||
[](char * buffer, size_t len) {},
|
||||
[](const char * payload) {
|
||||
_lightAdjustKelvin(payload);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
|
||||
apiRegister(MQTT_TOPIC_MIRED,
|
||||
[](char * buffer, size_t len) {},
|
||||
[](const char * payload) {
|
||||
_lightAdjustMireds(payload);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
for (unsigned int id=0; id<_light_channels.size(); id++) {
|
||||
|
||||
char key[15];
|
||||
snprintf_P(key, sizeof(key), PSTR("%s/%d"), MQTT_TOPIC_CHANNEL, id);
|
||||
apiRegister(key,
|
||||
[id](char * buffer, size_t len) {
|
||||
snprintf_P(buffer, len, PSTR("%d"), _light_channels[id].target);
|
||||
},
|
||||
[id](const char * payload) {
|
||||
_lightAdjustChannel(id, payload);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
apiRegister(MQTT_TOPIC_TRANSITION,
|
||||
[](char * buffer, size_t len) {
|
||||
snprintf_P(buffer, len, PSTR("%d"), lightTransitionTime());
|
||||
},
|
||||
[](const char * payload) {
|
||||
lightTransitionTime(atol(payload));
|
||||
}
|
||||
);
|
||||
|
||||
apiRegister(MQTT_TOPIC_BRIGHTNESS,
|
||||
[](char * buffer, size_t len) {
|
||||
snprintf_P(buffer, len, PSTR("%d"), _light_brightness);
|
||||
},
|
||||
[](const char * payload) {
|
||||
_lightAdjustBrightness(payload);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
#endif // API_SUPPORT
|
||||
|
||||
#if TERMINAL_SUPPORT
|
||||
|
||||
void _lightChannelDebug(unsigned char id) {
|
||||
DEBUG_MSG_P(PSTR("Channel #%u (%s): %d\n"), id, lightDesc(id).c_str(), lightChannel(id));
|
||||
}
|
||||
|
||||
void _lightInitCommands() {
|
||||
|
||||
terminalRegisterCommand(F("BRIGHTNESS"), [](Embedis* e) {
|
||||
if (e->argc > 1) {
|
||||
_lightAdjustBrightness(e->argv[1]);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
DEBUG_MSG_P(PSTR("Brightness: %u\n"), lightBrightness());
|
||||
terminalOK();
|
||||
});
|
||||
|
||||
terminalRegisterCommand(F("CHANNEL"), [](Embedis* e) {
|
||||
if (!lightChannels()) return;
|
||||
|
||||
auto id = -1;
|
||||
if (e->argc > 1) {
|
||||
id = String(e->argv[1]).toInt();
|
||||
}
|
||||
|
||||
if (id < 0 || id >= static_cast<decltype(id)>(lightChannels())) {
|
||||
for (unsigned char index = 0; index < lightChannels(); ++index) {
|
||||
_lightChannelDebug(index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (e->argc > 2) {
|
||||
_lightAdjustChannel(id, e->argv[2]);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
|
||||
_lightChannelDebug(id);
|
||||
|
||||
terminalOK();
|
||||
});
|
||||
|
||||
terminalRegisterCommand(F("COLOR"), [](Embedis* e) {
|
||||
if (e->argc > 1) {
|
||||
lightColor(e->argv[1]);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
DEBUG_MSG_P(PSTR("Color: %s\n"), lightColor().c_str());
|
||||
terminalOK();
|
||||
});
|
||||
|
||||
terminalRegisterCommand(F("KELVIN"), [](Embedis* e) {
|
||||
if (e->argc > 1) {
|
||||
_lightAdjustKelvin(e->argv[1]);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
DEBUG_MSG_P(PSTR("Color: %s\n"), lightColor().c_str());
|
||||
terminalOK();
|
||||
});
|
||||
|
||||
terminalRegisterCommand(F("MIRED"), [](Embedis* e) {
|
||||
if (e->argc > 1) {
|
||||
_lightAdjustMireds(e->argv[1]);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
DEBUG_MSG_P(PSTR("Color: %s\n"), lightColor().c_str());
|
||||
terminalOK();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
#endif // TERMINAL_SUPPORT
|
||||
|
||||
#if LIGHT_PROVIDER == LIGHT_PROVIDER_DIMMER
|
||||
const unsigned long _light_iomux[16] PROGMEM = {
|
||||
PERIPHS_IO_MUX_GPIO0_U, PERIPHS_IO_MUX_U0TXD_U, PERIPHS_IO_MUX_GPIO2_U, PERIPHS_IO_MUX_U0RXD_U,
|
||||
@@ -1375,7 +1397,7 @@ void lightSetup() {
|
||||
#endif
|
||||
|
||||
#if LIGHT_PROVIDER == LIGHT_PROVIDER_TUYA
|
||||
tuyaSetupLight();
|
||||
Tuya::tuyaSetupLight();
|
||||
#endif
|
||||
|
||||
DEBUG_MSG_P(PSTR("[LIGHT] LIGHT_PROVIDER = %d\n"), LIGHT_PROVIDER);
|
||||
@@ -4,18 +4,21 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
// TODO: lowercase
|
||||
namespace Light {
|
||||
constexpr const size_t ChannelsMax = 5;
|
||||
constexpr size_t ChannelsMax = 5;
|
||||
|
||||
constexpr const long VALUE_MIN = LIGHT_MIN_VALUE;
|
||||
constexpr const long VALUE_MAX = LIGHT_MAX_VALUE;
|
||||
constexpr long VALUE_MIN = LIGHT_MIN_VALUE;
|
||||
constexpr long VALUE_MAX = LIGHT_MAX_VALUE;
|
||||
|
||||
constexpr const long BRIGHTNESS_MIN = LIGHT_MIN_BRIGHTNESS;
|
||||
constexpr const long BRIGHTNESS_MAX = LIGHT_MAX_BRIGHTNESS;
|
||||
constexpr long BRIGHTNESS_MIN = LIGHT_MIN_BRIGHTNESS;
|
||||
constexpr long BRIGHTNESS_MAX = LIGHT_MAX_BRIGHTNESS;
|
||||
|
||||
constexpr const long PWM_MIN = LIGHT_MIN_PWM;
|
||||
constexpr const long PWM_MAX = LIGHT_MAX_PWM;
|
||||
constexpr const long PWM_LIMIT = LIGHT_LIMIT_PWM;
|
||||
constexpr long PWM_MIN = LIGHT_MIN_PWM;
|
||||
constexpr long PWM_MAX = LIGHT_MAX_PWM;
|
||||
constexpr long PWM_LIMIT = LIGHT_LIMIT_PWM;
|
||||
|
||||
enum Communications : unsigned char {
|
||||
COMMS_NONE = 0,
|
||||
@@ -24,22 +27,15 @@ namespace Light {
|
||||
};
|
||||
}
|
||||
|
||||
struct channel_t {
|
||||
|
||||
channel_t();
|
||||
channel_t(unsigned char pin, bool inverse);
|
||||
|
||||
unsigned char pin; // real GPIO pin
|
||||
bool inverse; // whether we should invert the value before using it
|
||||
bool state; // is the channel ON
|
||||
unsigned char inputValue; // raw value, without the brightness
|
||||
unsigned char value; // normalized value, including brightness
|
||||
unsigned char target; // target value
|
||||
double current; // transition value
|
||||
|
||||
};
|
||||
|
||||
size_t lightChannels();
|
||||
unsigned int lightTransitionTime();
|
||||
void lightTransitionTime(unsigned long ms);
|
||||
|
||||
void lightColor(const char * color, bool rgb);
|
||||
void lightColor(const char * color);
|
||||
void lightColor(unsigned long color);
|
||||
String lightColor(bool rgb);
|
||||
String lightColor();
|
||||
|
||||
void lightState(unsigned char i, bool state);
|
||||
bool lightState(unsigned char i);
|
||||
@@ -55,3 +51,14 @@ void lightChannel(unsigned char id, long value);
|
||||
|
||||
void lightBrightnessStep(long steps, long multiplier = LIGHT_STEP);
|
||||
void lightChannelStep(unsigned char id, long steps, long multiplier = LIGHT_STEP);
|
||||
|
||||
void lightUpdate(bool save, bool forward, bool group_forward);
|
||||
void lightUpdate(bool save, bool forward);
|
||||
|
||||
bool lightHasColor();
|
||||
bool lightUseCCT();
|
||||
|
||||
void lightMQTT();
|
||||
void lightSetupChannels(unsigned char size);
|
||||
|
||||
void lightSetup();
|
||||
|
||||
@@ -6,6 +6,8 @@ LIGHT MODULE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
constexpr const unsigned char _lightEnablePin() {
|
||||
return LIGHT_ENABLE_PIN;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#if LLMNR_SUPPORT
|
||||
#include "llmnr.h"
|
||||
|
||||
#include <ESP8266LLMNR.h>
|
||||
#if LLMNR_SUPPORT
|
||||
|
||||
void llmnrSetup() {
|
||||
LLMNR.begin(getSetting("hostname").c_str());
|
||||
16
code/espurna/llmnr.h
Normal file
16
code/espurna/llmnr.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
|
||||
LLMNR MODULE
|
||||
|
||||
Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#if LLMNR_SUPPORT
|
||||
|
||||
#include <ESP8266LLMNR.h>
|
||||
void llmnrSetup();
|
||||
|
||||
#endif // LLMNR_SUPPORT
|
||||
315
code/espurna/main.cpp
Normal file
315
code/espurna/main.cpp
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
|
||||
ESPurna
|
||||
|
||||
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#include "alexa.h"
|
||||
#include "api.h"
|
||||
#include "broker.h"
|
||||
#include "button.h"
|
||||
#include "crash.h"
|
||||
#include "debug.h"
|
||||
#include "domoticz.h"
|
||||
#include "homeassistant.h"
|
||||
#include "i2c.h"
|
||||
#include "influxdb.h"
|
||||
#include "ir.h"
|
||||
#include "led.h"
|
||||
#include "light.h"
|
||||
#include "llmnr.h"
|
||||
#include "mdns.h"
|
||||
#include "mqtt.h"
|
||||
#include "netbios.h"
|
||||
#include "nofuss.h"
|
||||
#include "ntp.h"
|
||||
#include "ota.h"
|
||||
#include "relay.h"
|
||||
#include "rfbridge.h"
|
||||
#include "rfm69.h"
|
||||
#include "rpc.h"
|
||||
#include "rpnrules.h"
|
||||
#include "rtcmem.h"
|
||||
#include "scheduler.h"
|
||||
#include "sensor.h"
|
||||
#include "ssdp.h"
|
||||
#include "telnet.h"
|
||||
#include "thermostat.h"
|
||||
#include "thingspeak.h"
|
||||
#include "tuya.h"
|
||||
#include "uartmqtt.h"
|
||||
#include "web.h"
|
||||
#include "ws.h"
|
||||
|
||||
std::vector<void_callback_f> _loop_callbacks;
|
||||
std::vector<void_callback_f> _reload_callbacks;
|
||||
|
||||
bool _reload_config = false;
|
||||
unsigned long _loop_delay = 0;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// GENERAL CALLBACKS
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void espurnaRegisterLoop(void_callback_f callback) {
|
||||
_loop_callbacks.push_back(callback);
|
||||
}
|
||||
|
||||
void espurnaRegisterReload(void_callback_f callback) {
|
||||
_reload_callbacks.push_back(callback);
|
||||
}
|
||||
|
||||
void espurnaReload() {
|
||||
_reload_config = true;
|
||||
}
|
||||
|
||||
void _espurnaReload() {
|
||||
for (const auto& callback : _reload_callbacks) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long espurnaLoopDelay() {
|
||||
return _loop_delay;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// BOOTING
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void setup() {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Basic modules, will always run
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// Cache initial free heap value
|
||||
setInitialFreeHeap();
|
||||
|
||||
// Init logging module
|
||||
#if DEBUG_SUPPORT
|
||||
debugSetup();
|
||||
#endif
|
||||
|
||||
// Init GPIO functions
|
||||
gpioSetup();
|
||||
|
||||
// Init RTCMEM
|
||||
rtcmemSetup();
|
||||
|
||||
// Init EEPROM
|
||||
eepromSetup();
|
||||
|
||||
// Init persistance
|
||||
settingsSetup();
|
||||
|
||||
// Configure logger and crash recorder
|
||||
#if DEBUG_SUPPORT
|
||||
debugConfigureBoot();
|
||||
crashSetup();
|
||||
#endif
|
||||
|
||||
// Return bogus free heap value for broken devices
|
||||
// XXX: device is likely to trigger other bugs! tread carefuly
|
||||
wtfHeap(getSetting<int>("wtfHeap", 0));
|
||||
|
||||
// Init Serial, SPIFFS and system check
|
||||
systemSetup();
|
||||
|
||||
// Init terminal features
|
||||
#if TERMINAL_SUPPORT
|
||||
terminalSetup();
|
||||
#endif
|
||||
|
||||
// Hostname & board name initialization
|
||||
if (getSetting("hostname").length() == 0) {
|
||||
setDefaultHostname();
|
||||
}
|
||||
setBoardName();
|
||||
|
||||
// Show welcome message and system configuration
|
||||
info(true);
|
||||
|
||||
wifiSetup();
|
||||
#if OTA_ARDUINOOTA_SUPPORT
|
||||
arduinoOtaSetup();
|
||||
#endif
|
||||
#if TELNET_SUPPORT
|
||||
telnetSetup();
|
||||
#endif
|
||||
#if OTA_CLIENT != OTA_CLIENT_NONE
|
||||
otaClientSetup();
|
||||
#endif
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 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();
|
||||
wsSetup();
|
||||
#if DEBUG_WEB_SUPPORT
|
||||
debugWebSetup();
|
||||
#endif
|
||||
#if OTA_WEB_SUPPORT
|
||||
otaWebSetup();
|
||||
#endif
|
||||
#endif
|
||||
#if API_SUPPORT
|
||||
apiSetup();
|
||||
#endif
|
||||
|
||||
// lightSetup must be called before relaySetup
|
||||
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
|
||||
lightSetup();
|
||||
#endif
|
||||
#if RELAY_SUPPORT
|
||||
relaySetup();
|
||||
#endif
|
||||
#if BUTTON_SUPPORT
|
||||
buttonSetup();
|
||||
#endif
|
||||
#if ENCODER_SUPPORT && (LIGHT_PROVIDER != LIGHT_PROVIDER_NONE)
|
||||
encoderSetup();
|
||||
#endif
|
||||
#if LED_SUPPORT
|
||||
ledSetup();
|
||||
#endif
|
||||
|
||||
#if MQTT_SUPPORT
|
||||
mqttSetup();
|
||||
#endif
|
||||
#if MDNS_SERVER_SUPPORT
|
||||
mdnsServerSetup();
|
||||
#endif
|
||||
#if MDNS_CLIENT_SUPPORT
|
||||
mdnsClientSetup();
|
||||
#endif
|
||||
#if LLMNR_SUPPORT
|
||||
llmnrSetup();
|
||||
#endif
|
||||
#if NETBIOS_SUPPORT
|
||||
netbiosSetup();
|
||||
#endif
|
||||
#if SSDP_SUPPORT
|
||||
ssdpSetup();
|
||||
#endif
|
||||
#if NTP_SUPPORT
|
||||
ntpSetup();
|
||||
#endif
|
||||
#if I2C_SUPPORT
|
||||
i2cSetup();
|
||||
#endif
|
||||
#if RF_SUPPORT
|
||||
rfbSetup();
|
||||
#endif
|
||||
#if ALEXA_SUPPORT
|
||||
alexaSetup();
|
||||
#endif
|
||||
#if NOFUSS_SUPPORT
|
||||
nofussSetup();
|
||||
#endif
|
||||
#if SENSOR_SUPPORT
|
||||
sensorSetup();
|
||||
#endif
|
||||
#if INFLUXDB_SUPPORT
|
||||
idbSetup();
|
||||
#endif
|
||||
#if THINGSPEAK_SUPPORT
|
||||
tspkSetup();
|
||||
#endif
|
||||
#if RFM69_SUPPORT
|
||||
rfm69Setup();
|
||||
#endif
|
||||
#if IR_SUPPORT
|
||||
irSetup();
|
||||
#endif
|
||||
#if DOMOTICZ_SUPPORT
|
||||
domoticzSetup();
|
||||
#endif
|
||||
#if HOMEASSISTANT_SUPPORT
|
||||
haSetup();
|
||||
#endif
|
||||
#if SCHEDULER_SUPPORT
|
||||
schSetup();
|
||||
#endif
|
||||
#if RPN_RULES_SUPPORT
|
||||
rpnSetup();
|
||||
#endif
|
||||
#if UART_MQTT_SUPPORT
|
||||
uartmqttSetup();
|
||||
#endif
|
||||
#ifdef FOXEL_LIGHTFOX_DUAL
|
||||
lightfoxSetup();
|
||||
#endif
|
||||
#if THERMOSTAT_SUPPORT
|
||||
thermostatSetup();
|
||||
#endif
|
||||
#if THERMOSTAT_DISPLAY_SUPPORT
|
||||
displaySetup();
|
||||
#endif
|
||||
#if TUYA_SUPPORT
|
||||
Tuya::tuyaSetup();
|
||||
#endif
|
||||
|
||||
// 3rd party code hook
|
||||
#if USE_EXTRA
|
||||
extraSetup();
|
||||
#endif
|
||||
|
||||
// Prepare configuration for version 2.0
|
||||
migrate();
|
||||
|
||||
// Set up delay() after loop callbacks are finished
|
||||
// Note: should be after settingsSetup()
|
||||
_loop_delay = constrain(
|
||||
getSetting("loopDelay", LOOP_DELAY_TIME), 0, 300
|
||||
);
|
||||
|
||||
saveSettings();
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
// Reload config before running any callbacks
|
||||
if (_reload_config) {
|
||||
_espurnaReload();
|
||||
_reload_config = false;
|
||||
}
|
||||
|
||||
// Call registered loop callbacks
|
||||
for (unsigned char i = 0; i < _loop_callbacks.size(); i++) {
|
||||
(_loop_callbacks[i])();
|
||||
}
|
||||
|
||||
// Power saving delay
|
||||
if (_loop_delay) delay(_loop_delay);
|
||||
|
||||
}
|
||||
@@ -10,6 +10,11 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
// mDNS Server
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "mdns.h"
|
||||
|
||||
#include "mqtt.h"
|
||||
#include "utils.h"
|
||||
|
||||
#if MDNS_SERVER_SUPPORT
|
||||
|
||||
#include <ESP8266mDNS.h>
|
||||
11
code/espurna/mdns.h
Normal file
11
code/espurna/mdns.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
#if MDNS_SERVER_SUPPORT
|
||||
|
||||
#include <ESP8266mDNS.h>
|
||||
void mdnsServerSetup();
|
||||
|
||||
#endif
|
||||
@@ -7,18 +7,20 @@ Updated secure client support by Niek van der Maas < mail at niekvandermaas dot
|
||||
|
||||
*/
|
||||
|
||||
#if MQTT_SUPPORT
|
||||
|
||||
#include "mqtt.h"
|
||||
|
||||
#if MQTT_SUPPORT
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <Ticker.h>
|
||||
|
||||
#include "system.h"
|
||||
#include "mdns.h"
|
||||
#include "mqtt.h"
|
||||
#include "ntp.h"
|
||||
#include "rpc.h"
|
||||
#include "rtcmem.h"
|
||||
#include "ws.h"
|
||||
|
||||
#include "libs/AsyncClientHelpers.h"
|
||||
@@ -215,58 +217,6 @@ bool _mqttConnectSyncClient(bool secure = false) {
|
||||
#endif // (MQTT_LIBRARY == MQTT_LIBRARY_ARDUINOMQTT) || (MQTT_LIBRARY == MQTT_LIBRARY_PUBSUBCLIENT)
|
||||
|
||||
|
||||
void _mqttConnect() {
|
||||
|
||||
// Do not connect if disabled
|
||||
if (!_mqtt_enabled) return;
|
||||
|
||||
// Do not connect if already connected or still trying to connect
|
||||
if (_mqtt.connected() || (_mqtt_state != AsyncClientState::Disconnected)) return;
|
||||
|
||||
// Check reconnect interval
|
||||
if (millis() - _mqtt_last_connection < _mqtt_reconnect_delay) return;
|
||||
|
||||
// Increase the reconnect delay
|
||||
_mqtt_reconnect_delay += MQTT_RECONNECT_DELAY_STEP;
|
||||
if (_mqtt_reconnect_delay > MQTT_RECONNECT_DELAY_MAX) {
|
||||
_mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MAX;
|
||||
}
|
||||
|
||||
#if MDNS_CLIENT_SUPPORT
|
||||
_mqtt_server = mdnsResolve(_mqtt_server);
|
||||
#endif
|
||||
|
||||
DEBUG_MSG_P(PSTR("[MQTT] Connecting to broker at %s:%u\n"), _mqtt_server.c_str(), _mqtt_port);
|
||||
|
||||
DEBUG_MSG_P(PSTR("[MQTT] Client ID: %s\n"), _mqtt_clientid.c_str());
|
||||
DEBUG_MSG_P(PSTR("[MQTT] QoS: %d\n"), _mqtt_qos);
|
||||
DEBUG_MSG_P(PSTR("[MQTT] Retain flag: %d\n"), _mqtt_retain ? 1 : 0);
|
||||
DEBUG_MSG_P(PSTR("[MQTT] Keepalive time: %ds\n"), _mqtt_keepalive);
|
||||
DEBUG_MSG_P(PSTR("[MQTT] Will topic: %s\n"), _mqtt_will.c_str());
|
||||
|
||||
_mqtt_state = AsyncClientState::Connecting;
|
||||
|
||||
#if SECURE_CLIENT != SECURE_CLIENT_NONE
|
||||
const bool secure = getSetting("mqttUseSSL", 1 == MQTT_SSL_ENABLED);
|
||||
#else
|
||||
const bool secure = false;
|
||||
#endif
|
||||
|
||||
#if MQTT_LIBRARY == MQTT_LIBRARY_ASYNCMQTTCLIENT
|
||||
_mqttSetupAsyncClient(secure);
|
||||
#elif (MQTT_LIBRARY == MQTT_LIBRARY_ARDUINOMQTT) || (MQTT_LIBRARY == MQTT_LIBRARY_PUBSUBCLIENT)
|
||||
if (_mqttSetupSyncClient(secure) && _mqttConnectSyncClient(secure)) {
|
||||
_mqttOnConnect();
|
||||
} else {
|
||||
DEBUG_MSG_P(PSTR("[MQTT] Connection failed\n"));
|
||||
_mqttOnDisconnect();
|
||||
}
|
||||
#else
|
||||
#error "please check that MQTT_LIBRARY is valid"
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void _mqttPlaceholders(String& text) {
|
||||
|
||||
text.replace("{hostname}", getSetting("hostname"));
|
||||
@@ -1014,6 +964,86 @@ void mqttSendStatus() {
|
||||
// Initialization
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void _mqttConnect() {
|
||||
|
||||
// Do not connect if disabled
|
||||
if (!_mqtt_enabled) return;
|
||||
|
||||
// Do not connect if already connected or still trying to connect
|
||||
if (_mqtt.connected() || (_mqtt_state != AsyncClientState::Disconnected)) return;
|
||||
|
||||
// Check reconnect interval
|
||||
if (millis() - _mqtt_last_connection < _mqtt_reconnect_delay) return;
|
||||
|
||||
// Increase the reconnect delay
|
||||
_mqtt_reconnect_delay += MQTT_RECONNECT_DELAY_STEP;
|
||||
if (_mqtt_reconnect_delay > MQTT_RECONNECT_DELAY_MAX) {
|
||||
_mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MAX;
|
||||
}
|
||||
|
||||
#if MDNS_CLIENT_SUPPORT
|
||||
_mqtt_server = mdnsResolve(_mqtt_server);
|
||||
#endif
|
||||
|
||||
DEBUG_MSG_P(PSTR("[MQTT] Connecting to broker at %s:%u\n"), _mqtt_server.c_str(), _mqtt_port);
|
||||
|
||||
DEBUG_MSG_P(PSTR("[MQTT] Client ID: %s\n"), _mqtt_clientid.c_str());
|
||||
DEBUG_MSG_P(PSTR("[MQTT] QoS: %d\n"), _mqtt_qos);
|
||||
DEBUG_MSG_P(PSTR("[MQTT] Retain flag: %d\n"), _mqtt_retain ? 1 : 0);
|
||||
DEBUG_MSG_P(PSTR("[MQTT] Keepalive time: %ds\n"), _mqtt_keepalive);
|
||||
DEBUG_MSG_P(PSTR("[MQTT] Will topic: %s\n"), _mqtt_will.c_str());
|
||||
|
||||
_mqtt_state = AsyncClientState::Connecting;
|
||||
|
||||
#if SECURE_CLIENT != SECURE_CLIENT_NONE
|
||||
const bool secure = getSetting("mqttUseSSL", 1 == MQTT_SSL_ENABLED);
|
||||
#else
|
||||
const bool secure = false;
|
||||
#endif
|
||||
|
||||
#if MQTT_LIBRARY == MQTT_LIBRARY_ASYNCMQTTCLIENT
|
||||
_mqttSetupAsyncClient(secure);
|
||||
#elif (MQTT_LIBRARY == MQTT_LIBRARY_ARDUINOMQTT) || (MQTT_LIBRARY == MQTT_LIBRARY_PUBSUBCLIENT)
|
||||
if (_mqttSetupSyncClient(secure) && _mqttConnectSyncClient(secure)) {
|
||||
_mqttOnConnect();
|
||||
} else {
|
||||
DEBUG_MSG_P(PSTR("[MQTT] Connection failed\n"));
|
||||
_mqttOnDisconnect();
|
||||
}
|
||||
#else
|
||||
#error "please check that MQTT_LIBRARY is valid"
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void mqttLoop() {
|
||||
|
||||
if (WiFi.status() != WL_CONNECTED) return;
|
||||
|
||||
#if MQTT_LIBRARY == MQTT_LIBRARY_ASYNCMQTTCLIENT
|
||||
|
||||
_mqttConnect();
|
||||
|
||||
#else // MQTT_LIBRARY != MQTT_LIBRARY_ASYNCMQTTCLIENT
|
||||
|
||||
if (_mqtt.connected()) {
|
||||
|
||||
_mqtt.loop();
|
||||
|
||||
} else {
|
||||
|
||||
if (_mqtt_state != AsyncClientState::Disconnected) {
|
||||
_mqttOnDisconnect();
|
||||
}
|
||||
|
||||
_mqttConnect();
|
||||
|
||||
}
|
||||
|
||||
#endif // MQTT_LIBRARY == MQTT_LIBRARY_ASYNCMQTTCLIENT
|
||||
|
||||
}
|
||||
|
||||
void mqttSetup() {
|
||||
|
||||
_mqttBackwards();
|
||||
@@ -1132,38 +1162,4 @@ void mqttSetup() {
|
||||
|
||||
}
|
||||
|
||||
void mqttLoop() {
|
||||
|
||||
if (WiFi.status() != WL_CONNECTED) return;
|
||||
|
||||
#if MQTT_LIBRARY == MQTT_LIBRARY_ASYNCMQTTCLIENT
|
||||
|
||||
_mqttConnect();
|
||||
|
||||
#else // MQTT_LIBRARY != MQTT_LIBRARY_ASYNCMQTTCLIENT
|
||||
|
||||
if (_mqtt.connected()) {
|
||||
|
||||
_mqtt.loop();
|
||||
|
||||
} else {
|
||||
|
||||
if (_mqtt_state != AsyncClientState::Disconnected) {
|
||||
_mqttOnDisconnect();
|
||||
}
|
||||
|
||||
_mqttConnect();
|
||||
|
||||
}
|
||||
|
||||
#endif // MQTT_LIBRARY == MQTT_LIBRARY_ASYNCMQTTCLIENT
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool mqttForward() {
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // MQTT_SUPPORT
|
||||
@@ -9,6 +9,8 @@ Updated secure client support by Niek van der Maas < mail at niekvandermaas dot
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#include <WString.h>
|
||||
|
||||
#include <utility>
|
||||
@@ -17,9 +19,6 @@ Updated secure client support by Niek van der Maas < mail at niekvandermaas dot
|
||||
using mqtt_callback_f = std::function<void(unsigned int type, const char * topic, char * payload)>;
|
||||
using mqtt_msg_t = std::pair<String, String>; // topic, payload
|
||||
|
||||
// TODO: need this prototype for .ino
|
||||
class AsyncMqttClientMessageProperties;
|
||||
|
||||
#if MQTT_SUPPORT
|
||||
|
||||
#if MQTT_LIBRARY == MQTT_LIBRARY_ASYNCMQTTCLIENT
|
||||
@@ -45,13 +44,37 @@ void mqttSend(const char * topic, const char * message, bool force, bool retain)
|
||||
void mqttSend(const char * topic, const char * message, bool force);
|
||||
void mqttSend(const char * topic, const char * message);
|
||||
|
||||
void mqttSend(const char * topic, unsigned int index, const char * message, bool force, bool retain);
|
||||
void mqttSend(const char * topic, unsigned int index, const char * message, bool force);
|
||||
void mqttSend(const char * topic, unsigned int index, const char * message);
|
||||
|
||||
void mqttSendStatus();
|
||||
void mqttFlush();
|
||||
|
||||
int8_t mqttEnqueue(const char * topic, const char * message, unsigned char parent);
|
||||
int8_t mqttEnqueue(const char * topic, const char * message);
|
||||
|
||||
const String& mqttPayloadOnline();
|
||||
const String& mqttPayloadOffline();
|
||||
const char* mqttPayloadStatus(bool status);
|
||||
|
||||
void mqttSendStatus();
|
||||
void mqttSetBroker(IPAddress ip, uint16_t port);
|
||||
void mqttSetBrokerIfNone(IPAddress ip, uint16_t port);
|
||||
|
||||
void mqttSubscribeRaw(const char * topic);
|
||||
void mqttSubscribe(const char * topic);
|
||||
|
||||
void mqttUnsubscribeRaw(const char * topic);
|
||||
void mqttUnsubscribe(const char * topic);
|
||||
|
||||
void mqttEnabled(bool status);
|
||||
bool mqttEnabled();
|
||||
|
||||
bool mqttForward();
|
||||
|
||||
bool mqttConnected();
|
||||
|
||||
void mqttDisconnect();
|
||||
void mqttSetup();
|
||||
|
||||
#endif // MQTT_SUPPORT == 1
|
||||
|
||||
@@ -6,9 +6,9 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#if NETBIOS_SUPPORT
|
||||
#include "netbios.h"
|
||||
|
||||
#include <ESP8266NetBIOS.h>
|
||||
#if NETBIOS_SUPPORT
|
||||
|
||||
void netbiosSetup() {
|
||||
static WiFiEventHandler _netbios_wifi_onSTA = WiFi.onStationModeGotIP([](WiFiEventStationModeGotIP ipInfo) {
|
||||
17
code/espurna/netbios.h
Normal file
17
code/espurna/netbios.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
|
||||
NETBIOS MODULE
|
||||
|
||||
Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#if NETBIOS_SUPPORT
|
||||
|
||||
#include <ESP8266NetBIOS.h>
|
||||
|
||||
void netbiosSetup();
|
||||
|
||||
#endif // NETBIOS_SUPPORT
|
||||
@@ -6,9 +6,13 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "nofuss.h"
|
||||
|
||||
#if NOFUSS_SUPPORT
|
||||
|
||||
#include "NoFUSSClient.h"
|
||||
#include "wifi.h"
|
||||
#include "mdns.h"
|
||||
#include "terminal.h"
|
||||
#include "ws.h"
|
||||
|
||||
unsigned long _nofussLastCheck = 0;
|
||||
@@ -73,6 +77,23 @@ void _nofussConfigure() {
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void nofussRun() {
|
||||
NoFUSSClient.handle();
|
||||
_nofussLastCheck = millis();
|
||||
}
|
||||
|
||||
void _nofussLoop() {
|
||||
|
||||
if (!_nofussEnabled) return;
|
||||
if (!wifiConnected()) return;
|
||||
if ((_nofussLastCheck > 0) && ((millis() - _nofussLastCheck) < _nofussInterval)) return;
|
||||
|
||||
nofussRun();
|
||||
|
||||
}
|
||||
|
||||
#if TERMINAL_SUPPORT
|
||||
|
||||
void _nofussInitCommands() {
|
||||
@@ -86,13 +107,6 @@ void _nofussInitCommands() {
|
||||
|
||||
#endif // TERMINAL_SUPPORT
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void nofussRun() {
|
||||
NoFUSSClient.handle();
|
||||
_nofussLastCheck = millis();
|
||||
}
|
||||
|
||||
void nofussSetup() {
|
||||
|
||||
_nofussConfigure();
|
||||
@@ -177,19 +191,9 @@ void nofussSetup() {
|
||||
#endif
|
||||
|
||||
// Main callbacks
|
||||
espurnaRegisterLoop(nofussLoop);
|
||||
espurnaRegisterLoop(_nofussLoop);
|
||||
espurnaRegisterReload(_nofussConfigure);
|
||||
|
||||
}
|
||||
|
||||
void nofussLoop() {
|
||||
|
||||
if (!_nofussEnabled) return;
|
||||
if (!wifiConnected()) return;
|
||||
if ((_nofussLastCheck > 0) && ((millis() - _nofussLastCheck) < _nofussInterval)) return;
|
||||
|
||||
nofussRun();
|
||||
|
||||
}
|
||||
|
||||
#endif // NOFUSS_SUPPORT
|
||||
17
code/espurna/nofuss.h
Normal file
17
code/espurna/nofuss.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
|
||||
NOFUSS MODULE
|
||||
|
||||
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#if NOFUSS_SUPPORT
|
||||
|
||||
#include <NoFUSSClient.h>
|
||||
|
||||
void nofussSetup();
|
||||
|
||||
#endif // NOFUSS_SUPPORT
|
||||
@@ -11,6 +11,8 @@ Copyright (C) 2019 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "ntp.h"
|
||||
|
||||
#if NTP_SUPPORT && !NTP_LEGACY_SUPPORT
|
||||
|
||||
#include <Arduino.h>
|
||||
@@ -26,7 +28,6 @@ static_assert(
|
||||
#include "debug.h"
|
||||
#include "broker.h"
|
||||
#include "ws.h"
|
||||
#include "ntp.h"
|
||||
|
||||
// Arduino/esp8266 lwip2 custom functions that can be redefined
|
||||
// Must return time in milliseconds, legacy settings are in seconds.
|
||||
@@ -258,10 +259,7 @@ String ntpDateTime() {
|
||||
|
||||
#if BROKER_SUPPORT
|
||||
|
||||
// XXX: Nonos docs for some reason mention 100 micros as minimum time. Schedule next second in case this is 0
|
||||
void _ntpBrokerSchedule(int offset) {
|
||||
_ntp_broker_timer.once_scheduled(offset ?: 1, _ntpBrokerCallback);
|
||||
}
|
||||
void _ntpBrokerSchedule(int offset);
|
||||
|
||||
void _ntpBrokerCallback() {
|
||||
|
||||
@@ -303,6 +301,11 @@ void _ntpBrokerCallback() {
|
||||
|
||||
}
|
||||
|
||||
// XXX: Nonos docs for some reason mention 100 micros as minimum time. Schedule next second in case this is 0
|
||||
void _ntpBrokerSchedule(int offset) {
|
||||
_ntp_broker_timer.once_scheduled(offset ?: 1, _ntpBrokerCallback);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void _ntpSetTimeOfDayCallback() {
|
||||
@@ -6,17 +6,19 @@ NTP MODULE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "broker.h"
|
||||
|
||||
// TODO: need this prototype for .ino
|
||||
struct NtpCalendarWeekday;
|
||||
#include "espurna.h"
|
||||
|
||||
#if NTP_SUPPORT
|
||||
|
||||
#include "broker.h"
|
||||
|
||||
#if NTP_LEGACY_SUPPORT // Use legacy TimeLib and NtpClientLib
|
||||
|
||||
#include <TimeLib.h>
|
||||
#include "libs/NtpClientWrap.h"
|
||||
#include <WiFiUdp.h>
|
||||
#include <NtpClientLib.h>
|
||||
|
||||
time_t ntpLocal2UTC(time_t local);
|
||||
|
||||
#else // POSIX time functions + configTime(...)
|
||||
|
||||
@@ -44,8 +46,10 @@ struct NtpCalendarWeekday {
|
||||
|
||||
using NtpBroker = TBroker<TBrokerType::Datetime, const NtpTick, time_t, const String&>;
|
||||
|
||||
String ntpDateTime(tm* timestruct);
|
||||
String ntpDateTime(time_t ts);
|
||||
String ntpDateTime();
|
||||
bool ntpSynced();
|
||||
|
||||
void ntpSetup();
|
||||
|
||||
|
||||
@@ -6,13 +6,15 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "ntp.h"
|
||||
|
||||
#if NTP_LEGACY_SUPPORT && NTP_SUPPORT
|
||||
|
||||
#include <Ticker.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "broker.h"
|
||||
#include "ws.h"
|
||||
#include "ntp.h"
|
||||
|
||||
Ticker _ntp_defer;
|
||||
|
||||
@@ -20,6 +22,31 @@ bool _ntp_report = false;
|
||||
bool _ntp_configure = false;
|
||||
bool _ntp_want_sync = false;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// NtpClient overrides to avoid triggering network sync
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class NTPClientWrap : public NTPClient {
|
||||
|
||||
public:
|
||||
|
||||
NTPClientWrap() : NTPClient() {
|
||||
udp = new WiFiUDP();
|
||||
_lastSyncd = 0;
|
||||
}
|
||||
|
||||
bool setInterval(int shortInterval, int longInterval) {
|
||||
_shortInterval = shortInterval;
|
||||
_longInterval = longInterval;
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// NOTE: original NTP should be discarded by the linker
|
||||
// TODO: allow NTP client object to be destroyed
|
||||
static NTPClientWrap NTPw;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// NTP
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -67,24 +94,6 @@ int _ntpUpdateInterval() {
|
||||
return secureRandom(NTP_UPDATE_INTERVAL, NTP_UPDATE_INTERVAL * 2);
|
||||
}
|
||||
|
||||
void _ntpStart() {
|
||||
|
||||
_ntpConfigure();
|
||||
|
||||
// short (initial) and long (after sync) intervals
|
||||
NTPw.setInterval(_ntpSyncInterval(), _ntpUpdateInterval());
|
||||
DEBUG_MSG_P(PSTR("[NTP] Update intervals: %us / %us\n"),
|
||||
NTPw.getShortInterval(), NTPw.getLongInterval());
|
||||
|
||||
// setSyncProvider will immediatly call given function by setting next sync time to the current time.
|
||||
// Avoid triggering sync immediatly by canceling sync provider flag and resetting sync interval again
|
||||
setSyncProvider(_ntpSyncProvider);
|
||||
_ntp_want_sync = false;
|
||||
|
||||
setSyncInterval(NTPw.getShortInterval());
|
||||
|
||||
}
|
||||
|
||||
void _ntpConfigure() {
|
||||
|
||||
_ntp_configure = false;
|
||||
@@ -119,6 +128,25 @@ void _ntpConfigure() {
|
||||
|
||||
}
|
||||
|
||||
void _ntpStart() {
|
||||
|
||||
_ntpConfigure();
|
||||
|
||||
// short (initial) and long (after sync) intervals
|
||||
NTPw.setInterval(_ntpSyncInterval(), _ntpUpdateInterval());
|
||||
DEBUG_MSG_P(PSTR("[NTP] Update intervals: %us / %us\n"),
|
||||
NTPw.getShortInterval(), NTPw.getLongInterval());
|
||||
|
||||
// setSyncProvider will immediatly call given function by setting next sync time to the current time.
|
||||
// Avoid triggering sync immediatly by canceling sync provider flag and resetting sync interval again
|
||||
setSyncProvider(_ntpSyncProvider);
|
||||
_ntp_want_sync = false;
|
||||
|
||||
setSyncInterval(NTPw.getShortInterval());
|
||||
|
||||
}
|
||||
|
||||
|
||||
void _ntpReport() {
|
||||
|
||||
_ntp_report = false;
|
||||
@@ -6,8 +6,10 @@ OTA MODULE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Updater.h>
|
||||
#include "espurna.h"
|
||||
|
||||
#include <ArduinoOTA.h>
|
||||
#include <Updater.h>
|
||||
|
||||
#if OTA_WEB_SUPPORT
|
||||
|
||||
|
||||
@@ -6,9 +6,10 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "ota.h"
|
||||
|
||||
#if OTA_ARDUINOOTA_SUPPORT
|
||||
|
||||
#include "ota.h"
|
||||
#include "system.h"
|
||||
#include "ws.h"
|
||||
|
||||
@@ -6,20 +6,25 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "ota.h"
|
||||
|
||||
#if OTA_CLIENT == OTA_CLIENT_ASYNCTCP
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Terminal and MQTT OTA command handlers
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "espurna.h"
|
||||
|
||||
#if TERMINAL_SUPPORT || OTA_MQTT_SUPPORT
|
||||
|
||||
#include <Schedule.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
|
||||
#include "mqtt.h"
|
||||
#include "ota.h"
|
||||
#include "system.h"
|
||||
#include "settings.h"
|
||||
#include "terminal.h"
|
||||
|
||||
#include "libs/URL.h"
|
||||
@@ -10,12 +10,13 @@ Copyright (C) 2019 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
|
||||
// OTA by using Core's HTTP(s) updater
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "ota.h"
|
||||
|
||||
#if OTA_CLIENT == OTA_CLIENT_HTTPUPDATE
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "mqtt.h"
|
||||
#include "ota.h"
|
||||
#include "system.h"
|
||||
#include "terminal.h"
|
||||
|
||||
@@ -8,14 +8,12 @@ Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
|
||||
*/
|
||||
|
||||
#include "ota.h"
|
||||
#include "settings.h"
|
||||
#include "storage_eeprom.h"
|
||||
#include "utils.h"
|
||||
#include "web.h"
|
||||
#include "ws.h"
|
||||
|
||||
#if WEB_SUPPORT && OTA_WEB_SUPPORT
|
||||
|
||||
#include "web.h"
|
||||
#include "ws.h"
|
||||
|
||||
void _onUpgradeResponse(AsyncWebServerRequest *request, int code, const String& payload = "") {
|
||||
|
||||
auto *response = request->beginResponseStream("text/plain", 256);
|
||||
@@ -6,6 +6,8 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "relay.h"
|
||||
|
||||
#if RELAY_SUPPORT
|
||||
|
||||
#include <Ticker.h>
|
||||
@@ -14,13 +16,17 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
#include <functional>
|
||||
#include <bitset>
|
||||
|
||||
#include "api.h"
|
||||
#include "broker.h"
|
||||
#include "storage_eeprom.h"
|
||||
#include "settings.h"
|
||||
#include "light.h"
|
||||
#include "mqtt.h"
|
||||
#include "relay.h"
|
||||
#include "rfbridge.h"
|
||||
#include "rpc.h"
|
||||
#include "rtcmem.h"
|
||||
#include "settings.h"
|
||||
#include "storage_eeprom.h"
|
||||
#include "tuya.h"
|
||||
#include "utils.h"
|
||||
#include "ws.h"
|
||||
|
||||
#include "relay_config.h"
|
||||
@@ -423,9 +429,38 @@ void setSpeed(unsigned char speed) {
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// State persistance persistance
|
||||
namespace {
|
||||
|
||||
String u32toString(uint32_t value, int base) {
|
||||
String result;
|
||||
result.reserve(32 + 2);
|
||||
|
||||
if (base == 2) {
|
||||
result += "0b";
|
||||
} else if (base == 8) {
|
||||
result += "0o";
|
||||
} else if (base == 16) {
|
||||
result += "0x";
|
||||
}
|
||||
|
||||
char buffer[33] = {0};
|
||||
ultoa(value, buffer, base);
|
||||
result += buffer;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct RelayMask {
|
||||
const String as_string;
|
||||
uint32_t as_u32;
|
||||
};
|
||||
|
||||
RelayMask INLINE _relayMask(uint32_t mask) {
|
||||
return {std::move(u32toString(mask, 2)), mask};
|
||||
}
|
||||
|
||||
RelayMask INLINE _relayMaskRtcmem() {
|
||||
return RelayMask(Rtcmem->relay);
|
||||
return _relayMask(Rtcmem->relay);
|
||||
}
|
||||
|
||||
void INLINE _relayMaskRtcmem(uint32_t mask) {
|
||||
@@ -441,7 +476,9 @@ void INLINE _relayMaskRtcmem(const std::bitset<RELAYS_MAX>& bitset) {
|
||||
}
|
||||
|
||||
RelayMask INLINE _relayMaskSettings() {
|
||||
return RelayMask(getSetting("relayBootMask"));
|
||||
constexpr unsigned long defaultMask { 0ul };
|
||||
auto value = getSetting("relayBootMask", defaultMask);
|
||||
return _relayMask(value);
|
||||
}
|
||||
|
||||
void INLINE _relayMaskSettings(uint32_t mask) {
|
||||
@@ -456,6 +493,8 @@ void INLINE _relayMaskSettings(const std::bitset<RELAYS_MAX>& bitset) {
|
||||
_relayMaskSettings(bitset.to_ulong());
|
||||
}
|
||||
|
||||
} // ns anonymous
|
||||
|
||||
// Pulse timers (timer after ON or OFF event)
|
||||
|
||||
void relayPulse(unsigned char id) {
|
||||
@@ -563,7 +602,11 @@ bool relayStatus(unsigned char id, bool status, bool report, bool group_report)
|
||||
}
|
||||
|
||||
bool relayStatus(unsigned char id, bool status) {
|
||||
return relayStatus(id, status, mqttForward(), true);
|
||||
#if MQTT_SUPPORT
|
||||
return relayStatus(id, status, mqttForward(), true);
|
||||
#else
|
||||
return relayStatus(id, status, false, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool relayStatus(unsigned char id) {
|
||||
@@ -641,7 +684,7 @@ void relaySave(bool eeprom) {
|
||||
statuses.set(id, relayStatus(id));
|
||||
}
|
||||
|
||||
const RelayMask mask(statuses);
|
||||
const auto mask = _relayMask(statuses.to_ulong() & 0xffffffffu);
|
||||
DEBUG_MSG_P(PSTR("[RELAY] Setting relay mask: %s\n"), mask.as_string.c_str());
|
||||
|
||||
// Persist only to rtcmem, unless requested to save to the eeprom
|
||||
@@ -672,7 +715,11 @@ void relayToggle(unsigned char id, bool report, bool group_report) {
|
||||
}
|
||||
|
||||
void relayToggle(unsigned char id) {
|
||||
relayToggle(id, mqttForward(), true);
|
||||
#if MQTT_SUPPORT
|
||||
relayToggle(id, mqttForward(), true);
|
||||
#else
|
||||
relayToggle(id, false, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned char relayCount() {
|
||||
@@ -781,7 +828,7 @@ void _relayBoot() {
|
||||
_relayRecursive = false;
|
||||
|
||||
#if TUYA_SUPPORT
|
||||
tuyaSyncSwitchStatus();
|
||||
Tuya::tuyaSyncSwitchStatus();
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1114,12 +1161,17 @@ void relayMQTT() {
|
||||
}
|
||||
|
||||
void relayStatusWrap(unsigned char id, PayloadStatus value, bool is_group_topic) {
|
||||
#if MQTT_SUPPORT
|
||||
const auto forward = mqttForward();
|
||||
#else
|
||||
const auto forward = false;
|
||||
#endif
|
||||
switch (value) {
|
||||
case PayloadStatus::Off:
|
||||
relayStatus(id, false, mqttForward(), !is_group_topic);
|
||||
relayStatus(id, false, forward, !is_group_topic);
|
||||
break;
|
||||
case PayloadStatus::On:
|
||||
relayStatus(id, true, mqttForward(), !is_group_topic);
|
||||
relayStatus(id, true, forward, !is_group_topic);
|
||||
break;
|
||||
case PayloadStatus::Toggle:
|
||||
relayToggle(id, true, true);
|
||||
@@ -1270,7 +1322,7 @@ void _relaySetupProvider() {
|
||||
// note of the function call order! relay code is initialized before tuya's, and the easiest
|
||||
// way to accomplish that is to use ctor as a way to "register" callbacks even before setup() is called
|
||||
#if TUYA_SUPPORT
|
||||
tuyaSetupSwitch();
|
||||
Tuya::tuyaSetupSwitch();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -8,43 +8,13 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#include "espurna.h"
|
||||
#include "rpc.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <bitset>
|
||||
|
||||
constexpr size_t RELAYS_MAX = 32;
|
||||
|
||||
struct RelayMask {
|
||||
|
||||
explicit RelayMask(const String& string) :
|
||||
as_string(string),
|
||||
as_u32(u32fromString(string))
|
||||
{}
|
||||
|
||||
explicit RelayMask(String&& string) :
|
||||
as_string(std::move(string)),
|
||||
as_u32(u32fromString(as_string))
|
||||
{}
|
||||
|
||||
explicit RelayMask(uint32_t value) :
|
||||
as_string(std::move(u32toString(value, 2))),
|
||||
as_u32(value)
|
||||
{}
|
||||
|
||||
explicit RelayMask(std::bitset<RELAYS_MAX> bitset) :
|
||||
RelayMask(bitset.to_ulong())
|
||||
{}
|
||||
|
||||
RelayMask(String&& string, uint32_t value) :
|
||||
as_string(std::move(string)),
|
||||
as_u32(value)
|
||||
{}
|
||||
|
||||
const String as_string;
|
||||
uint32_t as_u32;
|
||||
|
||||
};
|
||||
|
||||
PayloadStatus relayParsePayload(const char * payload);
|
||||
|
||||
bool relayStatus(unsigned char id, bool status, bool report, bool group_report);
|
||||
@@ -62,5 +32,12 @@ const String& relayPayloadToggle();
|
||||
|
||||
const char* relayPayload(PayloadStatus status);
|
||||
|
||||
void relaySetupDummy(size_t size, bool reconfigure = false);
|
||||
void relayMQTT(unsigned char id);
|
||||
void relayMQTT();
|
||||
|
||||
void relayPulse(unsigned char id);
|
||||
void relaySync(unsigned char id);
|
||||
void relaySave(bool eeprom);
|
||||
|
||||
void relaySetupDummy(size_t size, bool reconfigure = false);
|
||||
void relaySetup();
|
||||
|
||||
@@ -6,6 +6,8 @@ RELAY MODULE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
constexpr const unsigned long _relayDelayOn(unsigned char index) {
|
||||
return (
|
||||
(index == 0) ? RELAY1_DELAY_ON :
|
||||
|
||||
@@ -6,12 +6,14 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "rfbridge.h"
|
||||
|
||||
#if RF_SUPPORT
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "api.h"
|
||||
#include "relay.h"
|
||||
#include "rfbridge.h"
|
||||
#include "terminal.h"
|
||||
#include "mqtt.h"
|
||||
#include "ws.h"
|
||||
@@ -55,10 +57,10 @@ unsigned char _learnId = 0;
|
||||
bool _learnStatus = true;
|
||||
bool _rfbin = false;
|
||||
|
||||
typedef struct {
|
||||
byte code[RF_MESSAGE_SIZE];
|
||||
byte times;
|
||||
} rfb_message_t;
|
||||
struct rfb_message_t {
|
||||
uint8_t code[RF_MESSAGE_SIZE];
|
||||
uint8_t times;
|
||||
};
|
||||
static std::queue<rfb_message_t> _rfb_message_queue;
|
||||
|
||||
#if RFB_DIRECT
|
||||
@@ -74,10 +76,8 @@ unsigned char _rfb_repeat = RF_SEND_TIMES;
|
||||
// PRIVATES
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
From a byte array to an hexa char array ("A220EE...", double the size)
|
||||
*/
|
||||
static bool _rfbToChar(byte * in, char * out, int n = RF_MESSAGE_SIZE) {
|
||||
// From a byte array to an hexa char array ("A220EE...", double the size)
|
||||
static bool _rfbToChar(uint8_t * in, char * out, int n = RF_MESSAGE_SIZE) {
|
||||
for (unsigned char p = 0; p<n; p++) {
|
||||
sprintf_P(&out[p*2], PSTR("%02X"), in[p]);
|
||||
}
|
||||
@@ -94,7 +94,7 @@ void _rfbWebSocketSendCodeArray(JsonObject& root, unsigned char start, unsigned
|
||||
JsonArray& on = rfb.createNestedArray("on");
|
||||
JsonArray& off = rfb.createNestedArray("off");
|
||||
|
||||
for (byte id=start; id<start+size; id++) {
|
||||
for (uint8_t id=start; id<start+size; id++) {
|
||||
on.add(rfbRetrieve(id, true));
|
||||
off.add(rfbRetrieve(id, false));
|
||||
}
|
||||
@@ -130,10 +130,8 @@ void _rfbWebSocketOnData(JsonObject& root) {
|
||||
|
||||
#endif // WEB_SUPPORT
|
||||
|
||||
/*
|
||||
From an hexa char array ("A220EE...") to a byte array (half the size)
|
||||
*/
|
||||
static int _rfbToArray(const char * in, byte * out, int length = RF_MESSAGE_SIZE * 2) {
|
||||
// From an hexa char array ("A220EE...") to a byte array (half the size)
|
||||
static int _rfbToArray(const char * in, uint8_t * out, int length = RF_MESSAGE_SIZE * 2) {
|
||||
int n = strlen(in);
|
||||
if (n > RF_MAX_MESSAGE_SIZE*2 || (length > 0 && n != length)) return 0;
|
||||
char tmp[3] = {0,0,0};
|
||||
@@ -145,64 +143,10 @@ static int _rfbToArray(const char * in, byte * out, int length = RF_MESSAGE_SIZE
|
||||
return n;
|
||||
}
|
||||
|
||||
void _rfbSendRaw(const byte *message, const unsigned char n = RF_MESSAGE_SIZE) {
|
||||
for (unsigned char j=0; j<n; j++) {
|
||||
Serial.write(message[j]);
|
||||
}
|
||||
}
|
||||
|
||||
void _rfbSend() {
|
||||
|
||||
if (!_rfb_transmit) return;
|
||||
|
||||
// Check if there is something in the queue
|
||||
if (_rfb_message_queue.empty()) return;
|
||||
|
||||
static unsigned long last = 0;
|
||||
if (millis() - last < RF_SEND_DELAY) return;
|
||||
last = millis();
|
||||
|
||||
// Pop the first message and send it
|
||||
rfb_message_t message = _rfb_message_queue.front();
|
||||
_rfb_message_queue.pop();
|
||||
_rfbSend(message.code);
|
||||
|
||||
// Push it to the stack again if we need to send it more than once
|
||||
if (message.times > 1) {
|
||||
message.times = message.times - 1;
|
||||
_rfb_message_queue.push(message);
|
||||
}
|
||||
|
||||
yield();
|
||||
|
||||
}
|
||||
|
||||
void _rfbSend(byte * code, unsigned char times) {
|
||||
|
||||
if (!_rfb_transmit) return;
|
||||
|
||||
// rc-switch will repeat on its own
|
||||
#if RFB_DIRECT
|
||||
times = 1;
|
||||
#endif
|
||||
|
||||
char buffer[RF_MESSAGE_SIZE];
|
||||
_rfbToChar(code, buffer);
|
||||
DEBUG_MSG_P(PSTR("[RF] Enqueuing MESSAGE '%s' %d time(s)\n"), buffer, times);
|
||||
|
||||
rfb_message_t message;
|
||||
memcpy(message.code, code, RF_MESSAGE_SIZE);
|
||||
message.times = times;
|
||||
_rfb_message_queue.push(message);
|
||||
|
||||
}
|
||||
|
||||
void _rfbSendRawOnce(byte *code, unsigned char length) {
|
||||
char buffer[length*2];
|
||||
_rfbToChar(code, buffer, length);
|
||||
DEBUG_MSG_P(PSTR("[RF] Sending RAW MESSAGE '%s'\n"), buffer);
|
||||
_rfbSendRaw(code, length);
|
||||
}
|
||||
void _rfbAck();
|
||||
void _rfbLearnImpl();
|
||||
void _rfbSend(uint8_t * message);
|
||||
void _rfbReceive();
|
||||
|
||||
bool _rfbMatch(char* code, unsigned char& relayID, unsigned char& value, char* buffer = NULL) {
|
||||
|
||||
@@ -248,7 +192,7 @@ void _rfbDecode() {
|
||||
if (millis() - last < RF_RECEIVE_DELAY) return;
|
||||
last = millis();
|
||||
|
||||
byte action = _uartbuf[0];
|
||||
uint8_t action = _uartbuf[0];
|
||||
char buffer[RF_MESSAGE_SIZE * 2 + 1] = {0};
|
||||
DEBUG_MSG_P(PSTR("[RF] Action 0x%02X\n"), action);
|
||||
|
||||
@@ -308,50 +252,6 @@ void _rfbDecode() {
|
||||
|
||||
}
|
||||
|
||||
bool _rfbCompare(const char * code1, const char * code2) {
|
||||
return strcmp(&code1[12], &code2[12]) == 0;
|
||||
}
|
||||
|
||||
bool _rfbSameOnOff(unsigned char id) {
|
||||
return _rfbCompare(rfbRetrieve(id, true).c_str(), rfbRetrieve(id, false).c_str());
|
||||
}
|
||||
|
||||
void _rfbParseRaw(char * raw) {
|
||||
byte message[RF_MAX_MESSAGE_SIZE];
|
||||
int len = _rfbToArray(raw, message, 0);
|
||||
if (len > 0) {
|
||||
_rfbSendRawOnce(message, len);
|
||||
}
|
||||
}
|
||||
|
||||
void _rfbParseCode(char * code) {
|
||||
|
||||
// The payload may be a code in HEX format ([0-9A-Z]{18}) or
|
||||
// the code comma the number of times to transmit it.
|
||||
char * tok = strtok(code, ",");
|
||||
|
||||
// Check if a switch is linked to that message
|
||||
unsigned char id;
|
||||
unsigned char status = 0;
|
||||
if (_rfbMatch(tok, id, status)) {
|
||||
if (status == 2) {
|
||||
relayToggle(id);
|
||||
} else {
|
||||
relayStatus(id, status == 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
byte message[RF_MESSAGE_SIZE];
|
||||
int len = _rfbToArray(tok, message, 0);
|
||||
if (len) {
|
||||
tok = strtok(NULL, ",");
|
||||
byte times = (tok != NULL) ? atoi(tok) : 1;
|
||||
_rfbSend(message, times);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// RF handler implementations
|
||||
@@ -379,7 +279,7 @@ void _rfbLearnImpl() {
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void _rfbSend(byte * message) {
|
||||
void _rfbSend(uint8_t * message) {
|
||||
Serial.println();
|
||||
Serial.write(RF_CODE_START);
|
||||
Serial.write(RF_CODE_RFOUT);
|
||||
@@ -396,7 +296,7 @@ void _rfbReceive() {
|
||||
while (Serial.available()) {
|
||||
|
||||
yield();
|
||||
byte c = Serial.read();
|
||||
uint8_t c = Serial.read();
|
||||
//DEBUG_MSG_P(PSTR("[RF] Received 0x%02X\n"), c);
|
||||
|
||||
if (receiving) {
|
||||
@@ -427,7 +327,7 @@ void _rfbLearnImpl() {
|
||||
_learning = true;
|
||||
}
|
||||
|
||||
void _rfbSend(byte * message) {
|
||||
void _rfbSend(uint8_t * message) {
|
||||
|
||||
if (!_rfb_transmit) return;
|
||||
|
||||
@@ -505,6 +405,109 @@ void _rfbReceive() {
|
||||
|
||||
#endif // RFB_DIRECT
|
||||
|
||||
void _rfbSendRaw(const uint8_t *message, const unsigned char n = RF_MESSAGE_SIZE) {
|
||||
for (unsigned char j=0; j<n; j++) {
|
||||
Serial.write(message[j]);
|
||||
}
|
||||
}
|
||||
|
||||
void _rfbSend(uint8_t * code, unsigned char times) {
|
||||
|
||||
if (!_rfb_transmit) return;
|
||||
|
||||
// rc-switch will repeat on its own
|
||||
#if RFB_DIRECT
|
||||
times = 1;
|
||||
#endif
|
||||
|
||||
char buffer[RF_MESSAGE_SIZE];
|
||||
_rfbToChar(code, buffer);
|
||||
DEBUG_MSG_P(PSTR("[RF] Enqueuing MESSAGE '%s' %d time(s)\n"), buffer, times);
|
||||
|
||||
rfb_message_t message;
|
||||
memcpy(message.code, code, RF_MESSAGE_SIZE);
|
||||
message.times = times;
|
||||
_rfb_message_queue.push(message);
|
||||
|
||||
}
|
||||
|
||||
void _rfbSend() {
|
||||
|
||||
if (!_rfb_transmit) return;
|
||||
|
||||
// Check if there is something in the queue
|
||||
if (_rfb_message_queue.empty()) return;
|
||||
|
||||
static unsigned long last = 0;
|
||||
if (millis() - last < RF_SEND_DELAY) return;
|
||||
last = millis();
|
||||
|
||||
// Pop the first message and send it
|
||||
rfb_message_t message = _rfb_message_queue.front();
|
||||
_rfb_message_queue.pop();
|
||||
_rfbSend(message.code);
|
||||
|
||||
// Push it to the stack again if we need to send it more than once
|
||||
if (message.times > 1) {
|
||||
message.times = message.times - 1;
|
||||
_rfb_message_queue.push(message);
|
||||
}
|
||||
|
||||
yield();
|
||||
|
||||
}
|
||||
|
||||
void _rfbSendRawOnce(uint8_t *code, unsigned char length) {
|
||||
char buffer[length*2];
|
||||
_rfbToChar(code, buffer, length);
|
||||
DEBUG_MSG_P(PSTR("[RF] Sending RAW MESSAGE '%s'\n"), buffer);
|
||||
_rfbSendRaw(code, length);
|
||||
}
|
||||
|
||||
bool _rfbCompare(const char * code1, const char * code2) {
|
||||
return strcmp(&code1[12], &code2[12]) == 0;
|
||||
}
|
||||
|
||||
bool _rfbSameOnOff(unsigned char id) {
|
||||
return _rfbCompare(rfbRetrieve(id, true).c_str(), rfbRetrieve(id, false).c_str());
|
||||
}
|
||||
|
||||
void _rfbParseRaw(char * raw) {
|
||||
uint8_t message[RF_MAX_MESSAGE_SIZE];
|
||||
int len = _rfbToArray(raw, message, 0);
|
||||
if (len > 0) {
|
||||
_rfbSendRawOnce(message, len);
|
||||
}
|
||||
}
|
||||
|
||||
void _rfbParseCode(char * code) {
|
||||
|
||||
// The payload may be a code in HEX format ([0-9A-Z]{18}) or
|
||||
// the code comma the number of times to transmit it.
|
||||
char * tok = strtok(code, ",");
|
||||
|
||||
// Check if a switch is linked to that message
|
||||
unsigned char id;
|
||||
unsigned char status = 0;
|
||||
if (_rfbMatch(tok, id, status)) {
|
||||
if (status == 2) {
|
||||
relayToggle(id);
|
||||
} else {
|
||||
relayStatus(id, status == 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t message[RF_MESSAGE_SIZE];
|
||||
int len = _rfbToArray(tok, message, 0);
|
||||
if (len) {
|
||||
tok = strtok(NULL, ",");
|
||||
uint8_t times = (tok != NULL) ? atoi(tok) : 1;
|
||||
_rfbSend(message, times);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if MQTT_SUPPORT
|
||||
|
||||
void _rfbMqttCallback(unsigned int type, const char * topic, const char * payload) {
|
||||
@@ -687,7 +690,7 @@ void rfbStatus(unsigned char id, bool status) {
|
||||
|
||||
bool same = _rfbSameOnOff(id);
|
||||
|
||||
byte message[RF_MAX_MESSAGE_SIZE];
|
||||
uint8_t message[RF_MAX_MESSAGE_SIZE];
|
||||
int len = _rfbToArray(value.c_str(), message, 0);
|
||||
|
||||
if (len == RF_MESSAGE_SIZE && // probably a standard msg
|
||||
@@ -788,13 +791,11 @@ void rfbSetup() {
|
||||
#endif
|
||||
|
||||
// Register loop only when properly configured
|
||||
espurnaRegisterLoop(rfbLoop);
|
||||
espurnaRegisterLoop([]() -> void {
|
||||
_rfbReceive();
|
||||
_rfbSend();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void rfbLoop() {
|
||||
_rfbReceive();
|
||||
_rfbSend();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -6,6 +6,8 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#if RF_SUPPORT
|
||||
|
||||
#if RFB_DIRECT
|
||||
|
||||
@@ -6,10 +6,11 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "rfm69.h"
|
||||
|
||||
#if RFM69_SUPPORT
|
||||
|
||||
#include "mqtt.h"
|
||||
#include "rfm69.h"
|
||||
#include "ws.h"
|
||||
|
||||
#define RFM69_PACKET_SEPARATOR ':'
|
||||
@@ -18,7 +19,15 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
// Locals
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
RFM69Wrap * _rfm69_radio;
|
||||
struct packet_t {
|
||||
unsigned long messageID;
|
||||
unsigned char packetID;
|
||||
unsigned char senderID;
|
||||
unsigned char targetID;
|
||||
char * key;
|
||||
char * value;
|
||||
int16_t rssi;
|
||||
};
|
||||
|
||||
struct _node_t {
|
||||
unsigned long count = 0;
|
||||
@@ -31,6 +40,58 @@ _node_t _rfm69_node_info[RFM69_MAX_NODES];
|
||||
unsigned char _rfm69_node_count;
|
||||
unsigned long _rfm69_packet_count;
|
||||
|
||||
void _rfm69Clear() {
|
||||
for(unsigned int i=0; i<RFM69_MAX_NODES; i++) {
|
||||
_rfm69_node_info[i].duplicates = 0;
|
||||
_rfm69_node_info[i].missing = 0;
|
||||
_rfm69_node_info[i].count = 0;
|
||||
}
|
||||
_rfm69_node_count = 0;
|
||||
_rfm69_packet_count = 0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class RFM69Wrap: public RFM69_ATC {
|
||||
|
||||
public:
|
||||
|
||||
RFM69Wrap(uint8_t slaveSelectPin=RF69_SPI_CS, uint8_t interruptPin=RF69_IRQ_PIN, bool isRFM69HW=false, uint8_t interruptNum=0):
|
||||
RFM69_ATC(slaveSelectPin, interruptPin, isRFM69HW, interruptNum) {};
|
||||
|
||||
protected:
|
||||
|
||||
// overriding SPI_CLOCK for ESP8266
|
||||
void select() {
|
||||
|
||||
noInterrupts();
|
||||
|
||||
#if defined (SPCR) && defined (SPSR)
|
||||
// save current SPI settings
|
||||
_SPCR = SPCR;
|
||||
_SPSR = SPSR;
|
||||
#endif
|
||||
|
||||
// set RFM69 SPI settings
|
||||
SPI.setDataMode(SPI_MODE0);
|
||||
SPI.setBitOrder(MSBFIRST);
|
||||
|
||||
#if defined(__arm__)
|
||||
SPI.setClockDivider(SPI_CLOCK_DIV16);
|
||||
#elif defined(ARDUINO_ARCH_ESP8266)
|
||||
SPI.setClockDivider(SPI_CLOCK_DIV2); // speeding it up for the ESP8266
|
||||
#else
|
||||
SPI.setClockDivider(SPI_CLOCK_DIV4);
|
||||
#endif
|
||||
|
||||
digitalWrite(_slaveSelectPin, LOW);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
RFM69Wrap * _rfm69_radio;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// WEB
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -238,16 +299,6 @@ void _rfm69Loop() {
|
||||
|
||||
}
|
||||
|
||||
void _rfm69Clear() {
|
||||
for(unsigned int i=0; i<RFM69_MAX_NODES; i++) {
|
||||
_rfm69_node_info[i].duplicates = 0;
|
||||
_rfm69_node_info[i].missing = 0;
|
||||
_rfm69_node_info[i].count = 0;
|
||||
}
|
||||
_rfm69_node_count = 0;
|
||||
_rfm69_packet_count = 0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// RFM69
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -8,21 +8,13 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
struct packet_t;
|
||||
#include "espurna.h"
|
||||
|
||||
#if RFM69_SUPPORT
|
||||
|
||||
#include "libs/RFM69Wrap.h"
|
||||
|
||||
struct packet_t {
|
||||
unsigned long messageID;
|
||||
unsigned char packetID;
|
||||
unsigned char senderID;
|
||||
unsigned char targetID;
|
||||
char * key;
|
||||
char * value;
|
||||
int16_t rssi;
|
||||
};
|
||||
#include <RFM69.h>
|
||||
#include <RFM69_ATC.h>
|
||||
#include <SPI.h>
|
||||
|
||||
void rfm69Setup();
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ Part of MQTT and API modules
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
|
||||
@@ -6,11 +6,16 @@ Copyright (C) 2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "rpnrules.h"
|
||||
|
||||
#if RPN_RULES_SUPPORT
|
||||
|
||||
#include "broker.h"
|
||||
#include "mqtt.h"
|
||||
#include "ntp.h"
|
||||
#include "relay.h"
|
||||
#include "rpnrules.h"
|
||||
#include "terminal.h"
|
||||
#include "ws.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Custom commands
|
||||
@@ -132,6 +137,21 @@ bool _rpnNtpFunc(rpn_context & ctxt, int (*func)(time_t)) {
|
||||
|
||||
#endif
|
||||
|
||||
void _rpnDump() {
|
||||
float value;
|
||||
DEBUG_MSG_P(PSTR("[RPN] Stack:\n"));
|
||||
unsigned char num = rpn_stack_size(_rpn_ctxt);
|
||||
if (0 == num) {
|
||||
DEBUG_MSG_P(PSTR(" (empty)\n"));
|
||||
} else {
|
||||
unsigned char index = num - 1;
|
||||
while (rpn_stack_get(_rpn_ctxt, index, value)) {
|
||||
DEBUG_MSG_P(PSTR(" %02d: %s\n"), index--, String(value).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void _rpnInit() {
|
||||
|
||||
// Init context
|
||||
@@ -300,20 +320,6 @@ void _rpnInitCommands() {
|
||||
}
|
||||
#endif
|
||||
|
||||
void _rpnDump() {
|
||||
float value;
|
||||
DEBUG_MSG_P(PSTR("[RPN] Stack:\n"));
|
||||
unsigned char num = rpn_stack_size(_rpn_ctxt);
|
||||
if (0 == num) {
|
||||
DEBUG_MSG_P(PSTR(" (empty)\n"));
|
||||
} else {
|
||||
unsigned char index = num - 1;
|
||||
while (rpn_stack_get(_rpn_ctxt, index, value)) {
|
||||
DEBUG_MSG_P(PSTR(" %02d: %s\n"), index--, String(value).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _rpnRun() {
|
||||
|
||||
unsigned char i = 0;
|
||||
@@ -8,8 +8,7 @@ Copyright (C) 2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
// TODO: need this prototype for .ino
|
||||
struct rpn_context;
|
||||
#include "espurna.h"
|
||||
|
||||
#if RPN_RULES_SUPPORT
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ Copyright (C) 2019 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "rtcmem.h"
|
||||
|
||||
volatile RtcmemData* Rtcmem = reinterpret_cast<volatile RtcmemData*>(RTCMEM_ADDR);
|
||||
|
||||
bool _rtcmem_status = false;
|
||||
|
||||
void _rtcmemErase() {
|
||||
@@ -8,6 +8,11 @@ Copyright (C) 2019 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <cstdint>
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
// Base address of USER RTC memory
|
||||
// https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map#memmory-mapped-io-registers
|
||||
#define RTCMEM_ADDR_BASE (0x60001200)
|
||||
@@ -56,6 +61,7 @@ struct RtcmemData {
|
||||
static_assert(sizeof(RtcmemData) <= (RTCMEM_BLOCKS * 4u), "RTCMEM struct is too big");
|
||||
constexpr uint8_t RtcmemSize = (sizeof(RtcmemData) / 4u);
|
||||
|
||||
auto Rtcmem = reinterpret_cast<volatile RtcmemData*>(RTCMEM_ADDR);
|
||||
extern volatile RtcmemData* Rtcmem;
|
||||
|
||||
bool rtcmemStatus();
|
||||
void rtcmemSetup();
|
||||
|
||||
@@ -7,11 +7,15 @@ Adapted by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "scheduler.h"
|
||||
|
||||
#if SCHEDULER_SUPPORT
|
||||
|
||||
#include "broker.h"
|
||||
#include "relay.h"
|
||||
#include "light.h"
|
||||
#include "ntp.h"
|
||||
#include "relay.h"
|
||||
#include "ws.h"
|
||||
|
||||
constexpr const int SchedulerDummySwitchId = 0xff;
|
||||
|
||||
14
code/espurna/scheduler.h
Normal file
14
code/espurna/scheduler.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
|
||||
SCHEDULER MODULE
|
||||
|
||||
Copyright (C) 2017 by faina09
|
||||
Adapted by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
void schSetup();
|
||||
@@ -6,20 +6,202 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "sensor.h"
|
||||
|
||||
#if SENSOR_SUPPORT
|
||||
|
||||
#include <vector>
|
||||
#include <float.h>
|
||||
|
||||
#include "api.h"
|
||||
#include "broker.h"
|
||||
#include "domoticz.h"
|
||||
#include "i2c.h"
|
||||
#include "mqtt.h"
|
||||
#include "ntp.h"
|
||||
#include "relay.h"
|
||||
#include "sensor.h"
|
||||
#include "terminal.h"
|
||||
#include "thingspeak.h"
|
||||
#include "rtcmem.h"
|
||||
#include "ws.h"
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
// TODO: namespace { ... } ? sensor ctors need to work though
|
||||
|
||||
#include "filters/LastFilter.h"
|
||||
#include "filters/MaxFilter.h"
|
||||
#include "filters/MedianFilter.h"
|
||||
#include "filters/MovingAverageFilter.h"
|
||||
#include "filters/SumFilter.h"
|
||||
|
||||
#include "sensors/BaseSensor.h"
|
||||
#include "sensors/BaseEmonSensor.h"
|
||||
#include "sensors/BaseAnalogSensor.h"
|
||||
|
||||
#if AM2320_SUPPORT
|
||||
#include "sensors/AM2320Sensor.h"
|
||||
#endif
|
||||
|
||||
#if ANALOG_SUPPORT
|
||||
#include "sensors/AnalogSensor.h"
|
||||
#endif
|
||||
|
||||
#if BH1750_SUPPORT
|
||||
#include "sensors/BH1750Sensor.h"
|
||||
#endif
|
||||
|
||||
#if BMP180_SUPPORT
|
||||
#include "sensors/BMP180Sensor.h"
|
||||
#endif
|
||||
|
||||
#if BMX280_SUPPORT
|
||||
#include "sensors/BMX280Sensor.h"
|
||||
#endif
|
||||
|
||||
#if CSE7766_SUPPORT
|
||||
#include "sensors/CSE7766Sensor.h"
|
||||
#endif
|
||||
|
||||
#if DALLAS_SUPPORT
|
||||
#include "sensors/DallasSensor.h"
|
||||
#endif
|
||||
|
||||
#if DHT_SUPPORT
|
||||
#include "sensors/DHTSensor.h"
|
||||
#endif
|
||||
|
||||
#if DIGITAL_SUPPORT
|
||||
#include "sensors/DigitalSensor.h"
|
||||
#endif
|
||||
|
||||
#if ECH1560_SUPPORT
|
||||
#include "sensors/ECH1560Sensor.h"
|
||||
#endif
|
||||
|
||||
#if EMON_ADC121_SUPPORT
|
||||
#include "sensors/EmonADC121Sensor.h"
|
||||
#endif
|
||||
|
||||
#if EMON_ADS1X15_SUPPORT
|
||||
#include "sensors/EmonADS1X15Sensor.h"
|
||||
#endif
|
||||
|
||||
#if EMON_ANALOG_SUPPORT
|
||||
#include "sensors/EmonAnalogSensor.h"
|
||||
#endif
|
||||
|
||||
#if EVENTS_SUPPORT
|
||||
#include "sensors/EventSensor.h"
|
||||
#endif
|
||||
|
||||
#if EZOPH_SUPPORT
|
||||
#include "sensors/EZOPHSensor.h"
|
||||
#endif
|
||||
|
||||
#if GEIGER_SUPPORT
|
||||
#include "sensors/GeigerSensor.h"
|
||||
#endif
|
||||
|
||||
#if GUVAS12SD_SUPPORT
|
||||
#include "sensors/GUVAS12SDSensor.h"
|
||||
#endif
|
||||
|
||||
#if HLW8012_SUPPORT
|
||||
#include "sensors/HLW8012Sensor.h"
|
||||
#endif
|
||||
|
||||
#if LDR_SUPPORT
|
||||
#include "sensors/LDRSensor.h"
|
||||
#endif
|
||||
|
||||
#if MAX6675_SUPPORT
|
||||
#include "sensors/MAX6675Sensor.h"
|
||||
#endif
|
||||
|
||||
#if MICS2710_SUPPORT
|
||||
#include "sensors/MICS2710Sensor.h"
|
||||
#endif
|
||||
|
||||
#if MICS5525_SUPPORT
|
||||
#include "sensors/MICS5525Sensor.h"
|
||||
#endif
|
||||
|
||||
#if MHZ19_SUPPORT
|
||||
#include "sensors/MHZ19Sensor.h"
|
||||
#endif
|
||||
|
||||
#if NTC_SUPPORT
|
||||
#include "sensors/NTCSensor.h"
|
||||
#endif
|
||||
|
||||
#if SDS011_SUPPORT
|
||||
#include "sensors/SDS011Sensor.h"
|
||||
#endif
|
||||
|
||||
#if SENSEAIR_SUPPORT
|
||||
#include "sensors/SenseAirSensor.h"
|
||||
#endif
|
||||
|
||||
#if PMSX003_SUPPORT
|
||||
#include "sensors/PMSX003Sensor.h"
|
||||
#endif
|
||||
|
||||
#if PULSEMETER_SUPPORT
|
||||
#include "sensors/PulseMeterSensor.h"
|
||||
#endif
|
||||
|
||||
#if PZEM004T_SUPPORT
|
||||
#include "sensors/PZEM004TSensor.h"
|
||||
#endif
|
||||
|
||||
#if SHT3X_I2C_SUPPORT
|
||||
#include "sensors/SHT3XI2CSensor.h"
|
||||
#endif
|
||||
|
||||
#if SI7021_SUPPORT
|
||||
#include "sensors/SI7021Sensor.h"
|
||||
#endif
|
||||
|
||||
#if SONAR_SUPPORT
|
||||
#include "sensors/SonarSensor.h"
|
||||
#endif
|
||||
|
||||
#if T6613_SUPPORT
|
||||
#include "sensors/T6613Sensor.h"
|
||||
#endif
|
||||
|
||||
#if TMP3X_SUPPORT
|
||||
#include "sensors/TMP3XSensor.h"
|
||||
#endif
|
||||
|
||||
#if V9261F_SUPPORT
|
||||
#include "sensors/V9261FSensor.h"
|
||||
#endif
|
||||
|
||||
#if VEML6075_SUPPORT
|
||||
#include "sensors/VEML6075Sensor.h"
|
||||
#endif
|
||||
|
||||
#if VL53L1X_SUPPORT
|
||||
#include "sensors/VL53L1XSensor.h"
|
||||
#endif
|
||||
|
||||
#if ADE7953_SUPPORT
|
||||
#include "sensors/ADE7953Sensor.h"
|
||||
#endif
|
||||
|
||||
#if SI1145_SUPPORT
|
||||
#include "sensors/SI1145Sensor.h"
|
||||
#endif
|
||||
|
||||
#if HDC1080_SUPPORT
|
||||
#include "sensors/HDC1080Sensor.h"
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
struct sensor_magnitude_t {
|
||||
|
||||
private:
|
||||
@@ -149,10 +331,108 @@ void Energy::reset() {
|
||||
|
||||
} // namespace sensor
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Energy persistence
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
std::vector<unsigned char> _sensor_save_count;
|
||||
unsigned char _sensor_save_every = SENSOR_SAVE_EVERY;
|
||||
|
||||
bool _sensorIsEmon(BaseSensor* sensor) {
|
||||
return sensor->type() & sensor::type::Emon;
|
||||
}
|
||||
|
||||
sensor::Energy _sensorRtcmemLoadEnergy(unsigned char index) {
|
||||
return sensor::Energy {
|
||||
sensor::KWh { Rtcmem->energy[index].kwh },
|
||||
sensor::Ws { Rtcmem->energy[index].ws }
|
||||
};
|
||||
}
|
||||
|
||||
void _sensorRtcmemSaveEnergy(unsigned char index, const sensor::Energy& source) {
|
||||
Rtcmem->energy[index].kwh = source.kwh.value;
|
||||
Rtcmem->energy[index].ws = source.ws.value;
|
||||
}
|
||||
|
||||
sensor::Energy _sensorParseEnergy(const String& value) {
|
||||
sensor::Energy result;
|
||||
|
||||
const bool separator = value.indexOf('+') > 0;
|
||||
if (value.length() && (separator > 0)) {
|
||||
const String before = value.substring(0, separator);
|
||||
const String after = value.substring(separator + 1);
|
||||
result.kwh = strtoul(before.c_str(), nullptr, 10);
|
||||
result.ws = strtoul(after.c_str(), nullptr, 10);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void _sensorApiResetEnergy(const sensor_magnitude_t& magnitude, const char* payload) {
|
||||
if (!payload || !strlen(payload)) return;
|
||||
if (payload[0] != '0') return;
|
||||
|
||||
auto* sensor = static_cast<BaseEmonSensor*>(magnitude.sensor);
|
||||
auto energy = _sensorParseEnergy(payload);
|
||||
|
||||
sensor->resetEnergy(magnitude.global, energy);
|
||||
}
|
||||
|
||||
sensor::Energy _sensorEnergyTotal(unsigned char index) {
|
||||
|
||||
sensor::Energy result;
|
||||
|
||||
if (rtcmemStatus() && (index < (sizeof(Rtcmem->energy) / sizeof(*Rtcmem->energy)))) {
|
||||
result = _sensorRtcmemLoadEnergy(index);
|
||||
} else if (_sensor_save_every > 0) {
|
||||
result = _sensorParseEnergy(getSetting({"eneTotal", index}));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
sensor::Energy sensorEnergyTotal() {
|
||||
return _sensorEnergyTotal(0);
|
||||
}
|
||||
|
||||
void _sensorResetEnergyTotal(unsigned char index) {
|
||||
delSetting({"eneTotal", index});
|
||||
delSetting({"eneTime", index});
|
||||
if (index < (sizeof(Rtcmem->energy) / sizeof(*Rtcmem->energy))) {
|
||||
Rtcmem->energy[index].kwh = 0;
|
||||
Rtcmem->energy[index].ws = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void _magnitudeSaveEnergyTotal(sensor_magnitude_t& magnitude, bool persistent) {
|
||||
if (magnitude.type != MAGNITUDE_ENERGY) return;
|
||||
|
||||
auto* sensor = static_cast<BaseEmonSensor*>(magnitude.sensor);
|
||||
|
||||
const auto energy = sensor->totalEnergy();
|
||||
|
||||
// Always save to RTCMEM
|
||||
if (magnitude.global < (sizeof(Rtcmem->energy) / sizeof(*Rtcmem->energy))) {
|
||||
_sensorRtcmemSaveEnergy(magnitude.global, energy);
|
||||
}
|
||||
|
||||
// Save to EEPROM every '_sensor_save_every' readings
|
||||
// Format is `<kwh>+<ws>`, value without `+` is treated as `<ws>`
|
||||
if (persistent && _sensor_save_every) {
|
||||
_sensor_save_count[magnitude.global] =
|
||||
(_sensor_save_count[magnitude.global] + 1) % _sensor_save_every;
|
||||
|
||||
if (0 == _sensor_save_count[magnitude.global]) {
|
||||
const String total = String(energy.kwh.value) + "+" + String(energy.ws.value);
|
||||
setSetting({"eneTotal", magnitude.global}, total);
|
||||
#if NTP_SUPPORT
|
||||
if (ntpSynced()) setSetting({"eneTime", magnitude.global}, ntpDateTime());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
std::vector<BaseSensor *> _sensors;
|
||||
@@ -163,10 +443,6 @@ bool _sensor_realtime = API_REAL_TIME_VALUES;
|
||||
unsigned long _sensor_read_interval = 1000 * SENSOR_READ_INTERVAL;
|
||||
unsigned char _sensor_report_every = SENSOR_REPORT_EVERY;
|
||||
|
||||
// Energy persistence
|
||||
std::vector<unsigned char> _sensor_save_count;
|
||||
unsigned char _sensor_save_every = SENSOR_SAVE_EVERY;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Private
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -369,11 +645,11 @@ String magnitudeTopic(unsigned char type) {
|
||||
|
||||
}
|
||||
|
||||
String magnitudeTopic(const sensor_magnitude_t& magnitude) {
|
||||
String _magnitudeTopic(const sensor_magnitude_t& magnitude) {
|
||||
return magnitudeTopic(magnitude.type);
|
||||
}
|
||||
|
||||
String magnitudeUnits(const sensor_magnitude_t& magnitude) {
|
||||
String _magnitudeUnits(const sensor_magnitude_t& magnitude) {
|
||||
|
||||
const __FlashStringHelper* result = nullptr;
|
||||
|
||||
@@ -457,7 +733,7 @@ String magnitudeUnits(const sensor_magnitude_t& magnitude) {
|
||||
|
||||
String magnitudeUnits(unsigned char index) {
|
||||
if (index >= magnitudeCount()) return String();
|
||||
return magnitudeUnits(_magnitudes[index]);
|
||||
return _magnitudeUnits(_magnitudes[index]);
|
||||
}
|
||||
|
||||
// Choose unit based on type of magnitude we use
|
||||
@@ -548,11 +824,12 @@ double _magnitudeProcess(const sensor_magnitude_t& magnitude, double value) {
|
||||
|
||||
#if WEB_SUPPORT
|
||||
|
||||
//void _sensorWebSocketMagnitudes(JsonObject& root, const String& ws_name, const String& conf_name) {
|
||||
template<typename T> void _sensorWebSocketMagnitudes(JsonObject& root, T prefix) {
|
||||
// Used by modules to generate magnitude_id<->module_id mapping for the WebUI
|
||||
|
||||
void sensorWebSocketMagnitudes(JsonObject& root, const String& prefix) {
|
||||
|
||||
// ws produces flat list <prefix>Magnitudes
|
||||
const String ws_name = String(prefix) + "Magnitudes";
|
||||
const String ws_name = prefix + "Magnitudes";
|
||||
|
||||
// config uses <prefix>Magnitude<index> (cut 's')
|
||||
const String conf_name = ws_name.substring(0, ws_name.length() - 1);
|
||||
@@ -560,33 +837,17 @@ template<typename T> void _sensorWebSocketMagnitudes(JsonObject& root, T prefix)
|
||||
JsonObject& list = root.createNestedObject(ws_name);
|
||||
list["size"] = magnitudeCount();
|
||||
|
||||
//JsonArray& name = list.createNestedArray("name");
|
||||
JsonArray& type = list.createNestedArray("type");
|
||||
JsonArray& index = list.createNestedArray("index");
|
||||
JsonArray& idx = list.createNestedArray("idx");
|
||||
|
||||
for (unsigned char i=0; i<magnitudeCount(); ++i) {
|
||||
//name.add(magnitudeName(i));
|
||||
type.add(magnitudeType(i));
|
||||
index.add(magnitudeIndex(i));
|
||||
idx.add(getSetting({conf_name, i}, 0));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
template<typename T> void _sensorWebSocketMagnitudes(JsonObject& root, T prefix) {
|
||||
|
||||
// ws produces flat list <prefix>Magnitudes
|
||||
const String ws_name = String(prefix) + "Magnitudes";
|
||||
|
||||
// config uses <prefix>Magnitude<index> (cut 's')
|
||||
const String conf_name = ws_name.substring(0, ws_name.length() - 1);
|
||||
|
||||
_sensorWebSocketMagnitudes(root, ws_name, conf_name);
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
bool _sensorWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
|
||||
if (strncmp(key, "pwr", 3) == 0) return true;
|
||||
if (strncmp(key, "sns", 3) == 0) return true;
|
||||
@@ -629,7 +890,7 @@ void _sensorWebSocketMagnitudesConfig(JsonObject& root) {
|
||||
|
||||
index.add<uint8_t>(magnitude.global);
|
||||
type.add<uint8_t>(magnitude.type);
|
||||
units.add(magnitudeUnits(magnitude));
|
||||
units.add(_magnitudeUnits(magnitude));
|
||||
|
||||
{
|
||||
String sensor_desc = magnitude.sensor->slot(magnitude.local);
|
||||
@@ -873,98 +1134,6 @@ void _sensorPost() {
|
||||
}
|
||||
}
|
||||
|
||||
sensor::Energy _sensorRtcmemLoadEnergy(unsigned char index) {
|
||||
return sensor::Energy {
|
||||
sensor::KWh { Rtcmem->energy[index].kwh },
|
||||
sensor::Ws { Rtcmem->energy[index].ws }
|
||||
};
|
||||
}
|
||||
|
||||
void _sensorRtcmemSaveEnergy(unsigned char index, const sensor::Energy& source) {
|
||||
Rtcmem->energy[index].kwh = source.kwh.value;
|
||||
Rtcmem->energy[index].ws = source.ws.value;
|
||||
}
|
||||
|
||||
sensor::Energy _sensorParseEnergy(const String& value) {
|
||||
sensor::Energy result;
|
||||
|
||||
const bool separator = value.indexOf('+') > 0;
|
||||
if (value.length() && (separator > 0)) {
|
||||
const String before = value.substring(0, separator);
|
||||
const String after = value.substring(separator + 1);
|
||||
result.kwh = strtoul(before.c_str(), nullptr, 10);
|
||||
result.ws = strtoul(after.c_str(), nullptr, 10);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void _sensorApiResetEnergy(const sensor_magnitude_t& magnitude, const char* payload) {
|
||||
if (!payload || !strlen(payload)) return;
|
||||
if (payload[0] != '0') return;
|
||||
|
||||
auto* sensor = static_cast<BaseEmonSensor*>(magnitude.sensor);
|
||||
auto energy = _sensorParseEnergy(payload);
|
||||
|
||||
sensor->resetEnergy(magnitude.global, energy);
|
||||
}
|
||||
|
||||
sensor::Energy _sensorEnergyTotal(unsigned char index) {
|
||||
|
||||
sensor::Energy result;
|
||||
|
||||
if (rtcmemStatus() && (index < (sizeof(Rtcmem->energy) / sizeof(*Rtcmem->energy)))) {
|
||||
result = _sensorRtcmemLoadEnergy(index);
|
||||
} else if (_sensor_save_every > 0) {
|
||||
result = _sensorParseEnergy(getSetting({"eneTotal", index}));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
sensor::Energy sensorEnergyTotal() {
|
||||
return _sensorEnergyTotal(0);
|
||||
}
|
||||
|
||||
void _sensorResetEnergyTotal(unsigned char index) {
|
||||
delSetting({"eneTotal", index});
|
||||
delSetting({"eneTime", index});
|
||||
if (index < (sizeof(Rtcmem->energy) / sizeof(*Rtcmem->energy))) {
|
||||
Rtcmem->energy[index].kwh = 0;
|
||||
Rtcmem->energy[index].ws = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void _magnitudeSaveEnergyTotal(sensor_magnitude_t& magnitude, bool persistent) {
|
||||
if (magnitude.type != MAGNITUDE_ENERGY) return;
|
||||
|
||||
auto* sensor = static_cast<BaseEmonSensor*>(magnitude.sensor);
|
||||
|
||||
const auto energy = sensor->totalEnergy();
|
||||
|
||||
// Always save to RTCMEM
|
||||
if (magnitude.global < (sizeof(Rtcmem->energy) / sizeof(*Rtcmem->energy))) {
|
||||
_sensorRtcmemSaveEnergy(magnitude.global, energy);
|
||||
}
|
||||
|
||||
// Save to EEPROM every '_sensor_save_every' readings
|
||||
// Format is `<kwh>+<ws>`, value without `+` is treated as `<ws>`
|
||||
if (persistent && _sensor_save_every) {
|
||||
_sensor_save_count[magnitude.global] =
|
||||
(_sensor_save_count[magnitude.global] + 1) % _sensor_save_every;
|
||||
|
||||
if (0 == _sensor_save_count[magnitude.global]) {
|
||||
const String total = String(energy.kwh.value) + "+" + String(energy.ws.value);
|
||||
setSetting({"eneTotal", magnitude.global}, total);
|
||||
#if NTP_SUPPORT
|
||||
if (ntpSynced()) setSetting({"eneTime", magnitude.global}, ntpDateTime());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Sensor initialization
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -1565,6 +1734,47 @@ void _sensorLoad() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void _sensorReport(unsigned char index, double value) {
|
||||
|
||||
const auto& magnitude = _magnitudes.at(index);
|
||||
|
||||
// XXX: ensure that the received 'value' will fit here
|
||||
// dtostrf 2nd arg only controls leading zeroes and the
|
||||
// 3rd is only for the part after the dot
|
||||
char buffer[64];
|
||||
dtostrf(value, 1, magnitude.decimals, buffer);
|
||||
|
||||
#if BROKER_SUPPORT
|
||||
SensorReportBroker::Publish(magnitudeTopic(magnitude.type), magnitude.global, value, buffer);
|
||||
#endif
|
||||
|
||||
#if MQTT_SUPPORT
|
||||
|
||||
mqttSend(magnitudeTopicIndex(index).c_str(), buffer);
|
||||
|
||||
#if SENSOR_PUBLISH_ADDRESSES
|
||||
char topic[32];
|
||||
snprintf(topic, sizeof(topic), "%s/%s", SENSOR_ADDRESS_TOPIC, magnitudeTopic(magnitude.type).c_str());
|
||||
if (SENSOR_USE_INDEX || (sensor_magnitude_t::counts(magnitude.type) > 1)) {
|
||||
mqttSend(topic, magnitude.global, magnitude.sensor->address(magnitude.local).c_str());
|
||||
} else {
|
||||
mqttSend(topic, magnitude.sensor->address(magnitude.local).c_str());
|
||||
}
|
||||
#endif // SENSOR_PUBLISH_ADDRESSES
|
||||
|
||||
#endif // MQTT_SUPPORT
|
||||
|
||||
#if THINGSPEAK_SUPPORT
|
||||
tspkEnqueueMeasurement(index, buffer);
|
||||
#endif // THINGSPEAK_SUPPORT
|
||||
|
||||
#if DOMOTICZ_SUPPORT
|
||||
domoticzSendMagnitude(magnitude.type, index, value, buffer);
|
||||
#endif // DOMOTICZ_SUPPORT
|
||||
|
||||
}
|
||||
|
||||
|
||||
void _sensorCallback(unsigned char i, unsigned char type, double value) {
|
||||
|
||||
DEBUG_MSG_P(PSTR("[SENSOR] Sensor #%u callback, type %u, payload: '%s'\n"), i, type, String(value).c_str());
|
||||
@@ -1864,46 +2074,6 @@ void _sensorConfigure() {
|
||||
|
||||
}
|
||||
|
||||
void _sensorReport(unsigned char index, double value) {
|
||||
|
||||
const auto& magnitude = _magnitudes.at(index);
|
||||
|
||||
// XXX: ensure that the received 'value' will fit here
|
||||
// dtostrf 2nd arg only controls leading zeroes and the
|
||||
// 3rd is only for the part after the dot
|
||||
char buffer[64];
|
||||
dtostrf(value, 1, magnitude.decimals, buffer);
|
||||
|
||||
#if BROKER_SUPPORT
|
||||
SensorReportBroker::Publish(magnitudeTopic(magnitude.type), magnitude.global, value, buffer);
|
||||
#endif
|
||||
|
||||
#if MQTT_SUPPORT
|
||||
|
||||
mqttSend(magnitudeTopicIndex(index).c_str(), buffer);
|
||||
|
||||
#if SENSOR_PUBLISH_ADDRESSES
|
||||
char topic[32];
|
||||
snprintf(topic, sizeof(topic), "%s/%s", SENSOR_ADDRESS_TOPIC, magnitudeTopic(magnitude.type).c_str());
|
||||
if (SENSOR_USE_INDEX || (sensor_magnitude_t::counts(magnitude.type) > 1)) {
|
||||
mqttSend(topic, magnitude.global, magnitude.sensor->address(magnitude.local).c_str());
|
||||
} else {
|
||||
mqttSend(topic, magnitude.sensor->address(magnitude.local).c_str());
|
||||
}
|
||||
#endif // SENSOR_PUBLISH_ADDRESSES
|
||||
|
||||
#endif // MQTT_SUPPORT
|
||||
|
||||
#if THINGSPEAK_SUPPORT
|
||||
tspkEnqueueMeasurement(index, buffer);
|
||||
#endif // THINGSPEAK_SUPPORT
|
||||
|
||||
#if DOMOTICZ_SUPPORT
|
||||
domoticzSendMagnitude(magnitude.type, index, value, buffer);
|
||||
#endif // DOMOTICZ_SUPPORT
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Public
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -2139,7 +2309,7 @@ void sensorLoop() {
|
||||
magnitude.sensor->slot(magnitude.local).c_str(),
|
||||
magnitudeTopic(magnitude.type).c_str(),
|
||||
buffer,
|
||||
magnitudeUnits(magnitude).c_str()
|
||||
_magnitudeUnits(magnitude).c_str()
|
||||
);
|
||||
}
|
||||
#endif // SENSOR_DEBUG
|
||||
@@ -9,7 +9,7 @@ Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
struct sensor_magnitude_t;
|
||||
#include "espurna.h"
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
@@ -133,9 +133,6 @@ unsigned char magnitudeType(unsigned char index);
|
||||
// consider using index instead of type or adding stronger param type
|
||||
String magnitudeTopic(unsigned char type);
|
||||
|
||||
String magnitudeTopic(const sensor_magnitude_t& magnitude);
|
||||
String magnitudeUnits(const sensor_magnitude_t& magnitude);
|
||||
|
||||
unsigned char sensorCount();
|
||||
unsigned char magnitudeCount();
|
||||
|
||||
@@ -144,179 +141,8 @@ double magnitudeValue(unsigned char index);
|
||||
unsigned char magnitudeIndex(unsigned char index);
|
||||
String magnitudeTopicIndex(unsigned char index);
|
||||
|
||||
void sensorWebSocketMagnitudes(JsonObject& root, const String& prefix);
|
||||
|
||||
void sensorSetup();
|
||||
void sensorLoop();
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
#include "filters/LastFilter.h"
|
||||
#include "filters/MaxFilter.h"
|
||||
#include "filters/MedianFilter.h"
|
||||
#include "filters/MovingAverageFilter.h"
|
||||
#include "filters/SumFilter.h"
|
||||
|
||||
#include "sensors/BaseSensor.h"
|
||||
#include "sensors/BaseEmonSensor.h"
|
||||
#include "sensors/BaseAnalogSensor.h"
|
||||
|
||||
#if AM2320_SUPPORT
|
||||
#include "sensors/AM2320Sensor.h"
|
||||
#endif
|
||||
|
||||
#if ANALOG_SUPPORT
|
||||
#include "sensors/AnalogSensor.h"
|
||||
#endif
|
||||
|
||||
#if BH1750_SUPPORT
|
||||
#include "sensors/BH1750Sensor.h"
|
||||
#endif
|
||||
|
||||
#if BMP180_SUPPORT
|
||||
#include "sensors/BMP180Sensor.h"
|
||||
#endif
|
||||
|
||||
#if BMX280_SUPPORT
|
||||
#include "sensors/BMX280Sensor.h"
|
||||
#endif
|
||||
|
||||
#if CSE7766_SUPPORT
|
||||
#include "sensors/CSE7766Sensor.h"
|
||||
#endif
|
||||
|
||||
#if DALLAS_SUPPORT
|
||||
#include "sensors/DallasSensor.h"
|
||||
#endif
|
||||
|
||||
#if DHT_SUPPORT
|
||||
#include "sensors/DHTSensor.h"
|
||||
#endif
|
||||
|
||||
#if DIGITAL_SUPPORT
|
||||
#include "sensors/DigitalSensor.h"
|
||||
#endif
|
||||
|
||||
#if ECH1560_SUPPORT
|
||||
#include "sensors/ECH1560Sensor.h"
|
||||
#endif
|
||||
|
||||
#if EMON_ADC121_SUPPORT
|
||||
#include "sensors/EmonADC121Sensor.h"
|
||||
#endif
|
||||
|
||||
#if EMON_ADS1X15_SUPPORT
|
||||
#include "sensors/EmonADS1X15Sensor.h"
|
||||
#endif
|
||||
|
||||
#if EMON_ANALOG_SUPPORT
|
||||
#include "sensors/EmonAnalogSensor.h"
|
||||
#endif
|
||||
|
||||
#if EVENTS_SUPPORT
|
||||
#include "sensors/EventSensor.h"
|
||||
#endif
|
||||
|
||||
#if EZOPH_SUPPORT
|
||||
#include "sensors/EZOPHSensor.h"
|
||||
#endif
|
||||
|
||||
#if GEIGER_SUPPORT
|
||||
#include "sensors/GeigerSensor.h"
|
||||
#endif
|
||||
|
||||
#if GUVAS12SD_SUPPORT
|
||||
#include "sensors/GUVAS12SDSensor.h"
|
||||
#endif
|
||||
|
||||
#if HLW8012_SUPPORT
|
||||
#include "sensors/HLW8012Sensor.h"
|
||||
#endif
|
||||
|
||||
#if LDR_SUPPORT
|
||||
#include "sensors/LDRSensor.h"
|
||||
#endif
|
||||
|
||||
#if MAX6675_SUPPORT
|
||||
#include "sensors/MAX6675Sensor.h"
|
||||
#endif
|
||||
|
||||
#if MICS2710_SUPPORT
|
||||
#include "sensors/MICS2710Sensor.h"
|
||||
#endif
|
||||
|
||||
#if MICS5525_SUPPORT
|
||||
#include "sensors/MICS5525Sensor.h"
|
||||
#endif
|
||||
|
||||
#if MHZ19_SUPPORT
|
||||
#include "sensors/MHZ19Sensor.h"
|
||||
#endif
|
||||
|
||||
#if NTC_SUPPORT
|
||||
#include "sensors/NTCSensor.h"
|
||||
#endif
|
||||
|
||||
#if SDS011_SUPPORT
|
||||
#include "sensors/SDS011Sensor.h"
|
||||
#endif
|
||||
|
||||
#if SENSEAIR_SUPPORT
|
||||
#include "sensors/SenseAirSensor.h"
|
||||
#endif
|
||||
|
||||
#if PMSX003_SUPPORT
|
||||
#include "sensors/PMSX003Sensor.h"
|
||||
#endif
|
||||
|
||||
#if PULSEMETER_SUPPORT
|
||||
#include "sensors/PulseMeterSensor.h"
|
||||
#endif
|
||||
|
||||
#if PZEM004T_SUPPORT
|
||||
#include "sensors/PZEM004TSensor.h"
|
||||
#endif
|
||||
|
||||
#if SHT3X_I2C_SUPPORT
|
||||
#include "sensors/SHT3XI2CSensor.h"
|
||||
#endif
|
||||
|
||||
#if SI7021_SUPPORT
|
||||
#include "sensors/SI7021Sensor.h"
|
||||
#endif
|
||||
|
||||
#if SONAR_SUPPORT
|
||||
#include "sensors/SonarSensor.h"
|
||||
#endif
|
||||
|
||||
#if T6613_SUPPORT
|
||||
#include "sensors/T6613Sensor.h"
|
||||
#endif
|
||||
|
||||
#if TMP3X_SUPPORT
|
||||
#include "sensors/TMP3XSensor.h"
|
||||
#endif
|
||||
|
||||
#if V9261F_SUPPORT
|
||||
#include "sensors/V9261FSensor.h"
|
||||
#endif
|
||||
|
||||
#if VEML6075_SUPPORT
|
||||
#include "sensors/VEML6075Sensor.h"
|
||||
#endif
|
||||
|
||||
#if VL53L1X_SUPPORT
|
||||
#include "sensors/VL53L1XSensor.h"
|
||||
#endif
|
||||
|
||||
#if ADE7953_SUPPORT
|
||||
#include "sensors/ADE7953Sensor.h"
|
||||
#endif
|
||||
|
||||
#if SI1145_SUPPORT
|
||||
#include "sensors/SI1145Sensor.h"
|
||||
#endif
|
||||
|
||||
#if HDC1080_SUPPORT
|
||||
#include "sensors/HDC1080Sensor.h"
|
||||
#endif
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "BaseSensor.h"
|
||||
#include "../i2c.h"
|
||||
|
||||
// TODO: Must inherit from I2CSensor<>, not just I2CSensor. Even with default value :(
|
||||
// Perhaps I2CSensor should be alias for I2CSensorBase?
|
||||
|
||||
@@ -6,17 +6,15 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include "storage_eeprom.h"
|
||||
|
||||
#include "settings_internal.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Reverse engineering EEPROM storage format
|
||||
// (HACK) Embedis storage format, reverse engineered
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
unsigned long settingsSize() {
|
||||
@@ -28,9 +26,94 @@ unsigned long settingsSize() {
|
||||
return SPI_FLASH_SEC_SIZE - pos + EEPROM_DATA_END;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
namespace settings {
|
||||
namespace internal {
|
||||
|
||||
uint32_t u32fromString(const String& string, int base) {
|
||||
|
||||
const char *ptr = string.c_str();
|
||||
char *value_endptr = nullptr;
|
||||
|
||||
// invalidate the whole string when invalid chars are detected
|
||||
const auto value = strtoul(ptr, &value_endptr, base);
|
||||
if (value_endptr == ptr || value_endptr[0] != '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
template <>
|
||||
float convert(const String& value) {
|
||||
return atof(value.c_str());
|
||||
}
|
||||
|
||||
template <>
|
||||
double convert(const String& value) {
|
||||
return atof(value.c_str());
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert(const String& value) {
|
||||
return value.toInt();
|
||||
}
|
||||
|
||||
template <>
|
||||
long convert(const String& value) {
|
||||
return value.toInt();
|
||||
}
|
||||
|
||||
template <>
|
||||
bool convert(const String& value) {
|
||||
return convert<int>(value) == 1;
|
||||
}
|
||||
|
||||
template <>
|
||||
unsigned long convert(const String& value) {
|
||||
if (!value.length()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int base = 10;
|
||||
if (value.length() > 2) {
|
||||
if (value.startsWith("0b")) {
|
||||
base = 2;
|
||||
} else if (value.startsWith("0o")) {
|
||||
base = 8;
|
||||
} else if (value.startsWith("0x")) {
|
||||
base = 16;
|
||||
}
|
||||
}
|
||||
|
||||
return u32fromString((base == 10) ? value : value.substring(2), base);
|
||||
}
|
||||
|
||||
template <>
|
||||
unsigned int convert(const String& value) {
|
||||
return convert<unsigned long>(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
unsigned short convert(const String& value) {
|
||||
return convert<unsigned long>(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
unsigned char convert(const String& value) {
|
||||
return convert<unsigned long>(value);
|
||||
}
|
||||
|
||||
} // namespace settings::internal
|
||||
} // namespace settings
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
unsigned int settingsKeyCount() {
|
||||
size_t settingsKeyCount() {
|
||||
unsigned count = 0;
|
||||
unsigned pos = SPI_FLASH_SEC_SIZE - 1;
|
||||
while (size_t len = EEPROMr.read(pos)) {
|
||||
@@ -68,13 +151,72 @@ String settingsKeyName(unsigned int index) {
|
||||
|
||||
}
|
||||
|
||||
std::vector<String> _settingsKeys() {
|
||||
/*
|
||||
struct SettingsKeys {
|
||||
|
||||
struct iterator {
|
||||
iterator(size_t total) :
|
||||
total(total)
|
||||
{}
|
||||
|
||||
iterator& operator++() {
|
||||
if (total && (current_index < (total - 1))) {
|
||||
++current_index
|
||||
current_value = settingsKeyName(current_index);
|
||||
return *this;
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
iterator operator++(int) {
|
||||
iterator val = *this;
|
||||
++(*this);
|
||||
return val;
|
||||
}
|
||||
|
||||
operator String() {
|
||||
return (current_index < total) ? current_value : empty_value;
|
||||
}
|
||||
|
||||
bool operator ==(iterator& const other) const {
|
||||
return (total == other.total) && (current_index == other.current_index);
|
||||
}
|
||||
|
||||
bool operator !=(iterator& const other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
using difference_type = size_t;
|
||||
using value_type = size_t;
|
||||
using pointer = const size_t*;
|
||||
using reference = const size_t&;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
const size_t total;
|
||||
|
||||
String empty_value;
|
||||
String current_value;
|
||||
size_t current_index = 0;
|
||||
};
|
||||
|
||||
iterator begin() {
|
||||
return iterator {total};
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return iterator {0};
|
||||
}
|
||||
|
||||
};
|
||||
*/
|
||||
|
||||
std::vector<String> settingsKeys() {
|
||||
|
||||
// Get sorted list of keys
|
||||
std::vector<String> keys;
|
||||
|
||||
//unsigned int size = settingsKeyCount();
|
||||
unsigned int size = settingsKeyCount();
|
||||
auto size = settingsKeyCount();
|
||||
for (unsigned int i=0; i<size; i++) {
|
||||
|
||||
//String key = settingsKeyName(i);
|
||||
@@ -141,6 +283,7 @@ void moveSettings(const String& from, const String& to) {
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
template<typename R, settings::internal::convert_t<R> Rfunc = settings::internal::convert>
|
||||
R getSetting(const settings_key_t& key, R defaultValue) {
|
||||
String value;
|
||||
@@ -149,6 +292,7 @@ R getSetting(const settings_key_t& key, R defaultValue) {
|
||||
}
|
||||
return Rfunc(value);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<>
|
||||
String getSetting(const settings_key_t& key, String defaultValue) {
|
||||
@@ -159,6 +303,33 @@ String getSetting(const settings_key_t& key, String defaultValue) {
|
||||
return value;
|
||||
}
|
||||
|
||||
template
|
||||
bool getSetting(const settings_key_t& key, bool defaultValue);
|
||||
|
||||
template
|
||||
int getSetting(const settings_key_t& key, int defaultValue);
|
||||
|
||||
template
|
||||
long getSetting(const settings_key_t& key, long defaultValue);
|
||||
|
||||
template
|
||||
unsigned char getSetting(const settings_key_t& key, unsigned char defaultValue);
|
||||
|
||||
template
|
||||
unsigned short getSetting(const settings_key_t& key, unsigned short defaultValue);
|
||||
|
||||
template
|
||||
unsigned int getSetting(const settings_key_t& key, unsigned int defaultValue);
|
||||
|
||||
template
|
||||
unsigned long getSetting(const settings_key_t& key, unsigned long defaultValue);
|
||||
|
||||
template
|
||||
float getSetting(const settings_key_t& key, float defaultValue);
|
||||
|
||||
template
|
||||
double getSetting(const settings_key_t& key, double defaultValue);
|
||||
|
||||
String getSetting(const settings_key_t& key) {
|
||||
static const String defaultValue("");
|
||||
return getSetting(key, defaultValue);
|
||||
@@ -172,9 +343,9 @@ String getSetting(const settings_key_t& key, const __FlashStringHelper* defaultV
|
||||
return getSetting(key, String(defaultValue));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool setSetting(const settings_key_t& key, const T& value) {
|
||||
return Embedis::set(key.toString(), String(value));
|
||||
template<>
|
||||
bool setSetting(const settings_key_t& key, const String& value) {
|
||||
return Embedis::set(key.toString(), value);
|
||||
}
|
||||
|
||||
bool delSetting(const settings_key_t& key) {
|
||||
@@ -199,30 +370,6 @@ void resetSettings() {
|
||||
EEPROMr.commit();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Deprecated implementation
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template<typename T>
|
||||
String getSetting(const String& key, unsigned char index, T defaultValue) {
|
||||
return getSetting({key, index}, defaultValue);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool setSetting(const String& key, unsigned char index, T value) {
|
||||
return setSetting({key, index}, value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool hasSetting(const String& key, unsigned char index) {
|
||||
return hasSetting({key, index});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool delSetting(const String& key, unsigned char index) {
|
||||
return delSetting({key, index});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// API
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -267,7 +414,7 @@ bool settingsRestoreJson(JsonObject& data) {
|
||||
|
||||
}
|
||||
|
||||
bool settingsRestoreJson(char* json_string, size_t json_buffer_size = 1024) {
|
||||
bool settingsRestoreJson(char* json_string, size_t json_buffer_size) {
|
||||
|
||||
// XXX: as of right now, arduinojson cannot trigger callbacks for each key individually
|
||||
// Manually separating kv pairs can allow to parse only a small chunk, since we know that there is only string type used (even with bools / ints). Can be problematic when parsing data that was not generated by us.
|
||||
@@ -287,7 +434,7 @@ bool settingsRestoreJson(char* json_string, size_t json_buffer_size = 1024) {
|
||||
void settingsGetJson(JsonObject& root) {
|
||||
|
||||
// Get sorted list of keys
|
||||
std::vector<String> keys = _settingsKeys();
|
||||
auto keys = settingsKeys();
|
||||
|
||||
// Add the key-values to the json object
|
||||
for (unsigned int i=0; i<keys.size(); i++) {
|
||||
@@ -8,13 +8,15 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include "espurna.h"
|
||||
#include "libs/EmbedisWrap.h"
|
||||
#include "settings_internal.h"
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
@@ -63,17 +65,83 @@ using settings_cfg_list_t = std::initializer_list<settings_cfg_t>;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
namespace settings {
|
||||
namespace internal {
|
||||
|
||||
uint32_t u32fromString(const String& string, int base);
|
||||
|
||||
template <typename T>
|
||||
using convert_t = T(*)(const String& value);
|
||||
|
||||
template <typename T>
|
||||
T convert(const String& value);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
template <>
|
||||
float convert(const String& value);
|
||||
|
||||
template <>
|
||||
double convert(const String& value);
|
||||
|
||||
template <>
|
||||
int convert(const String& value);
|
||||
|
||||
template <>
|
||||
long convert(const String& value);
|
||||
|
||||
template <>
|
||||
bool convert(const String& value);
|
||||
|
||||
template <>
|
||||
unsigned long convert(const String& value);
|
||||
|
||||
template <>
|
||||
unsigned int convert(const String& value);
|
||||
|
||||
template <>
|
||||
unsigned short convert(const String& value);
|
||||
|
||||
template <>
|
||||
unsigned char convert(const String& value);
|
||||
|
||||
} // namespace settings::internal
|
||||
} // namespace settings
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
void moveSetting(const String& from, const String& to);
|
||||
void moveSetting(const String& from, const String& to, unsigned int index);
|
||||
void moveSettings(const String& from, const String& to);
|
||||
|
||||
#if 1
|
||||
template<typename R, settings::internal::convert_t<R> Rfunc = settings::internal::convert>
|
||||
R getSetting(const settings_key_t& key, R defaultValue);
|
||||
R getSetting(const settings_key_t& key, R defaultValue) __attribute__((noinline));
|
||||
#endif
|
||||
|
||||
template<typename R, settings::internal::convert_t<R> Rfunc = settings::internal::convert>
|
||||
R getSetting(const settings_key_t& key, R defaultValue) {
|
||||
String value;
|
||||
if (!Embedis::get(key.toString(), value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Rfunc(value);
|
||||
}
|
||||
|
||||
template<>
|
||||
String getSetting(const settings_key_t& key, String defaultValue);
|
||||
|
||||
String getSetting(const settings_key_t& key);
|
||||
String getSetting(const settings_key_t& key, const char* defaultValue);
|
||||
String getSetting(const settings_key_t& key, const __FlashStringHelper* defaultValue);
|
||||
|
||||
template<typename T>
|
||||
bool setSetting(const settings_key_t& key, const T& value);
|
||||
bool setSetting(const settings_key_t& key, const T& value) {
|
||||
return Embedis::set(key.toString(), String(value));
|
||||
}
|
||||
|
||||
template<>
|
||||
bool setSetting(const settings_key_t& key, const String& value);
|
||||
|
||||
bool delSetting(const settings_key_t& key);
|
||||
bool hasSetting(const settings_key_t& key);
|
||||
@@ -82,11 +150,23 @@ void saveSettings();
|
||||
void resetSettings();
|
||||
|
||||
void settingsGetJson(JsonObject& data);
|
||||
bool settingsRestoreJson(char* json_string, size_t json_buffer_size = 1024);
|
||||
bool settingsRestoreJson(JsonObject& data);
|
||||
|
||||
size_t settingsKeyCount();
|
||||
String settingsKeyName(unsigned int index);
|
||||
std::vector<String> settingsKeys();
|
||||
|
||||
void settingsProcessConfig(const settings_cfg_list_t& config, settings_filter_t filter = nullptr);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
unsigned long settingsSize();
|
||||
|
||||
void migrate();
|
||||
void settingsSetup();
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Deprecated implementation
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
String getSetting(const String& key, unsigned char index, T defaultValue)
|
||||
@@ -103,3 +183,26 @@ __attribute__((deprecated("hasSetting({key, index}) should be used instead")));
|
||||
template<typename T>
|
||||
bool delSetting(const String& key, unsigned char index)
|
||||
__attribute__((deprecated("delSetting({key, index}) should be used instead")));
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
template<typename T>
|
||||
String getSetting(const String& key, unsigned char index, T defaultValue) {
|
||||
return getSetting({key, index}, defaultValue);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool setSetting(const String& key, unsigned char index, T value) {
|
||||
return setSetting({key, index}, value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool hasSetting(const String& key, unsigned char index) {
|
||||
return hasSetting({key, index});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool delSetting(const String& key, unsigned char index) {
|
||||
return delSetting({key, index});
|
||||
}
|
||||
|
||||
|
||||
@@ -6,70 +6,6 @@ SETTINGS MODULE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <cstdlib>
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
namespace settings {
|
||||
namespace internal {
|
||||
|
||||
template <typename T>
|
||||
using convert_t = T(*)(const String& value);
|
||||
|
||||
template <typename T>
|
||||
T convert(const String& value);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
template <>
|
||||
float convert(const String& value) {
|
||||
return value.toFloat();
|
||||
}
|
||||
|
||||
template <>
|
||||
double convert(const String& value) {
|
||||
return value.toFloat();
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert(const String& value) {
|
||||
return value.toInt();
|
||||
}
|
||||
|
||||
template <>
|
||||
long convert(const String& value) {
|
||||
return value.toInt();
|
||||
}
|
||||
|
||||
template <>
|
||||
bool convert(const String& value) {
|
||||
return convert<int>(value) == 1;
|
||||
}
|
||||
|
||||
template <>
|
||||
unsigned long convert(const String& value) {
|
||||
return strtoul(value.c_str(), nullptr, 10);
|
||||
}
|
||||
|
||||
template <>
|
||||
unsigned int convert(const String& value) {
|
||||
return convert<unsigned long>(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
unsigned short convert(const String& value) {
|
||||
return convert<unsigned long>(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
unsigned char convert(const String& value) {
|
||||
return convert<unsigned long>(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
String convert(const String& value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace settings::internal
|
||||
} // namespace settings
|
||||
|
||||
@@ -8,10 +8,13 @@ https://github.com/esp8266/Arduino/issues/2283#issuecomment-299635604
|
||||
|
||||
*/
|
||||
|
||||
#include "ssdp.h"
|
||||
|
||||
#if SSDP_SUPPORT
|
||||
|
||||
#include <ESP8266SSDP.h>
|
||||
|
||||
#include "web.h"
|
||||
#include "utils.h"
|
||||
|
||||
const char _ssdp_template[] PROGMEM =
|
||||
20
code/espurna/ssdp.h
Normal file
20
code/espurna/ssdp.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
|
||||
SSDP MODULE
|
||||
|
||||
Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
Uses SSDP library by PawelDino (https://github.com/PawelDino)
|
||||
https://github.com/esp8266/Arduino/issues/2283#issuecomment-299635604
|
||||
|
||||
*/
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#if SSDP_SUPPORT
|
||||
|
||||
#include <ESP8266SSDP.h>
|
||||
|
||||
void ssdpSetup();
|
||||
|
||||
#endif // SSDP_SUPPORT
|
||||
|
||||
@@ -4,8 +4,8 @@ EEPROM MODULE
|
||||
|
||||
*/
|
||||
|
||||
#include "debug.h"
|
||||
#include "storage_eeprom.h"
|
||||
#include "settings.h"
|
||||
|
||||
EEPROM_Rotate EEPROMr;
|
||||
bool _eeprom_commit = false;
|
||||
@@ -11,6 +11,8 @@ EEPROM MODULE
|
||||
|
||||
#include <EEPROM_Rotate.h>
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
extern EEPROM_Rotate EEPROMr;
|
||||
|
||||
void eepromSectorsDebug();
|
||||
|
||||
@@ -6,11 +6,17 @@ Copyright (C) 2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include <Ticker.h>
|
||||
#include <EEPROM_Rotate.h>
|
||||
|
||||
#include "system.h"
|
||||
|
||||
#include <Ticker.h>
|
||||
#include <Schedule.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "rtcmem.h"
|
||||
#include "ws.h"
|
||||
#include "ntp.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool _system_send_heartbeat = false;
|
||||
@@ -8,10 +8,7 @@ Copyright (C) 2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ticker.h>
|
||||
#include <Schedule.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include "espurna.h"
|
||||
|
||||
extern "C" {
|
||||
#include "user_interface.h"
|
||||
@@ -31,3 +28,8 @@ void customResetReason(unsigned char reason);
|
||||
|
||||
void deferredReset(unsigned long delay, unsigned char reason);
|
||||
bool checkNeedsReset();
|
||||
|
||||
unsigned long systemLoadAverage();
|
||||
bool systemGetHeartbeat();
|
||||
void systemSendHeartbeat();
|
||||
void systemSetup();
|
||||
|
||||
@@ -15,11 +15,14 @@ Updated to use WiFiServer and support reverse connections by Niek van der Maas <
|
||||
|
||||
*/
|
||||
|
||||
#include "telnet.h"
|
||||
|
||||
#if TELNET_SUPPORT
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "board.h"
|
||||
#include "telnet.h"
|
||||
#include "ws.h"
|
||||
|
||||
TTelnetServer _telnetServer(TELNET_PORT);
|
||||
std::unique_ptr<TTelnetClient> _telnetClients[TELNET_MAX_CLIENTS];
|
||||
@@ -8,12 +8,16 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Schedule.h>
|
||||
|
||||
#include <memory>
|
||||
#include <list>
|
||||
|
||||
#include <Schedule.h>
|
||||
#if TELNET_SERVER == TELNET_SERVER_ASYNC
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
|
||||
struct AsyncBufferedClient {
|
||||
@@ -44,18 +48,28 @@ struct AsyncBufferedClient {
|
||||
std::list<buffer_t> _buffers;
|
||||
};
|
||||
|
||||
#if TELNET_SERVER == TELNET_SERVER_WIFISERVER
|
||||
using TTelnetServer = WiFiServer;
|
||||
using TTelnetClient = WiFiClient;
|
||||
#elif TELNET_SERVER == TELNET_SERVER_ASYNC
|
||||
using TTelnetServer = AsyncServer;
|
||||
using TTelnetServer = AsyncServer;
|
||||
|
||||
#if TELNET_SERVER_ASYNC_BUFFERED
|
||||
using TTelnetClient = AsyncBufferedClient;
|
||||
#else
|
||||
using TTelnetClient = AsyncClient;
|
||||
#endif // TELNET_SERVER_ASYNC_BUFFERED
|
||||
|
||||
#elif TELNET_SERVER == TELNET_SERVER_WIFISERVER
|
||||
|
||||
using TTelnetServer = WiFiServer;
|
||||
using TTelnetClient = WiFiClient;
|
||||
|
||||
#else
|
||||
#error "TELNET_SERVER value was not properly set"
|
||||
#endif
|
||||
|
||||
constexpr const char TELNET_IAC = 0xFF;
|
||||
constexpr const char TELNET_XEOF = 0xEC;
|
||||
constexpr unsigned char TELNET_IAC = 0xFF;
|
||||
constexpr unsigned char TELNET_XEOF = 0xEC;
|
||||
|
||||
bool telnetConnected();
|
||||
unsigned char telnetWrite(unsigned char ch);
|
||||
bool telnetDebugSend(const char* prefix, const char* data);
|
||||
void telnetSetup();
|
||||
|
||||
|
||||
@@ -6,14 +6,20 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
// (HACK) allow us to use internal lwip struct.
|
||||
// esp8266 re-defines enum values from tcp header... include them first
|
||||
#include "terminal.h"
|
||||
|
||||
#if TERMINAL_SUPPORT
|
||||
|
||||
#include "settings.h"
|
||||
#include "system.h"
|
||||
#include "terminal.h"
|
||||
#include "telnet.h"
|
||||
#include "utils.h"
|
||||
#include "wifi.h"
|
||||
#include "ws.h"
|
||||
#include "libs/URL.h"
|
||||
#include "libs/StreamInjector.h"
|
||||
#include "libs/HeapStats.h"
|
||||
|
||||
#include <vector>
|
||||
#include <Stream.h>
|
||||
@@ -66,7 +72,7 @@ void _terminalHelpCommand() {
|
||||
void _terminalKeysCommand() {
|
||||
|
||||
// Get sorted list of keys
|
||||
std::vector<String> keys = _settingsKeys();
|
||||
auto keys = settingsKeys();
|
||||
|
||||
// Write key-values
|
||||
DEBUG_MSG_P(PSTR("Current settings:\n"));
|
||||
@@ -8,6 +8,8 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#if TERMINAL_SUPPORT
|
||||
|
||||
#include "libs/EmbedisWrap.h"
|
||||
@@ -19,6 +21,7 @@ void terminalError(const String& error);
|
||||
|
||||
void terminalRegisterCommand(const String& name, embedis_command_f func);
|
||||
void terminalInject(void *data, size_t len);
|
||||
void terminalInject(char ch);
|
||||
Stream& terminalSerial();
|
||||
|
||||
void terminalSetup();
|
||||
|
||||
@@ -6,19 +6,16 @@ Copyright (C) 2017 by Dmitry Blinov <dblinov76 at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#if THERMOSTAT_SUPPORT
|
||||
#include "thermostat.h"
|
||||
|
||||
#include <float.h>
|
||||
#if THERMOSTAT_SUPPORT
|
||||
|
||||
#include "ntp.h"
|
||||
#include "relay.h"
|
||||
#include "thermostat.h"
|
||||
#include "sensor.h"
|
||||
#include "mqtt.h"
|
||||
#include "ws.h"
|
||||
|
||||
|
||||
bool _thermostat_enabled = true;
|
||||
bool _thermostat_mode_cooler = false;
|
||||
|
||||
const char* NAME_THERMOSTAT_ENABLED = "thermostatEnabled";
|
||||
const char* NAME_THERMOSTAT_MODE = "thermostatMode";
|
||||
const char* NAME_TEMP_RANGE_MIN = "tempRangeMin";
|
||||
@@ -38,25 +35,6 @@ const char* NAME_BURN_DAY = "burnDay";
|
||||
const char* NAME_BURN_MONTH = "burnMonth";
|
||||
const char* NAME_OPERATION_MODE = "thermostatOperationMode";
|
||||
|
||||
#define ASK_TEMP_RANGE_INTERVAL_INITIAL 15000 // ask initially once per every 15 seconds
|
||||
#define ASK_TEMP_RANGE_INTERVAL_REGULAR 60000 // ask every minute to be sure
|
||||
#define MILLIS_IN_SEC 1000
|
||||
#define MILLIS_IN_MIN 60000
|
||||
#define THERMOSTAT_STATE_UPDATE_INTERVAL 60000 // 1 min
|
||||
#define THERMOSTAT_RELAY 0 // use relay 0
|
||||
#define THERMOSTAT_TEMP_RANGE_MIN 10 // grad. Celsius
|
||||
#define THERMOSTAT_TEMP_RANGE_MIN_MIN 3 // grad. Celsius
|
||||
#define THERMOSTAT_TEMP_RANGE_MIN_MAX 30 // grad. Celsius
|
||||
#define THERMOSTAT_TEMP_RANGE_MAX 20 // grad. Celsius
|
||||
#define THERMOSTAT_TEMP_RANGE_MAX_MIN 8 // grad. Celsius
|
||||
#define THERMOSTAT_TEMP_RANGE_MAX_MAX 35 // grad. Celsius
|
||||
#define THERMOSTAT_ALONE_ON_TIME 5 // 5 min
|
||||
#define THERMOSTAT_ALONE_OFF_TIME 55 // 55 min
|
||||
#define THERMOSTAT_MAX_ON_TIME 30 // 30 min
|
||||
#define THERMOSTAT_MIN_OFF_TIME 10 // 10 min
|
||||
#define THERMOSTAT_ENABLED_BY_DEFAULT true
|
||||
#define THERMOSTAT_MODE_COOLER_BY_DEFAULT false
|
||||
|
||||
unsigned long _thermostat_remote_temp_max_wait = THERMOSTAT_REMOTE_TEMP_MAX_WAIT * MILLIS_IN_SEC;
|
||||
unsigned long _thermostat_alone_on_time = THERMOSTAT_ALONE_ON_TIME * MILLIS_IN_MIN;
|
||||
unsigned long _thermostat_alone_off_time = THERMOSTAT_ALONE_OFF_TIME * MILLIS_IN_MIN;
|
||||
@@ -71,23 +49,6 @@ unsigned int _thermostat_burn_prev_month = 0;
|
||||
unsigned int _thermostat_burn_day = 0;
|
||||
unsigned int _thermostat_burn_month = 0;
|
||||
|
||||
struct temp_t {
|
||||
float temp;
|
||||
unsigned long last_update = 0;
|
||||
bool need_display_update = false;
|
||||
};
|
||||
temp_t _remote_temp;
|
||||
|
||||
struct temp_range_t {
|
||||
int min = THERMOSTAT_TEMP_RANGE_MIN;
|
||||
int max = THERMOSTAT_TEMP_RANGE_MAX;
|
||||
unsigned long last_update = 0;
|
||||
unsigned long ask_time = 0;
|
||||
unsigned long ask_interval = ASK_TEMP_RANGE_INTERVAL_INITIAL;
|
||||
bool need_display_update = true;
|
||||
};
|
||||
temp_range_t _temp_range;
|
||||
|
||||
enum temperature_source_t {temp_none, temp_local, temp_remote};
|
||||
struct thermostat_t {
|
||||
unsigned long last_update = 0;
|
||||
@@ -95,12 +56,28 @@ struct thermostat_t {
|
||||
String remote_sensor_name;
|
||||
unsigned int temperature_source = temp_none;
|
||||
};
|
||||
|
||||
bool _thermostat_enabled = true;
|
||||
bool _thermostat_mode_cooler = false;
|
||||
|
||||
temp_t _remote_temp;
|
||||
temp_range_t _temp_range;
|
||||
thermostat_t _thermostat;
|
||||
|
||||
enum thermostat_cycle_type {cooling, heating};
|
||||
unsigned int _thermostat_cycle = heating;
|
||||
String thermostat_remote_sensor_topic;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const temp_t& thermostatRemoteTemp() {
|
||||
return _remote_temp;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const temp_range_t& thermostatRange() {
|
||||
return _temp_range;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void thermostatEnabled(bool enabled) {
|
||||
_thermostat_enabled = enabled;
|
||||
@@ -128,24 +105,6 @@ void thermostatRegister(thermostat_callback_f callback) {
|
||||
_thermostat_callbacks.push_back(callback);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void updateOperationMode() {
|
||||
#if WEB_SUPPORT
|
||||
String message;
|
||||
if (_thermostat.temperature_source == temp_remote) {
|
||||
message = "{\"thermostatVisible\": 1, \"thermostatOperationMode\": \"remote temperature\"}";
|
||||
updateRemoteTemp(true);
|
||||
} else if (_thermostat.temperature_source == temp_local) {
|
||||
message = "{\"thermostatVisible\": 1, \"thermostatOperationMode\": \"local temperature\"}";
|
||||
updateRemoteTemp(false);
|
||||
} else {
|
||||
message = "{\"thermostatVisible\": 1, \"thermostatOperationMode\": \"autonomous\"}";
|
||||
updateRemoteTemp(false);
|
||||
}
|
||||
wsSend(message.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void updateRemoteTemp(bool remote_temp_actual) {
|
||||
#if WEB_SUPPORT
|
||||
@@ -161,6 +120,25 @@ void updateRemoteTemp(bool remote_temp_actual) {
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void updateOperationMode() {
|
||||
#if WEB_SUPPORT
|
||||
String message(F("{\"thermostatVisible\": 1, \"thermostatOperationMode\": \""));
|
||||
if (_thermostat.temperature_source == temp_remote) {
|
||||
message += F("remote temperature");
|
||||
updateRemoteTemp(true);
|
||||
} else if (_thermostat.temperature_source == temp_local) {
|
||||
message += F("local temperature");
|
||||
updateRemoteTemp(false);
|
||||
} else {
|
||||
message += F("autonomous");
|
||||
updateRemoteTemp(false);
|
||||
}
|
||||
message += F("\"}");
|
||||
wsSend(message.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// MQTT
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -233,13 +211,6 @@ void thermostatMQTTCallback(unsigned int type, const char * topic, const char *
|
||||
}
|
||||
}
|
||||
|
||||
#if MQTT_SUPPORT
|
||||
//------------------------------------------------------------------------------
|
||||
void thermostatSetupMQTT() {
|
||||
mqttRegister(thermostatMQTTCallback);
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void notifyRangeChanged(bool min) {
|
||||
DEBUG_MSG_P(PSTR("[THERMOSTAT] notifyRangeChanged %s = %d\n"), min ? "MIN" : "MAX", min ? _temp_range.min : _temp_range.max);
|
||||
@@ -274,35 +245,6 @@ void commonSetup() {
|
||||
_thermostat_min_off_time = getSetting(NAME_MIN_OFF_TIME, THERMOSTAT_MIN_OFF_TIME) * MILLIS_IN_MIN;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void thermostatSetup() {
|
||||
commonSetup();
|
||||
|
||||
_thermostat.temperature_source = temp_none;
|
||||
_thermostat_burn_total = getSetting(NAME_BURN_TOTAL, 0);
|
||||
_thermostat_burn_today = getSetting(NAME_BURN_TODAY, 0);
|
||||
_thermostat_burn_yesterday = getSetting(NAME_BURN_YESTERDAY, 0);
|
||||
_thermostat_burn_this_month = getSetting(NAME_BURN_THIS_MONTH, 0);
|
||||
_thermostat_burn_prev_month = getSetting(NAME_BURN_PREV_MONTH, 0);
|
||||
_thermostat_burn_day = getSetting(NAME_BURN_DAY, 0);
|
||||
_thermostat_burn_month = getSetting(NAME_BURN_MONTH, 0);
|
||||
|
||||
#if MQTT_SUPPORT
|
||||
thermostatSetupMQTT();
|
||||
#endif
|
||||
|
||||
// Websockets
|
||||
#if WEB_SUPPORT
|
||||
wsRegister()
|
||||
.onConnected(_thermostatWebSocketOnConnected)
|
||||
.onKeyCheck(_thermostatWebSocketOnKeyCheck)
|
||||
.onAction(_thermostatWebSocketOnAction);
|
||||
#endif
|
||||
|
||||
espurnaRegisterLoop(thermostatLoop);
|
||||
espurnaRegisterReload(_thermostatReload);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void _thermostatReload() {
|
||||
int prev_temp_range_min = _temp_range.min;
|
||||
@@ -316,58 +258,6 @@ void _thermostatReload() {
|
||||
notifyRangeChanged(false);
|
||||
}
|
||||
|
||||
#if WEB_SUPPORT
|
||||
//------------------------------------------------------------------------------
|
||||
void _thermostatWebSocketOnConnected(JsonObject& root) {
|
||||
root["thermostatEnabled"] = thermostatEnabled();
|
||||
root["thermostatMode"] = thermostatModeCooler();
|
||||
root["thermostatVisible"] = 1;
|
||||
root[NAME_TEMP_RANGE_MIN] = _temp_range.min;
|
||||
root[NAME_TEMP_RANGE_MAX] = _temp_range.max;
|
||||
root[NAME_REMOTE_SENSOR_NAME] = _thermostat.remote_sensor_name;
|
||||
root[NAME_REMOTE_TEMP_MAX_WAIT] = _thermostat_remote_temp_max_wait / MILLIS_IN_SEC;
|
||||
root[NAME_MAX_ON_TIME] = _thermostat_max_on_time / MILLIS_IN_MIN;
|
||||
root[NAME_MIN_OFF_TIME] = _thermostat_min_off_time / MILLIS_IN_MIN;
|
||||
root[NAME_ALONE_ON_TIME] = _thermostat_alone_on_time / MILLIS_IN_MIN;
|
||||
root[NAME_ALONE_OFF_TIME] = _thermostat_alone_off_time / MILLIS_IN_MIN;
|
||||
root[NAME_BURN_TODAY] = _thermostat_burn_today;
|
||||
root[NAME_BURN_YESTERDAY] = _thermostat_burn_yesterday;
|
||||
root[NAME_BURN_THIS_MONTH] = _thermostat_burn_this_month;
|
||||
root[NAME_BURN_PREV_MONTH] = _thermostat_burn_prev_month;
|
||||
root[NAME_BURN_TOTAL] = _thermostat_burn_total;
|
||||
if (_thermostat.temperature_source == temp_remote) {
|
||||
root[NAME_OPERATION_MODE] = "remote temperature";
|
||||
root["remoteTmp"] = _remote_temp.temp;
|
||||
} else if (_thermostat.temperature_source == temp_local) {
|
||||
root[NAME_OPERATION_MODE] = "local temperature";
|
||||
root["remoteTmp"] = "?";
|
||||
} else {
|
||||
root[NAME_OPERATION_MODE] = "autonomous";
|
||||
root["remoteTmp"] = "?";
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool _thermostatWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
|
||||
if (strncmp(key, NAME_THERMOSTAT_ENABLED, strlen(NAME_THERMOSTAT_ENABLED)) == 0) return true;
|
||||
if (strncmp(key, NAME_THERMOSTAT_MODE, strlen(NAME_THERMOSTAT_MODE)) == 0) return true;
|
||||
if (strncmp(key, NAME_TEMP_RANGE_MIN, strlen(NAME_TEMP_RANGE_MIN)) == 0) return true;
|
||||
if (strncmp(key, NAME_TEMP_RANGE_MAX, strlen(NAME_TEMP_RANGE_MAX)) == 0) return true;
|
||||
if (strncmp(key, NAME_REMOTE_SENSOR_NAME, strlen(NAME_REMOTE_SENSOR_NAME)) == 0) return true;
|
||||
if (strncmp(key, NAME_REMOTE_TEMP_MAX_WAIT, strlen(NAME_REMOTE_TEMP_MAX_WAIT)) == 0) return true;
|
||||
if (strncmp(key, NAME_MAX_ON_TIME, strlen(NAME_MAX_ON_TIME)) == 0) return true;
|
||||
if (strncmp(key, NAME_MIN_OFF_TIME, strlen(NAME_MIN_OFF_TIME)) == 0) return true;
|
||||
if (strncmp(key, NAME_ALONE_ON_TIME, strlen(NAME_ALONE_ON_TIME)) == 0) return true;
|
||||
if (strncmp(key, NAME_ALONE_OFF_TIME, strlen(NAME_ALONE_OFF_TIME)) == 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void _thermostatWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
|
||||
if (strcmp(action, "thermostat_reset_counters") == 0) resetBurnCounters();
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void sendTempRangeRequest() {
|
||||
DEBUG_MSG_P(PSTR("[THERMOSTAT] sendTempRangeRequest\n"));
|
||||
@@ -586,8 +476,6 @@ void resetBurnCounters() {
|
||||
_thermostat_burn_prev_month = 0;
|
||||
}
|
||||
|
||||
#endif // THERMOSTAT_SUPPORT
|
||||
|
||||
//#######################################################################
|
||||
// ___ _ _
|
||||
// | \ (_) ___ _ __ | | __ _ _ _
|
||||
@@ -849,3 +737,86 @@ void displayLoop() {
|
||||
}
|
||||
|
||||
#endif // THERMOSTAT_DISPLAY_SUPPORT
|
||||
|
||||
#if WEB_SUPPORT
|
||||
//------------------------------------------------------------------------------
|
||||
void _thermostatWebSocketOnConnected(JsonObject& root) {
|
||||
root["thermostatEnabled"] = thermostatEnabled();
|
||||
root["thermostatMode"] = thermostatModeCooler();
|
||||
root["thermostatVisible"] = 1;
|
||||
root[NAME_TEMP_RANGE_MIN] = _temp_range.min;
|
||||
root[NAME_TEMP_RANGE_MAX] = _temp_range.max;
|
||||
root[NAME_REMOTE_SENSOR_NAME] = _thermostat.remote_sensor_name;
|
||||
root[NAME_REMOTE_TEMP_MAX_WAIT] = _thermostat_remote_temp_max_wait / MILLIS_IN_SEC;
|
||||
root[NAME_MAX_ON_TIME] = _thermostat_max_on_time / MILLIS_IN_MIN;
|
||||
root[NAME_MIN_OFF_TIME] = _thermostat_min_off_time / MILLIS_IN_MIN;
|
||||
root[NAME_ALONE_ON_TIME] = _thermostat_alone_on_time / MILLIS_IN_MIN;
|
||||
root[NAME_ALONE_OFF_TIME] = _thermostat_alone_off_time / MILLIS_IN_MIN;
|
||||
root[NAME_BURN_TODAY] = _thermostat_burn_today;
|
||||
root[NAME_BURN_YESTERDAY] = _thermostat_burn_yesterday;
|
||||
root[NAME_BURN_THIS_MONTH] = _thermostat_burn_this_month;
|
||||
root[NAME_BURN_PREV_MONTH] = _thermostat_burn_prev_month;
|
||||
root[NAME_BURN_TOTAL] = _thermostat_burn_total;
|
||||
if (_thermostat.temperature_source == temp_remote) {
|
||||
root[NAME_OPERATION_MODE] = "remote temperature";
|
||||
root["remoteTmp"] = _remote_temp.temp;
|
||||
} else if (_thermostat.temperature_source == temp_local) {
|
||||
root[NAME_OPERATION_MODE] = "local temperature";
|
||||
root["remoteTmp"] = "?";
|
||||
} else {
|
||||
root[NAME_OPERATION_MODE] = "autonomous";
|
||||
root["remoteTmp"] = "?";
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool _thermostatWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
|
||||
if (strncmp(key, NAME_THERMOSTAT_ENABLED, strlen(NAME_THERMOSTAT_ENABLED)) == 0) return true;
|
||||
if (strncmp(key, NAME_THERMOSTAT_MODE, strlen(NAME_THERMOSTAT_MODE)) == 0) return true;
|
||||
if (strncmp(key, NAME_TEMP_RANGE_MIN, strlen(NAME_TEMP_RANGE_MIN)) == 0) return true;
|
||||
if (strncmp(key, NAME_TEMP_RANGE_MAX, strlen(NAME_TEMP_RANGE_MAX)) == 0) return true;
|
||||
if (strncmp(key, NAME_REMOTE_SENSOR_NAME, strlen(NAME_REMOTE_SENSOR_NAME)) == 0) return true;
|
||||
if (strncmp(key, NAME_REMOTE_TEMP_MAX_WAIT, strlen(NAME_REMOTE_TEMP_MAX_WAIT)) == 0) return true;
|
||||
if (strncmp(key, NAME_MAX_ON_TIME, strlen(NAME_MAX_ON_TIME)) == 0) return true;
|
||||
if (strncmp(key, NAME_MIN_OFF_TIME, strlen(NAME_MIN_OFF_TIME)) == 0) return true;
|
||||
if (strncmp(key, NAME_ALONE_ON_TIME, strlen(NAME_ALONE_ON_TIME)) == 0) return true;
|
||||
if (strncmp(key, NAME_ALONE_OFF_TIME, strlen(NAME_ALONE_OFF_TIME)) == 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void _thermostatWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
|
||||
if (strcmp(action, "thermostat_reset_counters") == 0) resetBurnCounters();
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void thermostatSetup() {
|
||||
commonSetup();
|
||||
|
||||
_thermostat.temperature_source = temp_none;
|
||||
_thermostat_burn_total = getSetting(NAME_BURN_TOTAL, 0);
|
||||
_thermostat_burn_today = getSetting(NAME_BURN_TODAY, 0);
|
||||
_thermostat_burn_yesterday = getSetting(NAME_BURN_YESTERDAY, 0);
|
||||
_thermostat_burn_this_month = getSetting(NAME_BURN_THIS_MONTH, 0);
|
||||
_thermostat_burn_prev_month = getSetting(NAME_BURN_PREV_MONTH, 0);
|
||||
_thermostat_burn_day = getSetting(NAME_BURN_DAY, 0);
|
||||
_thermostat_burn_month = getSetting(NAME_BURN_MONTH, 0);
|
||||
|
||||
#if MQTT_SUPPORT
|
||||
mqttRegister(thermostatMQTTCallback);
|
||||
#endif
|
||||
|
||||
// Websockets
|
||||
#if WEB_SUPPORT
|
||||
wsRegister()
|
||||
.onConnected(_thermostatWebSocketOnConnected)
|
||||
.onKeyCheck(_thermostatWebSocketOnKeyCheck)
|
||||
.onAction(_thermostatWebSocketOnAction);
|
||||
#endif
|
||||
|
||||
espurnaRegisterLoop(thermostatLoop);
|
||||
espurnaRegisterReload(_thermostatReload);
|
||||
}
|
||||
|
||||
#endif // THERMOSTAT_SUPPORT
|
||||
@@ -8,6 +8,8 @@ Copyright (C) 2017 by Dmitry Blinov <dblinov76 at gmail dot com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <float.h>
|
||||
|
||||
@@ -15,5 +17,50 @@ Copyright (C) 2017 by Dmitry Blinov <dblinov76 at gmail dot com>
|
||||
#include <SSD1306.h> // alias for `#include "SSD1306Wire.h"`
|
||||
#endif
|
||||
|
||||
#define ASK_TEMP_RANGE_INTERVAL_INITIAL 15000 // ask initially once per every 15 seconds
|
||||
#define ASK_TEMP_RANGE_INTERVAL_REGULAR 60000 // ask every minute to be sure
|
||||
#define MILLIS_IN_SEC 1000
|
||||
#define MILLIS_IN_MIN 60000
|
||||
#define THERMOSTAT_STATE_UPDATE_INTERVAL 60000 // 1 min
|
||||
#define THERMOSTAT_RELAY 0 // use relay 0
|
||||
#define THERMOSTAT_TEMP_RANGE_MIN 10 // grad. Celsius
|
||||
#define THERMOSTAT_TEMP_RANGE_MIN_MIN 3 // grad. Celsius
|
||||
#define THERMOSTAT_TEMP_RANGE_MIN_MAX 30 // grad. Celsius
|
||||
#define THERMOSTAT_TEMP_RANGE_MAX 20 // grad. Celsius
|
||||
#define THERMOSTAT_TEMP_RANGE_MAX_MIN 8 // grad. Celsius
|
||||
#define THERMOSTAT_TEMP_RANGE_MAX_MAX 35 // grad. Celsius
|
||||
#define THERMOSTAT_ALONE_ON_TIME 5 // 5 min
|
||||
#define THERMOSTAT_ALONE_OFF_TIME 55 // 55 min
|
||||
#define THERMOSTAT_MAX_ON_TIME 30 // 30 min
|
||||
#define THERMOSTAT_MIN_OFF_TIME 10 // 10 min
|
||||
#define THERMOSTAT_ENABLED_BY_DEFAULT true
|
||||
#define THERMOSTAT_MODE_COOLER_BY_DEFAULT false
|
||||
|
||||
struct temp_t {
|
||||
float temp;
|
||||
unsigned long last_update = 0;
|
||||
bool need_display_update = false;
|
||||
};
|
||||
|
||||
struct temp_range_t {
|
||||
int min = THERMOSTAT_TEMP_RANGE_MIN;
|
||||
int max = THERMOSTAT_TEMP_RANGE_MAX;
|
||||
unsigned long last_update = 0;
|
||||
unsigned long ask_time = 0;
|
||||
unsigned long ask_interval = ASK_TEMP_RANGE_INTERVAL_INITIAL;
|
||||
bool need_display_update = true;
|
||||
};
|
||||
|
||||
using thermostat_callback_f = std::function<void(bool state)>;
|
||||
void thermostatRegister(thermostat_callback_f callback);
|
||||
|
||||
const temp_t& thermostatRemoteTemp();
|
||||
const temp_range_t& thermostatRange();
|
||||
|
||||
void thermostatEnabled(bool enabled);
|
||||
bool thermostatEnabled();
|
||||
|
||||
void thermostatModeCooler(bool cooler);
|
||||
bool thermostatModeCooler();
|
||||
|
||||
void thermostatSetup();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user