implemented button - #708

This commit is contained in:
proddy
2021-03-01 22:19:28 +01:00
parent 7521ce6ad3
commit 3eb8ac9194
9 changed files with 560 additions and 216 deletions

View File

@@ -117,6 +117,22 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
margin="normal"
/>
<br></br>
<Typography variant="h6" color="primary" >
External Button
</Typography>
<TextValidator
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:40']}
errorMessages={['Button GPIO is required', "Must be a number", "Must be 0 or higher", "Max value is 40"]}
name="pbutton_gpio"
label="Button GPIO pin (0=none)"
fullWidth
variant="outlined"
value={data.pbutton_gpio}
type="number"
onChange={handleValueChange('pbutton_gpio')}
margin="normal"
/>
<br></br>
<Typography variant="h6" color="primary" >
Dallas Sensor
</Typography>

View File

@@ -17,6 +17,7 @@ export interface EMSESPSettings {
hide_led: boolean;
api_enabled: boolean;
analog_enabled: boolean;
pbutton_gpio: number;
trace_raw: boolean;
}

187
lib/PButton/PButon.cpp Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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_;
}

69
lib/PButton/PButton.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef _PButton_H_
#define _PButton_H_
#include <Arduino.h>
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

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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)
};

View File

@@ -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
@@ -35,14 +33,7 @@ uuid::syslog::SyslogService System::syslog_;
// 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;
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;
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) {
if (!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.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<uuid::log::Level>(settings.syslog_level)));
shell.printfln(F_(log_level_fmt), uuid::log::format_level_lowercase(static_cast<uuid::log::Level>(syslog_level_)));
shell.print(F(" "));
shell.printfln(F_(mark_interval_fmt), settings.syslog_mark_interval);
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<std::string> & arguments __attribute__((unused))) { restart(); });
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & 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<std::string> & arguments __attribute__((unused))) { wifi_reconnect(); });
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & 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<std::string> & 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<std::string> & 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<std::string> & 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

View File

@@ -36,6 +36,7 @@
#endif
#include <uuid/log.h>
#include <PButton.h>
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