Merge remote-tracking branch 'origin/v3.4' into dev

This commit is contained in:
proddy
2022-01-23 17:56:52 +01:00
parent 02e2b51814
commit 77e1898512
538 changed files with 32282 additions and 38655 deletions

View File

@@ -54,21 +54,40 @@ bool System::restart_requested_ = false;
// send on/off to a gpio pin
// value: true = HIGH, false = LOW
// e.g. http://ems-esp/api?device=system&cmd=pin&data=1&id=2
bool System::command_pin(const char * value, const int8_t id) {
#ifndef EMSESP_STANDALONE
if (!is_valid_gpio(id)) {
LOG_INFO(F("invalid GPIO number"));
LOG_INFO(F("Invalid GPIO number"));
return false;
}
bool v = false;
if (Helpers::value2bool(value, v)) {
bool v = false;
std::string v1 = {7, '\0'};
int v2 = 0;
if (id == 25 && Helpers::value2number(value, v2)) {
if (v2 >= 0 && v2 <= 255) {
dacWrite(id, v2);
return true;
}
} else if (Helpers::value2bool(value, v)) {
pinMode(id, OUTPUT);
digitalWrite(id, v);
LOG_INFO(F("GPIO %d set to %s"), id, v ? "HIGH" : "LOW");
return true;
} else if (Helpers::value2string(value, v1)) {
if (v1 == "input" || v1 == "in" || v1 == "-1") {
pinMode(id, INPUT);
v = digitalRead(id);
LOG_INFO(F("GPIO %d set input, state %s"), id, v ? "HIGH" : "LOW");
return true;
}
}
LOG_INFO(F("GPIO %d: invalid value"), id);
#endif
return false;
}
@@ -80,7 +99,7 @@ bool System::command_send(const char * value, const int8_t id) {
// fetch device values
bool System::command_fetch(const char * value, const int8_t id) {
std::string value_s(14, '\0');
std::string value_s;
if (Helpers::value2string(value, value_s)) {
if (value_s == "all") {
LOG_INFO(F("Requesting data from EMS devices"));
@@ -107,7 +126,7 @@ bool System::command_fetch(const char * value, const int8_t id) {
// mqtt publish
bool System::command_publish(const char * value, const int8_t id) {
std::string value_s(14, '\0');
std::string value_s;
if (Helpers::value2string(value, value_s)) {
if (value_s == "ha") {
EMSESP::publish_all(true); // includes HA
@@ -126,16 +145,17 @@ bool System::command_publish(const char * value, const int8_t id) {
EMSESP::publish_device_values(EMSdevice::DeviceType::MIXER);
return true;
} else if (value_s == "other") {
EMSESP::publish_other_values();
EMSESP::publish_other_values(); // switch and heat pump
return true;
} else if (value_s == read_flash_string(F_(dallassensor))) {
} else if ((value_s == read_flash_string(F_(dallassensor))) || (value_s == read_flash_string(F_(analogsensor)))) {
EMSESP::publish_sensor_values(true);
return true;
}
}
EMSESP::publish_all(); // ignore value and id
EMSESP::publish_all();
LOG_INFO(F("Publishing all data to MQTT"));
return true;
}
@@ -163,6 +183,9 @@ bool System::command_watch(const char * value, const int8_t id) {
EMSESP::watch_id(0);
}
EMSESP::watch(w);
if (Mqtt::publish_single()) {
Mqtt::publish(F("system/watch"), read_flash_string(FL_(enum_watch)[w]).c_str());
}
return true;
}
uint16_t i = Helpers::hextoint(value);
@@ -171,6 +194,12 @@ bool System::command_watch(const char * value, const int8_t id) {
if (EMSESP::watch() == EMSESP::Watch::WATCH_OFF) {
EMSESP::watch(EMSESP::Watch::WATCH_ON);
}
if (Mqtt::publish_single()) {
char s[10];
snprintf(s, sizeof(s), "0x%04X", i);
Mqtt::publish(F("system/watch"), s);
// Mqtt::publish(F("system/watch"), read_flash_string(FL_(enum_watch)[EMSESP::watch()]).c_str());
}
return true;
}
return false;
@@ -188,7 +217,7 @@ void System::system_restart() {
// saves all settings
void System::wifi_reconnect() {
LOG_INFO(F("Wifi reconnecting..."));
LOG_INFO(F("WiFi reconnecting..."));
Shell::loop_all();
EMSESP::console_.loop();
delay(1000); // wait a second
@@ -196,7 +225,6 @@ void System::wifi_reconnect() {
EMSESP::esp8266React.getNetworkSettingsService()->callUpdateHandlers("local"); // in case we've changed ssid or password
}
// format fs
// format the FS. Wipes everything.
void System::format(uuid::console::Shell & shell) {
auto msg = F("Formatting file system. This will reset all settings to their defaults");
@@ -234,7 +262,7 @@ void System::syslog_start() {
syslog_.hostname(hostname().c_str());
// register the command
Command::add(EMSdevice::DeviceType::SYSTEM, F_(syslog_level), System::command_syslog_level, F("changes syslog level"), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(syslog), System::command_syslog_level, F("change the syslog level"), CommandFlag::ADMIN_ONLY);
} else if (was_enabled) {
// in case service is still running, this flushes the queue
@@ -244,30 +272,50 @@ void System::syslog_start() {
syslog_.mark_interval(0);
syslog_.destination("");
}
if (Mqtt::publish_single()) {
Mqtt::publish(F("system/syslog"), syslog_enabled_ ? read_flash_string(FL_(enum_syslog_level)[syslog_level_ + 1]).c_str() : "off");
if (EMSESP::watch_id() == 0 || EMSESP::watch() == 0) {
Mqtt::publish(F("system/watch"), read_flash_string(FL_(enum_watch)[EMSESP::watch()]).c_str());
} else {
char s[10];
snprintf(s, sizeof(s), "0x%04X", EMSESP::watch_id());
Mqtt::publish(F("system/watch"), s);
}
}
#endif
}
// read all the settings except syslog from the config files and store locally
// read some specific system settings to store locally for faster access
void System::get_settings() {
EMSESP::webSettingsService.read([&](WebSettings & settings) {
// Button
pbutton_gpio_ = settings.pbutton_gpio;
// ADC
pbutton_gpio_ = settings.pbutton_gpio;
analog_enabled_ = settings.analog_enabled;
low_clock_ = settings.low_clock;
hide_led_ = settings.hide_led;
led_gpio_ = settings.led_gpio;
board_profile_ = settings.board_profile;
telnet_enabled_ = settings.telnet_enabled;
// Sysclock
low_clock_ = settings.low_clock;
rx_gpio_ = settings.rx_gpio;
tx_gpio_ = settings.tx_gpio;
dallas_gpio_ = settings.dallas_gpio;
// LED
hide_led_ = settings.hide_led;
led_gpio_ = settings.led_gpio;
syslog_enabled_ = settings.syslog_enabled;
syslog_level_ = settings.syslog_level;
syslog_mark_interval_ = settings.syslog_mark_interval;
syslog_host_ = settings.syslog_host;
syslog_port_ = settings.syslog_port;
// Board profile
board_profile_ = settings.board_profile;
fahrenheit_ = settings.fahrenheit;
bool_format_ = settings.bool_format;
enum_format_ = settings.enum_format;
readonly_mode_ = settings.readonly_mode;
// Ethernet PHY
phy_type_ = settings.phy_type;
phy_type_ = settings.phy_type;
eth_power_ = settings.eth_power;
eth_phy_addr_ = settings.eth_phy_addr;
eth_clock_mode_ = settings.eth_clock_mode;
});
}
@@ -328,7 +376,7 @@ void System::start(uint32_t heap_start) {
#ifndef EMSESP_STANDALONE
// disable bluetooth module
periph_module_disable(PERIPH_BT_MODULE);
// periph_module_disable(PERIPH_BT_MODULE);
if (low_clock_) {
setCpuFrequencyMhz(160);
}
@@ -336,12 +384,10 @@ void System::start(uint32_t heap_start) {
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
hostname(networkSettings.hostname.c_str()); // sets the hostname
LOG_INFO(F("System name: %s"), hostname().c_str());
});
commands_init(); // console & api commands
led_init(false); // init LED
adc_init(false); // analog ADC
button_init(false); // the special button
network_init(false); // network
syslog_start(); // start Syslog
@@ -349,21 +395,6 @@ void System::start(uint32_t heap_start) {
EMSESP::init_uart(); // start UART
}
// adc and bluetooth
void System::adc_init(bool refresh) {
if (refresh) {
get_settings();
}
#ifndef EMSESP_STANDALONE
// disable ADC
/*
if (!analog_enabled_) {
adc_power_release(); // turn off ADC to save power if not needed
}
*/
#endif
}
// button single click
void System::button_OnClick(PButton & b) {
LOG_DEBUG(F("Button pressed - single click"));
@@ -403,9 +434,9 @@ void System::button_init(bool refresh) {
if (is_valid_gpio(pbutton_gpio_)) {
if (!myPButton_.init(pbutton_gpio_, HIGH)) {
LOG_INFO(F("Multi-functional button not detected"));
LOG_DEBUG(F("Multi-functional button not detected"));
} else {
LOG_INFO(F("Multi-functional button enabled"));
LOG_DEBUG(F("Multi-functional button enabled"));
}
} else {
LOG_WARNING(F("Invalid button GPIO. Check config."));
@@ -424,8 +455,8 @@ void System::led_init(bool refresh) {
}
if ((led_gpio_ != 0) && is_valid_gpio(led_gpio_)) {
pinMode(led_gpio_, OUTPUT); // 0 means disabled
digitalWrite(led_gpio_, hide_led_ ? !LED_ON : LED_ON);
pinMode(led_gpio_, OUTPUT); // 0 means disabled
digitalWrite(led_gpio_, !LED_ON); // start with LED off
}
}
@@ -462,9 +493,6 @@ void System::loop() {
led_monitor(); // check status and report back using the LED
system_check(); // check system health
if (analog_enabled_) {
measure_analog();
}
// send out heartbeat
uint32_t currentMillis = uuid::get_uptime();
@@ -509,29 +537,30 @@ bool System::heartbeat_json(JsonObject & output) {
output["bus_status"] = FJSON("disconnected");
}
output["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
output["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
output["uptime_sec"] = uuid::get_uptime_sec();
output["rxreceived"] = EMSESP::rxservice_.telegram_count();
output["rxfails"] = EMSESP::rxservice_.telegram_error_count();
output["txreads"] = EMSESP::txservice_.telegram_read_count();
output["txwrites"] = EMSESP::txservice_.telegram_write_count();
output["txfails"] = EMSESP::txservice_.telegram_fail_count();
output["txfails"] = EMSESP::txservice_.telegram_read_fail_count() + EMSESP::txservice_.telegram_write_fail_count();
if (Mqtt::enabled()) {
output["mqttfails"] = Mqtt::publish_fails();
output["mqttfails"] = Mqtt::publish_fails();
}
if (EMSESP::dallas_enabled()) {
output["dallasfails"] = EMSESP::sensor_fails();
output["apicalls"] = WebAPIService::api_count(); // + WebAPIService::api_fails();
output["apifails"] = WebAPIService::api_fails();
if (EMSESP::dallas_enabled() || EMSESP::analog_enabled()) {
output["sensorreads"] = EMSESP::dallassensor_.reads() + EMSESP::analogsensor_.reads();
output["sensorfails"] = EMSESP::dallassensor_.fails() + EMSESP::analogsensor_.fails();
}
#ifndef EMSESP_STANDALONE
output["freemem"] = ESP.getFreeHeap() / 1000L; // kilobytes
#endif
if (analog_enabled_) {
output["adc"] = analog_;
}
#ifndef EMSESP_STANDALONE
if (!ethernet_connected_) {
int8_t rssi = WiFi.RSSI();
@@ -550,43 +579,14 @@ void System::send_heartbeat() {
return;
}
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc;
JsonObject json = doc.to<JsonObject>();
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> doc;
JsonObject json = doc.to<JsonObject>();
if (heartbeat_json(json)) {
Mqtt::publish(F_(heartbeat), doc.as<JsonObject>()); // send to MQTT with retain off. This will add to MQTT queue.
Mqtt::publish(F_(heartbeat), json); // send to MQTT with retain off. This will add to MQTT queue.
}
}
// measure and moving average adc
void System::measure_analog() {
static uint32_t measure_last_ = 0;
if (!measure_last_ || (uint32_t)(uuid::get_uptime() - measure_last_) >= SYSTEM_MEASURE_ANALOG_INTERVAL) {
measure_last_ = uuid::get_uptime();
#if defined(EMSESP_STANDALONE)
uint16_t a = 0;
#else
uint16_t a = analogReadMilliVolts(ADC1_CHANNEL_0_GPIO_NUM);
#endif
static uint32_t sum_ = 0;
if (!analog_) { // init first time
analog_ = a;
sum_ = a * 512;
} else { // simple moving average filter
sum_ = (sum_ * 511) / 512 + a;
analog_ = sum_ / 512;
}
}
}
// sets rate of led flash
void System::set_led_speed(uint32_t speed) {
led_flash_speed_ = speed;
led_monitor();
}
// initializes network
void System::network_init(bool refresh) {
if (refresh) {
@@ -600,72 +600,57 @@ void System::network_init(bool refresh) {
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 (phy_type_ == PHY_type::PHY_TYPE_LAN8720) {
phy_addr = 1;
power = 16;
mdc = 23;
mdio = 18;
type = ETH_PHY_LAN8720;
clock_mode = ETH_CLOCK_GPIO0_IN;
} else if (phy_type_ == PHY_type::PHY_TYPE_TLK110) {
phy_addr = 31;
power = -1;
mdc = 23;
mdio = 18;
type = ETH_PHY_TLK110;
clock_mode = ETH_CLOCK_GPIO0_IN;
} else {
return; // no valid profile
}
// special case for Olimex ESP32-EVB (LAN8720) (different power and phy_addr)
if (board_profile_.equals("OLIMEX")) {
phy_addr = 0;
power = -1;
mdc = 23;
mdio = 18;
type = ETH_PHY_LAN8720;
clock_mode = ETH_CLOCK_GPIO0_IN;
}
// configure Ethernet
int mdc = 23; // Pin# of the I²C clock signal for the Ethernet PHY - hardcoded
int mdio = 18; // Pin# of the I²C IO signal for the Ethernet PHY - hardcoded
uint8_t phy_addr = eth_phy_addr_; // I²C-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)
int8_t power = eth_power_; // Pin# of the enable signal for the external crystal oscillator (-1 to disable for internal APLL source)
eth_phy_type_t type = (phy_type_ == PHY_type::PHY_TYPE_LAN8720) ? ETH_PHY_LAN8720 : ETH_PHY_TLK110; // Type of the Ethernet PHY (LAN8720 or TLK110)
// clock mode
// ETH_CLOCK_GPIO0_IN = 0 RMII clock input to GPIO0
// ETH_CLOCK_GPIO0_OUT = 1 RMII clock output from GPIO0
// ETH_CLOCK_GPIO16_OUT = 2 RMII clock output from GPIO16
// ETH_CLOCK_GPIO17_OUT = 3 RMII clock output from GPIO17, for 50hz inverted clock
eth_clock_mode_t clock_mode = (eth_clock_mode_t)eth_clock_mode_;
ETH.begin(phy_addr, power, mdc, mdio, type, clock_mode);
}
// check health of system, done every few seconds
// check health of system, done every 5 seconds
void System::system_check() {
if (!last_system_check_ || ((uint32_t)(uuid::get_uptime() - last_system_check_) >= SYSTEM_CHECK_FREQUENCY)) {
last_system_check_ = uuid::get_uptime();
#ifndef EMSESP_STANDALONE
// check if we have a valid network connection
if (!ethernet_connected() && (WiFi.status() != WL_CONNECTED)) {
set_led_speed(LED_WARNING_BLINK_FAST);
system_healthy_ = false;
return;
}
#endif
// not healthy if bus not connected
if (!EMSbus::bus_connected()) {
if (system_healthy_) {
LOG_ERROR(F("Error: No connection to the EMS bus"));
}
system_healthy_ = false;
set_led_speed(LED_WARNING_BLINK); // flash every 1/2 second from now on
healthcheck_ |= HEALTHCHECK_NO_NETWORK;
} else {
// if it was unhealthy but now we're better, make sure the LED is solid again cos we've been healed
if (!system_healthy_) {
system_healthy_ = true;
send_heartbeat();
healthcheck_ &= ~HEALTHCHECK_NO_NETWORK;
}
// check if we have a bus connection
if (!EMSbus::bus_connected()) {
healthcheck_ |= HEALTHCHECK_NO_BUS;
} else {
healthcheck_ &= ~HEALTHCHECK_NO_BUS;
}
// see if the healthcheck state has changed
static uint8_t last_healthcheck_ = 0;
if (healthcheck_ != last_healthcheck_) {
last_healthcheck_ = healthcheck_;
// see if we're better now
if (healthcheck_ == 0) {
// everything is healthy, show LED permanently on or off depending on setting
if (led_gpio_) {
digitalWrite(led_gpio_, hide_led_ ? !LED_ON : LED_ON);
}
send_heartbeat();
} else {
// turn off LED so we're ready to the flashes
if (led_gpio_) {
digitalWrite(led_gpio_, !LED_ON);
}
}
}
}
@@ -676,45 +661,94 @@ void System::commands_init() {
Command::add(EMSdevice::DeviceType::SYSTEM,
F_(pin),
System::command_pin,
F("sets a GPIO on/off"),
F("set a GPIO on/off"),
CommandFlag::MQTT_SUB_FLAG_NOSUB | CommandFlag::ADMIN_ONLY); // dont create a MQTT topic for this
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, F("sends a telegram"), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, F("refreshes all EMS values"), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(restart), System::command_restart, F("restarts EMS-ESP"), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, F("send a telegram"), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, F("refresh all EMS values"), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(restart), System::command_restart, F("restart EMS-ESP"), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(watch), System::command_watch, F("watch incoming telegrams"));
// Command::add(EMSdevice::DeviceType::SYSTEM, F_(syslog), System::command_syslog_level, F("set syslog level"), CommandFlag::ADMIN_ONLY);
if (Mqtt::enabled()) {
Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish, F("forces a MQTT publish"));
Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish, F("force a MQTT publish"));
}
// these commands will return data in JSON format
Command::add(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info, F("show system status"));
Command::add(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings, F("shows system settings"));
Command::add(EMSdevice::DeviceType::SYSTEM, F_(commands), System::command_commands, F("shows system commands"));
Command::add(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings, F("fetch system settings"));
Command::add(EMSdevice::DeviceType::SYSTEM, F_(customizations), System::command_customizations, F("fetch system customizations"));
Command::add(EMSdevice::DeviceType::SYSTEM, F_(commands), System::command_commands, F("fetch system commands"));
#if defined(EMSESP_DEBUG)
Command::add(EMSdevice::DeviceType::SYSTEM, F("test"), System::command_test, F("runs a specific test"));
Command::add(EMSdevice::DeviceType::SYSTEM, F("test"), System::command_test, F("run a specific test"));
#endif
// MQTT subscribe "ems-esp/system/#"
Mqtt::subscribe(EMSdevice::DeviceType::SYSTEM, "system/#", nullptr); // use empty function callback
}
// flashes the LED
// uses LED to show system health
void System::led_monitor() {
if (!led_gpio_) {
return;
// we only need to run the LED healthcheck if there are errors
if (!healthcheck_) {
return; // all good
}
static uint32_t led_last_blink_ = 0;
static uint32_t led_long_timer_ = 1; // 1 will kick it off immediately
static uint32_t led_short_timer_ = 0;
static uint8_t led_flash_step_ = 0; // 0 means we're not in the short flash timer
auto current_time = uuid::get_uptime();
if (!led_last_blink_ || (uint32_t)(uuid::get_uptime() - led_last_blink_) >= led_flash_speed_) {
led_last_blink_ = uuid::get_uptime();
// first long pause before we start flashing
if (led_long_timer_ && (uint32_t)(current_time - led_long_timer_) >= HEALTHCHECK_LED_LONG_DUARATION) {
// Serial.println("starting the flash check");
led_short_timer_ = current_time; // start the short timer
led_long_timer_ = 0; // stop long timer
led_flash_step_ = 1; // enable the short flash timer
}
// if bus_not_connected or network not connected, start flashing
if (!system_healthy_) {
digitalWrite(led_gpio_, !digitalRead(led_gpio_));
// the flash timer which starts after the long pause
if (led_flash_step_ && (uint32_t)(current_time - led_short_timer_) >= HEALTHCHECK_LED_FLASH_DUARATION) {
led_long_timer_ = 0; // stop the long timer
led_short_timer_ = current_time;
static bool led_on_ = false;
if (++led_flash_step_ == 8) {
// reset the whole sequence
// Serial.println("resetting flash check");
led_long_timer_ = uuid::get_uptime();
led_flash_step_ = 0;
digitalWrite(led_gpio_, !LED_ON); // LED off
} else if (led_flash_step_ % 2) {
// handle the step events (on odd numbers 3,5,7,etc). see if we need to turn on a LED
// 1 flash is the EMS bus is not connected
// 2 flashes if the network (wifi or ethernet) is not connected
// 3 flashes is both the bus and the network are not connected. Then you know you're truly f*cked.
if ((led_flash_step_ == 3)
&& (((healthcheck_ & HEALTHCHECK_NO_NETWORK) == HEALTHCHECK_NO_NETWORK) || ((healthcheck_ & HEALTHCHECK_NO_BUS) == HEALTHCHECK_NO_BUS))) {
led_on_ = true;
}
if ((led_flash_step_ == 5) && ((healthcheck_ & HEALTHCHECK_NO_NETWORK) == HEALTHCHECK_NO_NETWORK)) {
led_on_ = true;
}
if ((led_flash_step_ == 7) && ((healthcheck_ & HEALTHCHECK_NO_NETWORK) == HEALTHCHECK_NO_NETWORK)
&& ((healthcheck_ & HEALTHCHECK_NO_BUS) == HEALTHCHECK_NO_BUS)) {
led_on_ = true;
}
if (led_on_ && led_gpio_) {
digitalWrite(led_gpio_, LED_ON);
}
} else {
// turn the led off after the flash, on even number count
if (led_on_ && led_gpio_) {
digitalWrite(led_gpio_, !LED_ON);
led_on_ = false;
}
}
}
}
@@ -761,19 +795,19 @@ void System::show_system(uuid::console::Shell & shell) {
switch (WiFi.status()) {
case WL_IDLE_STATUS:
shell.printfln(F("WiFi: Idle"));
shell.printfln(F("Network: Idle"));
break;
case WL_NO_SSID_AVAIL:
shell.printfln(F("WiFi: Network not found"));
shell.printfln(F("Network: Network not found"));
break;
case WL_SCAN_COMPLETED:
shell.printfln(F("WiFi: Network scan complete"));
shell.printfln(F("Network: Network scan complete"));
break;
case WL_CONNECTED:
shell.printfln(F("WiFi: Connected"));
shell.printfln(F("Network: connected"));
shell.printfln(F("SSID: %s"), WiFi.SSID().c_str());
shell.printfln(F("BSSID: %s"), WiFi.BSSIDstr().c_str());
shell.printfln(F("RSSI: %d dBm (%d %%)"), WiFi.RSSI(), wifi_quality(WiFi.RSSI()));
@@ -788,28 +822,28 @@ void System::show_system(uuid::console::Shell & shell) {
break;
case WL_CONNECT_FAILED:
shell.printfln(F("WiFi: Connection failed"));
shell.printfln(F("WiFi Network: Connection failed"));
break;
case WL_CONNECTION_LOST:
shell.printfln(F("WiFi: Connection lost"));
shell.printfln(F("WiFi Network: Connection lost"));
break;
case WL_DISCONNECTED:
shell.printfln(F("WiFi: Disconnected"));
shell.printfln(F("WiFi Network: Disconnected"));
break;
case WL_NO_SHIELD:
default:
shell.printfln(F("WiFi: Unknown"));
shell.printfln(F("WiFi Network: Unknown"));
break;
}
shell.println();
// show Ethernet
// show Ethernet if connected
if (ethernet_connected_) {
shell.printfln(F("Ethernet: Connected"));
shell.printfln(F("Wired Network: connected"));
shell.printfln(F("MAC address: %s"), ETH.macAddress().c_str());
shell.printfln(F("Hostname: %s"), ETH.getHostname());
shell.printfln(F("IPv4 address: %s/%s"), uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str());
@@ -818,8 +852,6 @@ void System::show_system(uuid::console::Shell & shell) {
if (ETH.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000") {
shell.printfln(F("IPv6 address: %s"), uuid::printable_to_string(ETH.localIPv6()).c_str());
}
} else {
shell.printfln(F("Ethernet: disconnected"));
}
shell.println();
@@ -856,18 +888,20 @@ bool System::command_commands(const char * value, const int8_t id, JsonObject &
// export all settings to JSON text
// http://ems-esp/api/system/settings
// value and id are ignored
// note: ssid and passwords are excluded
bool System::command_settings(const char * value, const int8_t id, JsonObject & output) {
JsonObject node;
output["label"] = "settings";
node = output.createNestedObject("System");
JsonObject node = output.createNestedObject("System");
node["version"] = EMSESP_APP_VERSION;
// hide ssid from this list
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & settings) {
node = output.createNestedObject("Network");
node["hostname"] = settings.hostname;
node["static_ip_config"] = settings.staticIPConfig;
node["enableIPv6"] = settings.enableIPv6;
node["low_bandwidth"] = settings.bandwidth20;
node["disable_sleep"] = settings.nosleep;
JsonUtils::writeIP(node, "local_ip", settings.localIP);
JsonUtils::writeIP(node, "gateway_ip", settings.gatewayIP);
JsonUtils::writeIP(node, "subnet_mask", settings.subnetMask);
@@ -878,35 +912,41 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject &
#ifndef EMSESP_STANDALONE
EMSESP::esp8266React.getAPSettingsService()->read([&](APSettings & settings) {
node = output.createNestedObject("AP");
node["provision_mode"] = settings.provisionMode;
const char * pM[] = {"always", "disconnected", "never"};
node["provision_mode"] = pM[settings.provisionMode];
node["security"] = settings.password.length() ? "wpa2" : "open";
node["ssid"] = settings.ssid;
node["local_ip"] = settings.localIP.toString();
node["gateway_ip"] = settings.gatewayIP.toString();
node["subnet_mask"] = settings.subnetMask.toString();
node["channel"] = settings.channel;
node["ssid_hidden"] = settings.ssidHidden;
node["max_clients"] = settings.maxClients;
});
#endif
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) {
node = output.createNestedObject("MQTT");
node["enabled"] = settings.enabled;
#ifndef EMSESP_STANDALONE
node["host"] = settings.host;
node["port"] = settings.port;
node["username"] = settings.username;
node["client_id"] = settings.clientId;
node["keep_alive"] = settings.keepAlive;
node["clean_session"] = settings.cleanSession;
#endif
node = output.createNestedObject("MQTT");
node["enabled"] = settings.enabled;
node["host"] = settings.host;
node["port"] = settings.port;
node["username"] = settings.username;
node["client_id"] = settings.clientId;
node["keep_alive"] = settings.keepAlive;
node["clean_session"] = settings.cleanSession;
node["base"] = settings.base;
node["discovery_prefix"] = settings.discovery_prefix;
node["nested_format"] = settings.nested_format;
node["ha_enabled"] = settings.ha_enabled;
node["mqtt_qos"] = settings.mqtt_qos;
node["mqtt_retain"] = settings.mqtt_retain;
node["publish_time_boiler"] = settings.publish_time_boiler;
node["publish_time_thermostat"] = settings.publish_time_thermostat;
node["publish_time_solar"] = settings.publish_time_solar;
node["publish_time_mixer"] = settings.publish_time_mixer;
node["publish_time_other"] = settings.publish_time_other;
node["publish_time_sensor"] = settings.publish_time_sensor;
node["ha_climate_format"] = settings.ha_climate_format;
node["ha_enabled"] = settings.ha_enabled;
node["mqtt_qos"] = settings.mqtt_qos;
node["mqtt_retain"] = settings.mqtt_retain;
node["publish_single"] = settings.publish_single;
node["send_response"] = settings.send_response;
});
@@ -949,15 +989,73 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject &
node["dallas_gpio"] = settings.dallas_gpio;
node["pbutton_gpio"] = settings.pbutton_gpio;
node["led_gpio"] = settings.led_gpio;
node["phy_type"] = settings.phy_type;
node["hide_led"] = settings.hide_led;
node["notoken_api"] = settings.notoken_api;
node["hide_led"] = settings.hide_led;
node["notoken_api"] = settings.notoken_api;
node["readonly_mode"] = settings.readonly_mode;
node["fahrenheit"] = settings.fahrenheit;
node["dallas_parasite"] = settings.dallas_parasite;
node["dallas_format"] = settings.dallas_format;
node["bool_format"] = settings.bool_format;
node["enum_format"] = settings.enum_format;
node["analog_enabled"] = settings.analog_enabled;
node["telnet_enabled"] = settings.telnet_enabled;
node["phy_type"] = settings.phy_type;
node["eth_power"] = settings.eth_power;
node["eth_phy_addr"] = settings.eth_phy_addr;
node["eth_clock_mode"] = settings.eth_clock_mode;
});
return true;
}
// http://ems-esp/api/system/customizations
bool System::command_customizations(const char * value, const int8_t id, JsonObject & output) {
output["label"] = "customizations";
JsonObject node = output.createNestedObject("Customizations");
// hide ssid from this list
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
// sensors
JsonArray sensorsJson = node.createNestedArray("sensors");
for (const auto & sensor : settings.sensorCustomizations) {
JsonObject sensorJson = sensorsJson.createNestedObject();
sensorJson["id_str"] = sensor.id_str; // key, is
sensorJson["name"] = sensor.name; // n
sensorJson["offset"] = sensor.offset; // o
}
JsonArray analogJson = node.createNestedArray("analogs");
for (const AnalogCustomization & sensor : settings.analogCustomizations) {
JsonObject sensorJson = analogJson.createNestedObject();
sensorJson["gpio"] = sensor.id;
sensorJson["name"] = sensor.name;
if (EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX) {
sensorJson["type"] = sensor.type;
} else {
sensorJson["type"] = FL_(enum_sensortype)[sensor.type];
}
if (sensor.type == AnalogSensor::AnalogType::ADC) {
sensorJson["offset"] = sensor.offset;
sensorJson["factor"] = sensor.factor;
sensorJson["uom"] = EMSdevice::uom_to_string(sensor.uom);
}
}
// exclude entities
JsonArray exclude_entitiesJson = node.createNestedArray("exclude_entities");
for (const auto & entityCustomization : settings.entityCustomizations) {
JsonObject entityJson = exclude_entitiesJson.createNestedObject();
entityJson["product_id"] = entityCustomization.product_id;
entityJson["device_id"] = entityCustomization.device_id;
JsonArray exclude_entityJson = entityJson.createNestedArray("entity_ids");
for (uint8_t entity_id : entityCustomization.entity_ids) {
exclude_entityJson.add(entity_id);
}
}
});
return true;
@@ -981,7 +1079,11 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
node["reset reason"] = EMSESP::system_.reset_reason(0) + " / " + EMSESP::system_.reset_reason(1);
if (EMSESP::dallas_enabled()) {
node["Dallas sensors"] = EMSESP::sensor_devices().size();
node["Temperature sensors"] = EMSESP::dallassensor_.no_sensors();
}
if (EMSESP::analog_enabled()) {
node["Analog sensors"] = EMSESP::analogsensor_.no_sensors();
}
#ifndef EMSESP_STANDALONE
@@ -1001,7 +1103,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
node["IPv6 address"] = uuid::printable_to_string(WiFi.localIPv6());
}
} else if (EMSESP::system_.ethernet_connected()) {
node["connection"] = F("Ethernet");
node["connection"] = F("Wired");
node["hostname"] = ETH.getHostname();
node["MAC"] = ETH.macAddress();
node["IPv4 address"] = uuid::printable_to_string(ETH.localIP()) + "/" + uuid::printable_to_string(ETH.subnetMask());
@@ -1021,7 +1123,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
node["bus status"] = (F("disconnected"));
break;
case EMSESP::BUS_STATUS_TX_ERRORS:
node["bus status"] = (F("connected, instable tx"));
node["bus status"] = (F("connected, tx issues - try a different tx-mode"));
break;
case EMSESP::BUS_STATUS_CONNECTED:
default:
@@ -1030,27 +1132,39 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
}
if (EMSESP::bus_status() != EMSESP::BUS_STATUS_OFFLINE) {
node["bus protocol"] = EMSbus::is_ht3() ? F("HT3") : F("Buderus");
node["telegrams received"] = EMSESP::rxservice_.telegram_count();
node["read requests sent"] = EMSESP::txservice_.telegram_read_count();
node["write requests sent"] = EMSESP::txservice_.telegram_write_count();
node["incomplete telegrams"] = EMSESP::rxservice_.telegram_error_count();
node["tx fails"] = EMSESP::txservice_.telegram_fail_count();
node["rx line quality"] = EMSESP::rxservice_.quality();
node["tx line quality"] = EMSESP::txservice_.quality();
node["bus protocol"] = EMSbus::is_ht3() ? F("HT3") : F("Buderus");
node["bus telegrams received (rx)"] = EMSESP::rxservice_.telegram_count();
node["bus reads (tx)"] = EMSESP::txservice_.telegram_read_count();
node["bus writes (tx)"] = EMSESP::txservice_.telegram_write_count();
node["bus incomplete telegrams"] = EMSESP::rxservice_.telegram_error_count();
node["bus reads failed"] = EMSESP::txservice_.telegram_read_fail_count();
node["bus writes failed"] = EMSESP::txservice_.telegram_write_fail_count();
node["bus rx line quality"] = EMSESP::rxservice_.quality();
node["bus tx line quality"] = (EMSESP::txservice_.read_quality() + EMSESP::txservice_.read_quality()) / 2;
if (Mqtt::enabled()) {
node["MQTT"] = Mqtt::connected() ? F_(connected) : F_(disconnected);
node["MQTT status"] = Mqtt::connected() ? F_(connected) : F_(disconnected);
node["MQTT publishes"] = Mqtt::publish_count();
node["MQTT publish fails"] = Mqtt::publish_fails();
}
node["Temperature sensors"] = EMSESP::dallassensor_.no_sensors();
if (EMSESP::dallas_enabled()) {
node["Dallas reads"] = EMSESP::sensor_reads();
node["Dallas fails"] = EMSESP::sensor_fails();
node["Temperature sensor reads"] = EMSESP::dallassensor_.reads();
node["Temperature sensor fails"] = EMSESP::dallassensor_.fails();
}
node["Analog sensors"] = EMSESP::analogsensor_.no_sensors();
if (EMSESP::analog_enabled()) {
node["Analog sensor reads"] = EMSESP::analogsensor_.reads();
node["Analog sensor fails"] = EMSESP::analogsensor_.fails();
}
node["API calls"] = WebAPIService::api_count();
node["API fails"] = WebAPIService::api_fails();
#ifndef EMSESP_STANDALONE
if (EMSESP::system_.syslog_enabled_) {
node["syslog IP"] = syslog_.ip();
node["syslog active"] = syslog_.started();
node["syslog_started"] = syslog_.started();
node["syslog_level"] = FL_(enum_syslog_level)[syslog_.log_level() + 1];
node["syslog_ip"] = syslog_.ip();
node["syslog_queue"] = syslog_.queued();
}
#endif
}
@@ -1064,9 +1178,17 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
obj["type"] = emsdevice->device_type_name();
obj["name"] = emsdevice->to_string();
char result[200];
(void)emsdevice->show_telegram_handlers(result);
(void)emsdevice->show_telegram_handlers(result, EMSdevice::Handlers::RECEIVED);
if (result[0] != '\0') {
obj["handlers"] = result; // don't show hanlders if there aren't any
obj["handlers_received"] = result; // don't show handlers if there aren't any
}
(void)emsdevice->show_telegram_handlers(result, EMSdevice::Handlers::FETCHED);
if (result[0] != '\0') {
obj["handlers_fetched"] = result;
}
(void)emsdevice->show_telegram_handlers(result, EMSdevice::Handlers::PENDING);
if (result[0] != '\0') {
obj["handlers_pending"] = result;
}
}
}
@@ -1084,29 +1206,55 @@ bool System::command_test(const char * value, const int8_t id) {
#endif
// takes a board profile and populates a data array with GPIO configurations
// data = led, dallas, rx, tx, button, phy_type
// returns false if profile is not found
bool System::load_board_profile(std::vector<uint8_t> & data, const std::string & board_profile) {
//
// data = led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode
//
// clock modes:
// ETH_CLOCK_GPIO0_IN = 0 RMII clock input to GPIO0
// ETH_CLOCK_GPIO0_OUT = 1 RMII clock output from GPIO0
// ETH_CLOCK_GPIO16_OUT = 2 RMII clock output from GPIO16
// ETH_CLOCK_GPIO17_OUT = 3 RMII clock output from GPIO17, for 50hz inverted cloc
bool System::load_board_profile(std::vector<int8_t> & data, const std::string & board_profile) {
if (board_profile == "S32") {
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE}; // BBQKees Gateway S32
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // BBQKees Gateway S32
} else if (board_profile == "E32") {
data = {2, 4, 5, 17, 33, PHY_type::PHY_TYPE_LAN8720}; // BBQKees Gateway E32
data = {2, 4, 5, 17, 33, PHY_type::PHY_TYPE_LAN8720, 16, 1, 0}; // BBQKees Gateway E32
} else if (board_profile == "MH-ET") {
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE}; // MH-ET Live D1 Mini
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // MH-ET Live D1 Mini
} else if (board_profile == "NODEMCU") {
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE}; // NodeMCU 32S
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // NodeMCU 32S
} else if (board_profile == "LOLIN") {
data = {2, 18, 17, 16, 0, PHY_type::PHY_TYPE_NONE}; // Lolin D32
data = {2, 18, 17, 16, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // Lolin D32
} else if (board_profile == "OLIMEX") {
data = {0, 0, 36, 4, 34, PHY_type::PHY_TYPE_LAN8720}; // Olimex ESP32-EVB (uses U1TXD/U1RXD/BUTTON, no LED or Dallas)
data = {0, 0, 36, 4, 34, PHY_type::PHY_TYPE_LAN8720, -1, 0, 0}; // Olimex ESP32-EVB (uses U1TXD/U1RXD/BUTTON, no LED or Dallas)
} else if (board_profile == "OLIMEXPOE") {
data = {0, 0, 36, 4, 34, PHY_type::PHY_TYPE_LAN8720, 12, 0, 3}; // Olimex ESP32-POE
} else if (board_profile == "CUSTOM") {
// send back current values
data = {(int8_t)EMSESP::system_.led_gpio_,
(int8_t)EMSESP::system_.dallas_gpio_,
(int8_t)EMSESP::system_.rx_gpio_,
(int8_t)EMSESP::system_.tx_gpio_,
(int8_t)EMSESP::system_.pbutton_gpio_,
(int8_t)EMSESP::system_.phy_type_,
EMSESP::system_.eth_power_,
(int8_t)EMSESP::system_.eth_phy_addr_,
(int8_t)EMSESP::system_.eth_clock_mode_};
} else {
data = {EMSESP_DEFAULT_LED_GPIO,
EMSESP_DEFAULT_DALLAS_GPIO,
EMSESP_DEFAULT_RX_GPIO,
EMSESP_DEFAULT_TX_GPIO,
EMSESP_DEFAULT_PBUTTON_GPIO,
EMSESP_DEFAULT_PHY_TYPE};
return (board_profile == "CUSTOM");
// unknown, use defaults
data = {
EMSESP_DEFAULT_LED_GPIO,
EMSESP_DEFAULT_DALLAS_GPIO,
EMSESP_DEFAULT_RX_GPIO,
EMSESP_DEFAULT_TX_GPIO,
EMSESP_DEFAULT_PBUTTON_GPIO,
EMSESP_DEFAULT_PHY_TYPE,
-1, // power
0, // phy_addr,
0 // clock_mode
};
return false;
}
return true;
@@ -1147,7 +1295,7 @@ const std::string System::reset_reason(uint8_t cpu) {
case 13:
return ("RTC watch dog reset: CPU");
case 14:
return ("APP CPU reseted by PRO CPU");
return ("APP CPU reset by PRO CPU");
case 15:
return ("Brownout reset");
case 16: