diff --git a/interface/src/project/EMSESPSettingsController.tsx b/interface/src/project/EMSESPSettingsController.tsx
index 2e8fe2af7..53b666efa 100644
--- a/interface/src/project/EMSESPSettingsController.tsx
+++ b/interface/src/project/EMSESPSettingsController.tsx
@@ -117,6 +117,22 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
margin="normal"
/>
+
+ External Button
+
+
+
Dallas Sensor
diff --git a/interface/src/project/EMSESPtypes.ts b/interface/src/project/EMSESPtypes.ts
index d1f67b953..8fddcb7db 100644
--- a/interface/src/project/EMSESPtypes.ts
+++ b/interface/src/project/EMSESPtypes.ts
@@ -17,6 +17,7 @@ export interface EMSESPSettings {
hide_led: boolean;
api_enabled: boolean;
analog_enabled: boolean;
+ pbutton_gpio: number;
trace_raw: boolean;
}
diff --git a/lib/PButton/PButon.cpp b/lib/PButton/PButon.cpp
new file mode 100644
index 000000000..27b23514a
--- /dev/null
+++ b/lib/PButton/PButon.cpp
@@ -0,0 +1,187 @@
+/*
+ * Paul's Button Library
+ *
+ * Handles recognition of button presses, short, long, double-click and special sequences
+ * Used to send specific commands to the ESP8
+ *
+ * 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 .
+ */
+
+#include "PButton.h"
+
+// Constructor
+PButton::PButton() {
+ // Initialization of default properties
+ Debounce_ = 40; // Debounce period to prevent flickering when pressing or releasing the button (in ms)
+ DblClickDelay_ = 250; // Max period between clicks for a double click event (in ms)
+ LongPressDelay_ = 750; // Hold period for a long press event (in ms)
+ VLongPressDelay_ = 3000; // Hold period for a very long press event (in ms)
+
+ cb_onClick = nullptr;
+ cb_onDblClick = nullptr;
+ cb_onLongPress = nullptr;
+ cb_onVLongPress = nullptr;
+
+ // Initialization of variables
+ state_ = true; // Value read from button
+ lastState_ = true; // Last value of button state
+ dblClickWaiting_ = false; // whether we're waiting for a double click (down)
+ dblClickOnNextUp_ = false; // whether to register a double click on next release, or whether to wait and click
+ singleClickOK_ = true; // whether it's OK to do a single click (default is true)
+ downTime_ = -1; // time the button was pressed down
+ upTime_ = -1; // time the button was released
+ ignoreUP_ = true; // whether to ignore the button release because the click+hold was triggered, changed from false for ESP32
+ waitForUP_ = false; // when held, whether to wait for the up event
+ longPressHappened_ = false; // whether or not the hold event happened already
+ vLongPressHappened_ = false; // whether or not the long hold event happened already
+ buttonBusy_ = false; // idle
+}
+
+bool PButton::init(uint8_t pin, bool pullMode) {
+ pin_ = pin;
+ pullMode_ = pullMode; // 1=HIGH (pullup) 0=LOW (pulldown)
+
+#if defined(ESP8266)
+ pinMode(pin_, pullMode ? INPUT_PULLUP : INPUT_PULLDOWN);
+ enabled_ = (digitalRead(pin_) != pullMode); // see if a button is connected
+#else
+ // ESP32
+ pinMode(pin_, INPUT);
+ enabled_ = (digitalRead(pin_) == pullMode); // see if a button is connected
+#endif
+
+ return enabled_;
+}
+
+void PButton::onClick(uint16_t t, buttonEventHandler handler) {
+ Debounce_ = t; // Debounce period to prevent flickering when pressing or releasing the button (in ms)
+ cb_onClick = handler;
+}
+
+void PButton::onDblClick(uint16_t t, buttonEventHandler handler) {
+ DblClickDelay_ = t; // Max period between clicks for a double click event (in ms)
+ cb_onDblClick = handler;
+}
+
+void PButton::onLongPress(uint16_t t, buttonEventHandler handler) {
+ LongPressDelay_ = t; // Hold period for a long press event (in ms)
+ cb_onLongPress = handler;
+}
+
+void PButton::onVLongPress(uint16_t t, buttonEventHandler handler) {
+ VLongPressDelay_ = t; // Hold period for a very long press event (in ms)
+ cb_onVLongPress = handler;
+}
+
+bool PButton::check(void) {
+ if (!enabled_) {
+ return false;
+ }
+
+ int resultEvent = 0;
+ long millisRes = millis();
+
+ state_ = digitalRead(pin_) == pullMode_;
+
+ // Button pressed down
+ if (state_ != pullMode_ && lastState_ == pullMode_ && (millisRes - upTime_) > Debounce_) {
+ // Serial.println("*pressed*");
+ downTime_ = millisRes;
+ ignoreUP_ = false;
+ waitForUP_ = false;
+ singleClickOK_ = true;
+ longPressHappened_ = false;
+ vLongPressHappened_ = false;
+ if ((millisRes - upTime_) < DblClickDelay_ && dblClickOnNextUp_ == false && dblClickWaiting_ == true) {
+ dblClickOnNextUp_ = true;
+ // Serial.println("*double up next*");
+ } else {
+ dblClickOnNextUp_ = false;
+ }
+ dblClickWaiting_ = false;
+
+ buttonBusy_ = true; // something is happening so we'll wait and see what action it is
+ }
+
+ // Button released
+ else if (state_ == pullMode_ && lastState_ != pullMode_ && (millisRes - downTime_) > Debounce_) {
+ if (ignoreUP_ == false) {
+ upTime_ = millisRes;
+ // Serial.println("*released*");
+ if (dblClickOnNextUp_ == false) {
+ dblClickWaiting_ = true;
+ } else {
+ resultEvent = 2;
+ // Serial.println("*double*");
+ dblClickOnNextUp_ = false;
+ dblClickWaiting_ = false;
+ singleClickOK_ = false;
+ }
+ }
+ }
+
+ // Test for normal click event: DblClickDelay expired
+ if (state_ == pullMode_ && (millisRes - upTime_) >= DblClickDelay_ && dblClickWaiting_ == true && dblClickOnNextUp_ == false && singleClickOK_ == true && resultEvent != 2) {
+ // Serial.println("*single click pressed*");
+ resultEvent = 1;
+ dblClickWaiting_ = false;
+ }
+
+ // added code: raise OnLongPress event when only when the button is released
+ if (state_ == pullMode_ && longPressHappened_ && !vLongPressHappened_) {
+ resultEvent = 3;
+ longPressHappened_ = false;
+ }
+
+ // Test for hold
+ if (state_ != pullMode_ && (millisRes - downTime_) >= LongPressDelay_) {
+ // Trigger "normal" hold
+ if (longPressHappened_ == false) {
+ // resultEvent = 3;
+ waitForUP_ = true;
+ ignoreUP_ = true;
+ dblClickOnNextUp_ = false;
+ dblClickWaiting_ = false;
+ // _downTime = millis();
+ longPressHappened_ = true;
+ }
+ // Trigger "long" hold
+ if ((millisRes - downTime_) >= VLongPressDelay_) {
+ if (vLongPressHappened_ == false) {
+ resultEvent = 4;
+ vLongPressHappened_ = true;
+ // _longPressHappened = false;
+ }
+ }
+ }
+
+ lastState_ = state_;
+
+ if (resultEvent == 1 && cb_onClick) {
+ cb_onClick(*this);
+ } else if (resultEvent == 2 && cb_onDblClick) {
+ cb_onDblClick(*this);
+ } else if (resultEvent == 3 && cb_onLongPress) {
+ cb_onLongPress(*this);
+ } else if (resultEvent == 4 && cb_onVLongPress) {
+ cb_onVLongPress(*this);
+ }
+
+ // if any action has been prefromed we can stop waiting, and become idle
+ if (resultEvent >= 1 && resultEvent <= 4) {
+ buttonBusy_ = false;
+ }
+
+ return buttonBusy_;
+}
diff --git a/lib/PButton/PButton.h b/lib/PButton/PButton.h
new file mode 100644
index 000000000..16899ff49
--- /dev/null
+++ b/lib/PButton/PButton.h
@@ -0,0 +1,69 @@
+/*
+ * Paul's Button Library
+ *
+ * Handles recognition of button presses, short, long, double-click and special sequences
+ * Used to send specific commands to the ESP8
+ *
+ * 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 .
+ */
+
+#ifndef _PButton_H_
+#define _PButton_H_
+
+#include
+
+class PButton {
+ public:
+ PButton();
+ ~PButton() = default;
+
+ typedef void (*buttonEventHandler)(PButton &);
+
+ void onClick(uint16_t t, buttonEventHandler handler);
+ void onDblClick(uint16_t, buttonEventHandler handler);
+ void onLongPress(uint16_t t, buttonEventHandler handler);
+ void onVLongPress(uint16_t, buttonEventHandler handler);
+ bool init(uint8_t pin, bool pullMode);
+ bool check(void);
+
+ private:
+ uint16_t Debounce_; // Debounce period to prevent flickering when pressing or releasing the button (in ms)
+ uint16_t DblClickDelay_; // Max period between clicks for a double click event (in ms)
+ uint16_t LongPressDelay_; // Hold period for a long press event (in ms)
+ uint16_t VLongPressDelay_; // Hold period for a very long press event (in ms)
+
+ uint8_t pin_;
+ bool pullMode_;
+ bool enabled_;
+
+ bool state_; // Value read from button
+ bool lastState_; // Last value of button state
+ bool dblClickWaiting_; // whether we're waiting for a double click (down)
+ bool dblClickOnNextUp_; // whether to register a double click on next release, or whether to wait and click
+ bool singleClickOK_; // whether it's OK to do a single click
+
+ uint32_t downTime_; // time the button was pressed down
+ uint32_t upTime_; // time the button was released
+
+ bool ignoreUP_; // whether to ignore the button release because the click+hold was triggered
+ bool waitForUP_; // when held, whether to wait for the up event
+ bool longPressHappened_; // whether or not the hold event happened already
+ bool vLongPressHappened_; // whether or not the long hold event happened already
+
+ bool buttonBusy_; // false if idle
+
+ buttonEventHandler cb_onClick, cb_onDblClick, cb_onLongPress, cb_onVLongPress;
+};
+
+#endif
diff --git a/makefile b/makefile
index 4f01c490a..477397c40 100644
--- a/makefile
+++ b/makefile
@@ -17,8 +17,8 @@ MAKEFLAGS+="j "
#TARGET := $(notdir $(CURDIR))
TARGET := emsesp
BUILD := build
-SOURCES := src lib_standalone lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src src/devices lib/ArduinoJson/src src/test
-INCLUDES := lib/ArduinoJson/src lib_standalone lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src src/devices lib src
+SOURCES := src lib_standalone lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src src/devices lib/ArduinoJson/src lib/PButton src/test
+INCLUDES := lib/ArduinoJson/src lib_standalone lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src lib/PButton src/devices lib src
LIBRARIES :=
CPPCHECK = cppcheck
diff --git a/src/WebSettingsService.cpp b/src/WebSettingsService.cpp
index f14aa7ce9..be60557e7 100644
--- a/src/WebSettingsService.cpp
+++ b/src/WebSettingsService.cpp
@@ -48,6 +48,7 @@ void WebSettings::read(WebSettings & settings, JsonObject & root) {
root["hide_led"] = settings.hide_led;
root["api_enabled"] = settings.api_enabled;
root["analog_enabled"] = settings.analog_enabled;
+ root["pbutton_gpio"] = settings.pbutton_gpio;
}
StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) {
@@ -67,36 +68,32 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
}
// syslog
- snprintf_P(&crc_before[0],
- crc_before.capacity() + 1,
- PSTR("%d%d%d%s"),
- settings.syslog_enabled,
- settings.syslog_level,
- settings.syslog_mark_interval,
- settings.syslog_host.c_str());
+ snprintf_P(&crc_before[0], crc_before.capacity() + 1, PSTR("%d%d%d%s"), settings.syslog_enabled, settings.syslog_level, settings.syslog_mark_interval, settings.syslog_host.c_str());
settings.syslog_enabled = root["syslog_enabled"] | EMSESP_DEFAULT_SYSLOG_ENABLED;
settings.syslog_level = root["syslog_level"] | EMSESP_DEFAULT_SYSLOG_LEVEL;
settings.syslog_mark_interval = root["syslog_mark_interval"] | EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL;
settings.syslog_host = root["syslog_host"] | EMSESP_DEFAULT_SYSLOG_HOST;
settings.trace_raw = root["trace_raw"] | EMSESP_DEFAULT_TRACELOG_RAW;
EMSESP::trace_raw(settings.trace_raw);
- snprintf_P(&crc_after[0],
- crc_after.capacity() + 1,
- PSTR("%d%d%d%s"),
- settings.syslog_enabled,
- settings.syslog_level,
- settings.syslog_mark_interval,
- settings.syslog_host.c_str());
+ snprintf_P(&crc_after[0], crc_after.capacity() + 1, PSTR("%d%d%d%s"), settings.syslog_enabled, settings.syslog_level, settings.syslog_mark_interval, settings.syslog_host.c_str());
if (crc_before != crc_after) {
add_flags(ChangeFlags::SYSLOG);
}
- // other
+ // adc
snprintf_P(&crc_before[0], crc_before.capacity() + 1, PSTR("%d"), settings.analog_enabled);
settings.analog_enabled = root["analog_enabled"] | EMSESP_DEFAULT_ANALOG_ENABLED;
snprintf_P(&crc_after[0], crc_after.capacity() + 1, PSTR("%d"), settings.analog_enabled);
if (crc_before != crc_after) {
- add_flags(ChangeFlags::OTHER);
+ add_flags(ChangeFlags::ADC);
+ }
+
+ // button
+ snprintf_P(&crc_before[0], crc_before.capacity() + 1, PSTR("%d"), settings.pbutton_gpio);
+ settings.pbutton_gpio = root["pbutton_gpio"] | EMSESP_DEFAULT_PBUTTON_GPIO;
+ snprintf_P(&crc_after[0], crc_after.capacity() + 1, PSTR("%d"), settings.pbutton_gpio);
+ if (crc_before != crc_after) {
+ add_flags(ChangeFlags::BUTTON);
}
// dallas
@@ -152,15 +149,19 @@ void WebSettingsService::onUpdate() {
}
if (WebSettings::has_flags(WebSettings::ChangeFlags::SYSLOG)) {
- System::syslog_init();
+ EMSESP::system_.syslog_init(true); // reload settings
}
- if (WebSettings::has_flags(WebSettings::ChangeFlags::OTHER)) {
- System::other_init();
+ if (WebSettings::has_flags(WebSettings::ChangeFlags::ADC)) {
+ EMSESP::system_.adc_init(true); // reload settings
+ }
+
+ if (WebSettings::has_flags(WebSettings::ChangeFlags::BUTTON)) {
+ EMSESP::system_.button_init(true); // reload settings
}
if (WebSettings::has_flags(WebSettings::ChangeFlags::LED)) {
- System::led_init();
+ EMSESP::system_.led_init(true); // reload settings
}
}
diff --git a/src/WebSettingsService.h b/src/WebSettingsService.h
index e096a110f..2898ca2c1 100644
--- a/src/WebSettingsService.h
+++ b/src/WebSettingsService.h
@@ -36,13 +36,7 @@
#define EMSESP_DEFAULT_MASTER_THERMOSTAT 0 // not set
#define EMSESP_DEFAULT_SHOWER_TIMER false
#define EMSESP_DEFAULT_SHOWER_ALERT false
-
-#if defined(ESP32)
#define EMSESP_DEFAULT_HIDE_LED true
-#else
-#define EMSESP_DEFAULT_HIDE_LED false
-#endif
-
#define EMSESP_DEFAULT_DALLAS_PARASITE false
#define EMSESP_DEFAULT_API_ENABLED false // turn off, because its insecure
#define EMSESP_DEFAULT_BOOL_FORMAT 1 // on/off
@@ -54,12 +48,14 @@
#define EMSESP_DEFAULT_TX_GPIO 5 // D8 on Wemos D1-32, OR 16 for UART2 on Lolin D32
#define EMSESP_DEFAULT_DALLAS_GPIO 18 // 18 on Wemos D1-32, 14 on LOLIN D32
#define EMSESP_DEFAULT_LED_GPIO 2 // 2 on Wemos D1-32, 5 on LOLIN D32
+#define EMSESP_DEFAULT_PBUTTON_GPIO 0 // default GPIO is 0 (off)
#else
// for standalone
#define EMSESP_DEFAULT_RX_GPIO 0
#define EMSESP_DEFAULT_TX_GPIO 0
#define EMSESP_DEFAULT_DALLAS_GPIO 0
#define EMSESP_DEFAULT_LED_GPIO 0
+#define EMSESP_DEFAULT_PBUTTON_GPIO 0
#endif
namespace emsesp {
@@ -85,6 +81,7 @@ class WebSettings {
bool hide_led;
bool api_enabled;
bool analog_enabled;
+ uint8_t pbutton_gpio;
static void read(WebSettings & settings, JsonObject & root);
static StateUpdateResult update(JsonObject & root, WebSettings & settings);
@@ -94,10 +91,11 @@ class WebSettings {
NONE = 0,
UART = (1 << 0),
SYSLOG = (1 << 1),
- OTHER = (1 << 2),
+ ADC = (1 << 2),
DALLAS = (1 << 3),
SHOWER = (1 << 4),
- LED = (1 << 5)
+ LED = (1 << 5),
+ BUTTON = (1 << 6)
};
diff --git a/src/system.cpp b/src/system.cpp
index e71f9a4ac..4155eb095 100644
--- a/src/system.cpp
+++ b/src/system.cpp
@@ -19,8 +19,6 @@
#include "system.h"
#include "emsesp.h" // for send_raw_telegram() command
-#include "version.h" // firmware version of EMS-ESP
-
#if defined(EMSESP_TEST)
#include "test/test.h"
#endif
@@ -34,15 +32,8 @@ uuid::syslog::SyslogService System::syslog_;
#endif
// init statics
-uint32_t System::heap_start_ = 1; // avoid using 0 to divide-by-zero later
-bool System::upload_status_ = false;
-bool System::hide_led_ = false;
-uint8_t System::led_gpio_ = 0;
-uint16_t System::analog_ = 0;
-bool System::analog_enabled_ = false;
-bool System::syslog_enabled_ = false;
-std::string System::hostname_;
-bool System::ethernet_connected_ = false;
+uint32_t System::heap_start_ = 1; // avoid using 0 to divide-by-zero later
+PButton System::myPButton_;
// send on/off to a gpio pin
// value: true = HIGH, false = LOW
@@ -106,6 +97,7 @@ void System::restart() {
void System::wifi_reconnect() {
LOG_INFO(F("Wifi reconnecting..."));
Shell::loop_all();
+ EMSESP::console_.loop();
delay(1000); // wait a second
EMSESP::webSettingsService.save(); // local settings
EMSESP::esp8266React.getNetworkSettingsService()->callUpdateHandlers("local"); // in case we've changed ssid or password
@@ -127,21 +119,12 @@ void System::format(uuid::console::Shell & shell) {
System::restart();
}
-void System::syslog_init() {
- int8_t syslog_level_;
- uint32_t syslog_mark_interval_;
- String syslog_host_;
-
- // fetch settings
- EMSESP::webSettingsService.read([&](WebSettings & settings) {
- syslog_enabled_ = settings.syslog_enabled;
- syslog_level_ = settings.syslog_level;
- syslog_mark_interval_ = settings.syslog_mark_interval;
- syslog_host_ = settings.syslog_host;
- });
+void System::syslog_init(bool refresh) {
+ if (refresh) {
+ get_settings();
+ }
#ifndef EMSESP_STANDALONE
-
// check for empty hostname
IPAddress addr;
if (!addr.fromString(syslog_host_.c_str())) {
@@ -161,137 +144,150 @@ void System::syslog_init() {
syslog_.log_level((uuid::log::Level)syslog_level_);
syslog_.mark_interval(syslog_mark_interval_);
syslog_.destination(addr);
- EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) { syslog_.hostname(networkSettings.hostname.c_str()); });
+ syslog_.hostname(hostname_.c_str());
EMSESP::logger().info(F("Syslog started"));
#endif
}
+// read all the settings from the config files and store locally
+void System::get_settings() {
+ EMSESP::webSettingsService.read([&](WebSettings & settings) {
+ // BUTTON
+ pbutton_gpio_ = settings.pbutton_gpio;
+
+ // ADC
+ analog_enabled_ = settings.analog_enabled;
+
+ // SYSLOG
+ syslog_enabled_ = settings.syslog_enabled;
+ syslog_level_ = settings.syslog_level;
+ syslog_mark_interval_ = settings.syslog_mark_interval;
+ syslog_host_ = settings.syslog_host;
+
+ // LED
+ hide_led_ = settings.hide_led;
+ led_gpio_ = settings.led_gpio;
+ });
+
+ EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
+ hostname(networkSettings.hostname.c_str());
+ LOG_INFO(F("System %s booted (EMS-ESP version %s)"), networkSettings.hostname.c_str(), EMSESP_APP_VERSION); // print boot message
+ ethernet_profile_ = networkSettings.ethernet_profile;
+ });
+}
+
// first call. Sets memory and starts up the UART Serial bridge
void System::start(uint32_t heap_start) {
+#if defined(EMSESP_DEBUG)
+ show_mem("Startup");
+#endif
+
// set the inital free mem, only on first boot
if (heap_start_ < 2) {
heap_start_ = heap_start;
}
-#if defined(EMSESP_DEBUG)
- show_mem("Startup");
-#endif
+ // load in all the settings first
+ get_settings();
- uint8_t ethernet_profile;
- EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
- LOG_INFO(F("System %s booted (EMS-ESP version %s)"), networkSettings.hostname.c_str(), EMSESP_APP_VERSION); // print boot message
- ethernet_profile = networkSettings.ethernet_profile;
- });
-
- // these commands respond to the topic "system" and take a payload like {cmd:"", data:"", id:""}
- EMSESP::webSettingsService.read([&](WebSettings & settings) {
- Command::add(EMSdevice::DeviceType::SYSTEM, F_(pin), System::command_pin);
- Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send);
- Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish);
- Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch);
- Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info);
- Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings);
-
-#if defined(EMSESP_TEST)
- Command::add(EMSdevice::DeviceType::SYSTEM, F("test"), System::command_test);
-#endif
- });
-
- // start other services first
- init();
-
- // check ethernet profile, if we're using exclusive Ethernet then disabled wifi and AP/captive portal
- if (ethernet_profile == 0) {
- return;
- }
-
- uint8_t phy_addr; // I²C-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)
- int power; // Pin# of the enable signal for the external crystal oscillator (-1 to disable for internal APLL source)
- int mdc; // Pin# of the I²C clock signal for the Ethernet PHY
- int mdio; // Pin# of the I²C IO signal for the Ethernet PHY
- eth_phy_type_t type; // Type of the Ethernet PHY (LAN8720 or TLK110)
- eth_clock_mode_t clock_mode; // ETH_CLOCK_GPIO0_IN or ETH_CLOCK_GPIO0_OUT, ETH_CLOCK_GPIO16_OUT, ETH_CLOCK_GPIO17_OUT for 50Hz inverted clock
-
- if (ethernet_profile == 1) {
- // LAN8720
- phy_addr = 0;
- power = -1;
- mdc = 23;
- mdio = 18;
- type = ETH_PHY_LAN8720;
- clock_mode = ETH_CLOCK_GPIO0_IN;
- } else if (ethernet_profile == 2) {
- // TLK110
- phy_addr = 31;
- power = -1;
- mdc = 23;
- mdio = 18;
- type = ETH_PHY_TLK110;
- clock_mode = ETH_CLOCK_GPIO0_IN;
- }
-
-#ifndef EMSESP_STANDALONE
- if (ETH.begin(phy_addr, power, mdc, mdio, type, clock_mode)) {
- // disable ssid and AP
- EMSESP::esp8266React.getNetworkSettingsService()->update(
- [&](NetworkSettings & settings) {
- settings.ssid == ""; // remove SSID
- return StateUpdateResult::CHANGED;
- },
- "local");
-
- EMSESP::esp8266React.getAPSettingsService()->update(
- [&](APSettings & settings) {
- settings.provisionMode = AP_MODE_NEVER;
- return StateUpdateResult::CHANGED;
- },
- "local");
- }
-#endif
+ commands_init(); // console & api commands
+ button_init(false); // the special button
+ led_init(false); // init LED
+ syslog_init(false); // init SysLog
+ adc_init(false); // analog ADC
+ network_init(); // network
+ EMSESP::init_tx(); // start UART
}
-void System::other_init() {
- // set the boolean format used for rendering booleans
- EMSESP::webSettingsService.read([&](WebSettings & settings) { analog_enabled_ = settings.analog_enabled; });
-}
-
-// init stuff. This is called when settings are changed in the web
-void System::init() {
- led_init(); // init LED
-
- other_init(); // boolean format and analog setting
-
- syslog_init(); // init SysLog
-
- EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & settings) { hostname(settings.hostname.c_str()); });
-
+// adc and bluetooth
+void System::adc_init(bool refresh) {
+ if (refresh) {
+ get_settings();
+ }
#ifndef EMSESP_STANDALONE
// setCpuFrequencyMhz(160); // default is 240
// disable bluetooth
btStop();
esp_bt_controller_disable();
-
- // turn off ADC to save power if not needed
if (!analog_enabled_) {
- adc_power_off();
+ adc_power_off(); // turn off ADC to save power if not needed
}
#endif
+}
- EMSESP::init_tx(); // start UART
+// button single click
+void System::button_OnClick(PButton & b) {
+ LOG_DEBUG(F("Button pressed - single click"));
+}
+
+// button double click
+void System::button_OnDblClick(PButton & b) {
+ LOG_DEBUG(F("Button pressed - double click - reconnect"));
+ EMSESP::system_.wifi_reconnect();
+}
+
+// button long press
+void System::button_OnLongPress(PButton & b) {
+ LOG_DEBUG(F("Button pressed - long press"));
+}
+
+// button indefinite press
+void System::button_OnVLongPress(PButton & b) {
+ LOG_DEBUG(F("Button pressed - very long press"));
+ LOG_WARNING(F("Performing factory reset..."));
+ EMSESP::console_.loop();
+
+#ifndef EMSESP_STANDALONE
+ // remove all files under config
+ File root = LITTLEFS.open(FS_CONFIG_DIRECTORY);
+ File file;
+ Serial.printf("Removing files: ", file.name());
+ while (file = root.openNextFile()) {
+ Serial.printf("%s ", file.name());
+ LITTLEFS.remove(file.name());
+ }
+ Serial.println();
+
+ // restart
+ WiFi.disconnect(true);
+ delay(500);
+ ESP.restart();
+#endif
+}
+
+// push button
+void System::button_init(bool refresh) {
+ if (refresh) {
+ get_settings();
+ }
+
+ if (pbutton_gpio_) {
+ if (!myPButton_.init(pbutton_gpio_, HIGH)) {
+ LOG_INFO(F("External multi-functional button not detected"));
+ } else {
+ LOG_INFO(F("External multi-functional button enabled"));
+ }
+
+
+ myPButton_.onClick(BUTTON_Debounce, button_OnClick);
+ myPButton_.onDblClick(BUTTON_DblClickDelay, button_OnDblClick);
+ myPButton_.onLongPress(BUTTON_LongPressDelay, button_OnLongPress);
+ myPButton_.onVLongPress(BUTTON_VLongPressDelay, button_OnVLongPress);
+ }
}
// set the LED to on or off when in normal operating mode
-void System::led_init() {
- EMSESP::webSettingsService.read([&](WebSettings & settings) {
- hide_led_ = settings.hide_led;
- led_gpio_ = settings.led_gpio;
- if (led_gpio_) {
- pinMode(led_gpio_, OUTPUT); // 0 means disabled
- digitalWrite(led_gpio_, hide_led_ ? !LED_ON : LED_ON); // LED on, for ever
- }
- });
+void System::led_init(bool refresh) {
+ if (refresh) {
+ get_settings();
+ }
+
+ if (led_gpio_) {
+ pinMode(led_gpio_, OUTPUT); // 0 means disabled
+ digitalWrite(led_gpio_, hide_led_ ? !LED_ON : LED_ON); // LED on, for ever
+ }
}
// returns true if OTA is uploading
@@ -314,6 +310,7 @@ void System::upload_status(bool in_progress) {
// checks system health and handles LED flashing wizardry
void System::loop() {
#ifndef EMSESP_STANDALONE
+ myPButton_.check(); // check button press
if (syslog_enabled_) {
syslog_.loop();
@@ -429,7 +426,57 @@ void System::set_led_speed(uint32_t speed) {
led_monitor();
}
-void System::init_network() {
+// initializes network
+void System::network_init() {
+ // check ethernet profile, if we're using exclusive Ethernet then disabled wifi and AP/captive portal
+ if (ethernet_profile_ == 0) {
+ return;
+ }
+
+ uint8_t phy_addr; // I²C-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)
+ int power; // Pin# of the enable signal for the external crystal oscillator (-1 to disable for internal APLL source)
+ int mdc; // Pin# of the I²C clock signal for the Ethernet PHY
+ int mdio; // Pin# of the I²C IO signal for the Ethernet PHY
+ eth_phy_type_t type; // Type of the Ethernet PHY (LAN8720 or TLK110)
+ eth_clock_mode_t clock_mode; // ETH_CLOCK_GPIO0_IN or ETH_CLOCK_GPIO0_OUT, ETH_CLOCK_GPIO16_OUT, ETH_CLOCK_GPIO17_OUT for 50Hz inverted clock
+
+ if (ethernet_profile_ == 1) {
+ // LAN8720
+ phy_addr = 0;
+ power = -1;
+ mdc = 23;
+ mdio = 18;
+ type = ETH_PHY_LAN8720;
+ clock_mode = ETH_CLOCK_GPIO0_IN;
+ } else if (ethernet_profile_ == 2) {
+ // TLK110
+ phy_addr = 31;
+ power = -1;
+ mdc = 23;
+ mdio = 18;
+ type = ETH_PHY_TLK110;
+ clock_mode = ETH_CLOCK_GPIO0_IN;
+ }
+
+#ifndef EMSESP_STANDALONE
+ if (ETH.begin(phy_addr, power, mdc, mdio, type, clock_mode)) {
+ // disable ssid and AP
+ EMSESP::esp8266React.getNetworkSettingsService()->update(
+ [&](NetworkSettings & settings) {
+ settings.ssid == ""; // remove SSID
+ return StateUpdateResult::CHANGED;
+ },
+ "local");
+
+ EMSESP::esp8266React.getAPSettingsService()->update(
+ [&](APSettings & settings) {
+ settings.provisionMode = AP_MODE_NEVER;
+ return StateUpdateResult::CHANGED;
+ },
+ "local");
+ }
+#endif
+
last_system_check_ = 0; // force the LED to go from fast flash to pulse
send_heartbeat();
}
@@ -467,6 +514,20 @@ void System::system_check() {
}
}
+// commands - takes static function pointers
+void System::commands_init() {
+ // these commands respond to the topic "system" and take a payload like {cmd:"", data:"", id:""}
+ Command::add(EMSdevice::DeviceType::SYSTEM, F_(pin), System::command_pin);
+ Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send);
+ Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish);
+ Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch);
+ Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info);
+ Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings);
+#if defined(EMSESP_TEST)
+ Command::add(EMSdevice::DeviceType::SYSTEM, F("test"), System::command_test);
+#endif
+}
+
// flashes the LED
void System::led_monitor() {
if (!led_gpio_) {
@@ -579,7 +640,7 @@ void System::show_system(uuid::console::Shell & shell) {
shell.println();
// show Ethernet
- if (ethernet_connected()) {
+ if (ethernet_connected_) {
shell.printfln(F("Ethernet: Connected"));
shell.printfln(F("MAC address: %s"), ETH.macAddress().c_str());
shell.printfln(F("Hostname: %s"), ETH.getHostname());
@@ -590,21 +651,18 @@ void System::show_system(uuid::console::Shell & shell) {
shell.printfln(F("Ethernet: disconnected"));
}
- EMSESP::webSettingsService.read([&](WebSettings & settings) {
- shell.println();
-
- if (!settings.syslog_enabled) {
- shell.printfln(F("Syslog: disabled"));
- } else {
- shell.printfln(F("Syslog:"));
- shell.print(F(" "));
- shell.printfln(F_(host_fmt), !settings.syslog_host.isEmpty() ? settings.syslog_host.c_str() : uuid::read_flash_string(F_(unset)).c_str());
- shell.print(F(" "));
- shell.printfln(F_(log_level_fmt), uuid::log::format_level_lowercase(static_cast(settings.syslog_level)));
- shell.print(F(" "));
- shell.printfln(F_(mark_interval_fmt), settings.syslog_mark_interval);
- }
- });
+ shell.println();
+ if (!syslog_enabled_) {
+ shell.printfln(F("Syslog: disabled"));
+ } else {
+ shell.printfln(F("Syslog:"));
+ shell.print(F(" "));
+ shell.printfln(F_(host_fmt), !syslog_host_.isEmpty() ? syslog_host_.c_str() : uuid::read_flash_string(F_(unset)).c_str());
+ shell.print(F(" "));
+ shell.printfln(F_(log_level_fmt), uuid::log::format_level_lowercase(static_cast(syslog_level_)));
+ shell.print(F(" "));
+ shell.printfln(F_(mark_interval_fmt), syslog_mark_interval_);
+ }
#endif
}
@@ -614,19 +672,19 @@ void System::console_commands(Shell & shell, unsigned int context) {
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
CommandFlags::ADMIN,
flash_string_vector{F_(restart)},
- [](Shell & shell __attribute__((unused)), const std::vector & arguments __attribute__((unused))) { restart(); });
+ [](Shell & shell __attribute__((unused)), const std::vector & arguments __attribute__((unused))) { EMSESP::system_.restart(); });
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
CommandFlags::ADMIN,
flash_string_vector{F_(wifi), F_(reconnect)},
- [](Shell & shell __attribute__((unused)), const std::vector & arguments __attribute__((unused))) { wifi_reconnect(); });
+ [](Shell & shell __attribute__((unused)), const std::vector & arguments __attribute__((unused))) { EMSESP::system_.wifi_reconnect(); });
EMSESPShell::commands->add_command(ShellContext::SYSTEM, CommandFlags::ADMIN, flash_string_vector{F_(format)}, [](Shell & shell, const std::vector & arguments __attribute__((unused))) {
shell.enter_password(F_(password_prompt), [=](Shell & shell, bool completed, const std::string & password) {
if (completed) {
EMSESP::esp8266React.getSecuritySettingsService()->read([&](SecuritySettings & securitySettings) {
if (securitySettings.jwtSecret.equals(password.c_str())) {
- format(shell);
+ EMSESP::system_.format(shell);
} else {
shell.println(F("incorrect password"));
}
@@ -658,7 +716,7 @@ void System::console_commands(Shell & shell, unsigned int context) {
});
EMSESPShell::commands->add_command(ShellContext::SYSTEM, CommandFlags::USER, flash_string_vector{F_(show)}, [=](Shell & shell, const std::vector & arguments __attribute__((unused))) {
- show_system(shell);
+ EMSESP::system_.show_system(shell);
shell.println();
});
@@ -725,7 +783,7 @@ void System::console_commands(Shell & shell, unsigned int context) {
});
EMSESPShell::commands->add_command(ShellContext::SYSTEM, CommandFlags::ADMIN, flash_string_vector{F_(show), F_(users)}, [](Shell & shell, const std::vector & arguments __attribute__((unused))) {
- System::show_users(shell);
+ EMSESP::system_.show_users(shell);
});
// enter the context
@@ -825,6 +883,7 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject &
node["hide_led"] = settings.hide_led;
node["api_enabled"] = settings.api_enabled;
node["analog_enabled"] = settings.analog_enabled;
+ node["pbutton_gpio"] = settings.pbutton_gpio;
});
return true;
@@ -894,4 +953,5 @@ bool System::command_test(const char * value, const int8_t id) {
}
#endif
+
} // namespace emsesp
diff --git a/src/system.h b/src/system.h
index 6d527ad4e..32cdeb263 100644
--- a/src/system.h
+++ b/src/system.h
@@ -36,6 +36,7 @@
#endif
#include
+#include
using uuid::console::Shell;
@@ -47,92 +48,103 @@ class System {
void loop();
// commands
- static void restart();
- static void format(uuid::console::Shell & shell);
-
static void console_commands(Shell & shell, unsigned int context);
-
static bool command_pin(const char * value, const int8_t id);
static bool command_send(const char * value, const int8_t id);
static bool command_publish(const char * value, const int8_t id);
static bool command_fetch(const char * value, const int8_t id);
static bool command_info(const char * value, const int8_t id, JsonObject & json);
static bool command_settings(const char * value, const int8_t id, JsonObject & json);
-
#if defined(EMSESP_TEST)
static bool command_test(const char * value, const int8_t id);
#endif
- static void upload_status(bool in_progress);
- static bool upload_status();
- static void show_mem(const char * note);
- void init_network();
- static void init();
- static void led_init();
- static void syslog_init();
- static void other_init();
-
+ void restart();
+ void format(uuid::console::Shell & shell);
+ void upload_status(bool in_progress);
+ bool upload_status();
+ void show_mem(const char * note);
+ void get_settings();
+ void led_init(bool refresh);
+ void syslog_init(bool refresh);
+ void adc_init(bool refresh);
+ void network_init();
+ void commands_init();
+ void button_init(bool refresh);
bool check_upgrade();
void send_heartbeat();
- static std::string hostname() {
+ std::string hostname() {
return hostname_;
}
- static void hostname(std::string hostname) {
+ void hostname(std::string hostname) {
hostname_ = hostname;
}
- static bool ethernet_connected() {
+ bool ethernet_connected() {
return ethernet_connected_;
}
- static void ethernet_connected(bool b) {
+ void ethernet_connected(bool b) {
ethernet_connected_ = b;
}
private:
static uuid::log::Logger logger_;
+ static uint32_t heap_start_;
-#ifndef EMSESP_STANDALONE
- static uuid::syslog::SyslogService syslog_;
-#endif
+ // button
+ static PButton myPButton_; // PButton instance
+ static void button_OnClick(PButton & b);
+ static void button_OnDblClick(PButton & b);
+ static void button_OnLongPress(PButton & b);
+ static void button_OnVLongPress(PButton & b);
+ static constexpr uint32_t BUTTON_Debounce = 40; // Debounce period to prevent flickering when pressing or releasing the button (in ms)
+ static constexpr uint32_t BUTTON_DblClickDelay = 250; // Max period between clicks for a double click event (in ms)
+ static constexpr uint32_t BUTTON_LongPressDelay = 750; // Hold period for a long press event (in ms)
+ static constexpr uint32_t BUTTON_VLongPressDelay = 9000; // Hold period for a very long press event (in ms)
static constexpr uint32_t SYSTEM_CHECK_FREQUENCY = 5000; // check every 5 seconds
static constexpr uint32_t LED_WARNING_BLINK = 1000; // pulse to show no connection, 1 sec
static constexpr uint32_t LED_WARNING_BLINK_FAST = 100; // flash quickly for boot up sequence
static constexpr uint32_t SYSTEM_HEARTBEAT_INTERVAL = 60000; // in milliseconds, how often the MQTT heartbeat is sent (1 min)
static constexpr uint32_t SYSTEM_MEASURE_ANALOG_INTERVAL = 500;
+ static constexpr uint8_t LED_ON = LOW; // internal LED
- // internal LED
- static constexpr uint8_t LED_ON = LOW;
+#ifndef EMSESP_STANDALONE
+ static uuid::syslog::SyslogService syslog_;
+#endif
void led_monitor();
void set_led_speed(uint32_t speed);
void system_check();
void measure_analog();
- static void show_system(uuid::console::Shell & shell);
- static void show_users(uuid::console::Shell & shell);
- static void wifi_reconnect();
- static int8_t wifi_quality();
+ void show_system(uuid::console::Shell & shell);
+ void show_users(uuid::console::Shell & shell);
+ void wifi_reconnect();
+ int8_t wifi_quality();
bool system_healthy_ = false;
uint32_t led_flash_speed_ = LED_WARNING_BLINK_FAST; // default boot flashes quickly
uint32_t last_heartbeat_ = 0;
uint32_t last_system_check_ = 0;
-
- static bool upload_status_; // true if we're in the middle of a OTA firmware upload
- static uint32_t heap_start_;
- static uint16_t analog_;
- static bool ethernet_connected_;
+ bool upload_status_ = false; // true if we're in the middle of a OTA firmware upload
+ bool ethernet_connected_;
+ uint16_t analog_;
// settings
- static std::string hostname_;
- static bool hide_led_;
- static uint8_t led_gpio_;
- static bool syslog_enabled_;
- static bool analog_enabled_;
+ std::string hostname_;
+ bool hide_led_;
+ uint8_t led_gpio_;
+ bool syslog_enabled_;
+ bool analog_enabled_;
+ uint8_t ethernet_profile_;
+ uint8_t pbutton_gpio_;
+ int8_t syslog_level_;
+ uint32_t syslog_mark_interval_;
+ String syslog_host_;
};
} // namespace emsesp