mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-08 08:49:52 +03:00
Merge remote-tracking branch 'origin/v2_web' into v2
This commit is contained in:
35
src/EMSESPDevicesService.cpp
Normal file
35
src/EMSESPDevicesService.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "EMSESPDevicesService.h"
|
||||
#include "emsesp.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
EMSESPDevicesService::EMSESPDevicesService(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
server->on(EMSESP_DEVICES_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&EMSESPDevicesService::emsespDevicesService, this, std::placeholders::_1),
|
||||
AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
void EMSESPDevicesService::emsespDevicesService(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_EMSESP_STATUS_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
JsonArray devices = root.createNestedArray("devices");
|
||||
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice) {
|
||||
JsonObject deviceRoot = devices.createNestedObject();
|
||||
deviceRoot["type"] = emsdevice->device_type_name();
|
||||
deviceRoot["brand"] = emsdevice->brand_to_string();
|
||||
deviceRoot["name"] = emsdevice->name();
|
||||
deviceRoot["deviceid"] = emsdevice->device_id();
|
||||
deviceRoot["productid"] = emsdevice->product_id();
|
||||
deviceRoot["version"] = emsdevice->version();
|
||||
}
|
||||
}
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
30
src/EMSESPDevicesService.h
Normal file
30
src/EMSESPDevicesService.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef EMSESPDevicesService_h
|
||||
#define EMSESPDevicesService_h
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
// #include <HttpEndpoint.h>
|
||||
// #include <MqttPubSub.h>
|
||||
// #include <WebSocketTxRx.h>
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#define MAX_EMSESP_STATUS_SIZE 1024
|
||||
#define EMSESP_DEVICES_SERVICE_PATH "/rest/emsespDevices"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class EMSESPDevicesService {
|
||||
public:
|
||||
EMSESPDevicesService(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
|
||||
private:
|
||||
void emsespDevicesService(AsyncWebServerRequest * request);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
20
src/EMSESPScanDevicesService.cpp
Normal file
20
src/EMSESPScanDevicesService.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <EMSESPScanDevicesService.h>
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
EMSESPScanDevicesService::EMSESPScanDevicesService(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
server->on(SCAN_DEVICES_SERVICE_PATH,
|
||||
HTTP_POST,
|
||||
securityManager->wrapRequest(std::bind(&EMSESPScanDevicesService::scan_devices, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN));
|
||||
}
|
||||
|
||||
void EMSESPScanDevicesService::scan_devices(AsyncWebServerRequest * request) {
|
||||
request->onDisconnect([]() {
|
||||
EMSESP::send_read_request(EMSdevice::EMS_TYPE_UBADevices, EMSdevice::EMS_DEVICE_ID_BOILER);
|
||||
});
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
21
src/EMSESPScanDevicesService.h
Normal file
21
src/EMSESPScanDevicesService.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef EMSESPScanDevicesService_h
|
||||
#define EMSESPScanDevicesService_h
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#define SCAN_DEVICES_SERVICE_PATH "/rest/scanDevices"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class EMSESPScanDevicesService {
|
||||
public:
|
||||
EMSESPScanDevicesService(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
|
||||
private:
|
||||
void scan_devices(AsyncWebServerRequest * request);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
67
src/EMSESPSettingsService.cpp
Normal file
67
src/EMSESPSettingsService.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "EMSESPSettingsService.h"
|
||||
#include "emsesp.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
EMSESPSettingsService::EMSESPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(EMSESPSettings::read, EMSESPSettings::update, this, server, EMSESP_SETTINGS_SERVICE_PATH, securityManager)
|
||||
, _fsPersistence(EMSESPSettings::read, EMSESPSettings::update, this, fs, EMSESP_SETTINGS_FILE) {
|
||||
}
|
||||
|
||||
void EMSESPSettings::read(EMSESPSettings & settings, JsonObject & root) {
|
||||
root["tx_mode"] = settings.tx_mode;
|
||||
root["ems_bus_id"] = settings.ems_bus_id;
|
||||
|
||||
root["syslog_level"] = settings.syslog_level;
|
||||
root["syslog_mark_interval"] = settings.syslog_mark_interval;
|
||||
root["syslog_host"] = settings.syslog_host;
|
||||
|
||||
root["master_thermostat"] = settings.master_thermostat;
|
||||
root["shower_timer"] = settings.shower_timer;
|
||||
root["shower_alert"] = settings.shower_alert;
|
||||
}
|
||||
|
||||
StateUpdateResult EMSESPSettings::update(JsonObject & root, EMSESPSettings & settings) {
|
||||
EMSESPSettings newSettings = {};
|
||||
newSettings.tx_mode = root["tx_mode"] | EMSESP_DEFAULT_TX_MODE;
|
||||
newSettings.ems_bus_id = root["ems_bus_id"] | EMSESP_DEFAULT_EMS_BUS_ID;
|
||||
|
||||
newSettings.syslog_level = root["syslog_level"] | EMSESP_DEFAULT_SYSLOG_LEVEL;
|
||||
newSettings.syslog_mark_interval = root["syslog_mark_interval"] | EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL;
|
||||
newSettings.syslog_host = root["syslog_host"] | EMSESP_DEFAULT_SYSLOG_HOST;
|
||||
|
||||
newSettings.master_thermostat = root["master_thermostat"] | EMSESP_DEFAULT_MASTER_THERMOSTAT;
|
||||
newSettings.shower_timer = root["shower_timer"] | EMSESP_DEFAULT_SHOWER_TIMER;
|
||||
newSettings.shower_alert = root["shower_alert"] | EMSESP_DEFAULT_SHOWER_ALERT;
|
||||
|
||||
bool changed = false;
|
||||
|
||||
if (newSettings.tx_mode != settings.tx_mode) {
|
||||
EMSESP::reset_tx(newSettings.tx_mode); // reset counters
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ((newSettings.shower_timer != settings.shower_timer) || (newSettings.shower_alert != settings.shower_alert)) {
|
||||
EMSESP::shower_.start();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ((newSettings.syslog_level != settings.syslog_level) || (newSettings.syslog_mark_interval != settings.syslog_mark_interval)
|
||||
|| !newSettings.syslog_host.equals(settings.syslog_host)) {
|
||||
EMSESP::system_.syslog_init();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
settings = newSettings;
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
return StateUpdateResult::UNCHANGED;
|
||||
}
|
||||
|
||||
void EMSESPSettingsService::begin() {
|
||||
_fsPersistence.readFromFS();
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
55
src/EMSESPSettingsService.h
Normal file
55
src/EMSESPSettingsService.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef EMSESPSettingsConfig_h
|
||||
#define EMSESPSettingsConfig_h
|
||||
|
||||
#include <HttpEndpoint.h>
|
||||
#include <FSPersistence.h>
|
||||
|
||||
#define EMSESP_SETTINGS_FILE "/config/emsespSettings.json"
|
||||
#define EMSESP_SETTINGS_SERVICE_PATH "/rest/emsespSettings"
|
||||
|
||||
#define EMSESP_DEFAULT_TX_MODE 1
|
||||
#define EMSESP_DEFAULT_EMS_BUS_ID 0x0B // service key
|
||||
|
||||
#define EMSESP_DEFAULT_SYSLOG_LEVEL -1
|
||||
#define EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL 0
|
||||
#define EMSESP_DEFAULT_SYSLOG_HOST ""
|
||||
|
||||
#define EMSESP_DEFAULT_MASTER_THERMOSTAT 0 // not set
|
||||
#define EMSESP_DEFAULT_SHOWER_TIMER false
|
||||
#define EMSESP_DEFAULT_SHOWER_ALERT false
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
enum MQTT_format : uint8_t { SINGLE = 1, NESTED, HA, CUSTOM };
|
||||
|
||||
class EMSESPSettings {
|
||||
public:
|
||||
uint8_t tx_mode;
|
||||
uint8_t ems_bus_id;
|
||||
uint8_t master_thermostat;
|
||||
bool shower_timer;
|
||||
bool shower_alert;
|
||||
|
||||
// syslog
|
||||
int8_t syslog_level; // uuid::log::Level
|
||||
uint32_t syslog_mark_interval;
|
||||
String syslog_host;
|
||||
|
||||
static void read(EMSESPSettings & settings, JsonObject & root);
|
||||
static StateUpdateResult update(JsonObject & root, EMSESPSettings & settings);
|
||||
};
|
||||
|
||||
class EMSESPSettingsService : public StatefulService<EMSESPSettings> {
|
||||
public:
|
||||
EMSESPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||
|
||||
void begin();
|
||||
|
||||
private:
|
||||
HttpEndpoint<EMSESPSettings> _httpEndpoint;
|
||||
FSPersistence<EMSESPSettings> _fsPersistence;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
67
src/EMSESPStatusService.cpp
Normal file
67
src/EMSESPStatusService.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "EMSESPStatusService.h"
|
||||
#include "emsesp.h"
|
||||
#include "mqtt.h"
|
||||
#include "version.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
EMSESPStatusService::EMSESPStatusService(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
// rest endpoint for web page
|
||||
server->on(EMSESP_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&EMSESPStatusService::emsespStatusService, this, std::placeholders::_1),
|
||||
AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
|
||||
// trigger on wifi connects
|
||||
#ifdef ESP32
|
||||
WiFi.onEvent(onStationModeConnected, WiFiEvent_t::SYSTEM_EVENT_STA_CONNECTED);
|
||||
WiFi.onEvent(onStationModeDisconnected, WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
|
||||
WiFi.onEvent(onStationModeGotIP, WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
|
||||
#elif defined(ESP8266)
|
||||
_onStationModeConnectedHandler = WiFi.onStationModeConnected(onStationModeConnected);
|
||||
_onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected(onStationModeDisconnected);
|
||||
_onStationModeGotIPHandler = WiFi.onStationModeGotIP(onStationModeGotIP);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
void EMSESPStatusService::onStationModeConnected(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
EMSESP::logger().debug(F("Wifi Connected"));
|
||||
}
|
||||
|
||||
void EMSESPStatusService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
EMSESP::logger().debug(F("WiFi Disconnected. Reason code=%d"), info.disconnected.reason);
|
||||
}
|
||||
|
||||
void EMSESPStatusService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
EMSESP::logger().debug(F("WiFi connected with IP=%s, hostname=%s"), WiFi.localIP().toString().c_str(), WiFi.getHostname());
|
||||
}
|
||||
#elif defined(ESP8266)
|
||||
void EMSESPStatusService::onStationModeConnected(const WiFiEventStationModeConnected & event) {
|
||||
EMSESP::logger().debug(F("Wifi connected with SSID %s"), event.ssid.c_str());
|
||||
}
|
||||
|
||||
void EMSESPStatusService::onStationModeDisconnected(const WiFiEventStationModeDisconnected & event) {
|
||||
EMSESP::logger().debug(F("WiFi Disconnected. Reason code=%d"), event.reason);
|
||||
}
|
||||
|
||||
void EMSESPStatusService::onStationModeGotIP(const WiFiEventStationModeGotIP & event) {
|
||||
EMSESP::logger().debug(F("WiFi connected with IP=%s, hostname=%s"), event.ip.toString().c_str(), WiFi.hostname().c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
void EMSESPStatusService::emsespStatusService(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_EMSESP_STATUS_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["status"] = EMSESP::bus_status(); // 0, 1 or 2
|
||||
root["rx_received"] = EMSESP::rxservice_.telegram_count();
|
||||
root["tx_sent"] = EMSESP::txservice_.telegram_read_count() + EMSESP::txservice_.telegram_write_count();
|
||||
root["crc_errors"] = EMSESP::rxservice_.telegram_error_count();
|
||||
root["tx_errors"] = EMSESP::txservice_.telegram_fail_count();
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
38
src/EMSESPStatusService.h
Normal file
38
src/EMSESPStatusService.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef EMSESPStatusService_h
|
||||
#define EMSESPStatusService_h
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
#include <AsyncMqttClient.h>
|
||||
|
||||
#define MAX_EMSESP_STATUS_SIZE 1024
|
||||
#define EMSESP_STATUS_SERVICE_PATH "/rest/emsespStatus"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class EMSESPStatusService {
|
||||
public:
|
||||
EMSESPStatusService(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
|
||||
private:
|
||||
void emsespStatusService(AsyncWebServerRequest * request);
|
||||
|
||||
#ifdef ESP32
|
||||
static void onStationModeConnected(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
static void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
static void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
#elif defined(ESP8266)
|
||||
WiFiEventHandler _onStationModeConnectedHandler;
|
||||
WiFiEventHandler _onStationModeDisconnectedHandler;
|
||||
WiFiEventHandler _onStationModeGotIPHandler;
|
||||
static void onStationModeConnected(const WiFiEventStationModeConnected & event);
|
||||
static void onStationModeDisconnected(const WiFiEventStationModeDisconnected & event);
|
||||
static void onStationModeGotIP(const WiFiEventStationModeGotIP & event);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
312
src/console.cpp
312
src/console.cpp
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "console.h"
|
||||
#include "emsesp.h"
|
||||
#include "version.h"
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
#include "test/test.h"
|
||||
@@ -57,35 +58,33 @@ void EMSESPShell::stopped() {
|
||||
// remove all custom contexts
|
||||
commands->remove_all_commands();
|
||||
|
||||
_console_commands_loaded = false; // make sure they get reloaded next time a console is opened
|
||||
console_commands_loaded_ = false; // make sure they get reloaded next time a console is opened
|
||||
}
|
||||
|
||||
// show welcome banner
|
||||
// this is one of the first functions called when the shell is started
|
||||
void EMSESPShell::display_banner() {
|
||||
Settings settings;
|
||||
|
||||
println();
|
||||
printfln(F("┌──────────────────────────────────────────┐"));
|
||||
#if defined(ESP32)
|
||||
printfln(F("│ %sEMS-ESP version %-10s ESP32%s │"), COLOR_BOLD_ON, settings.app_version().c_str(), COLOR_BOLD_OFF);
|
||||
#else
|
||||
printfln(F("│ %sEMS-ESP version %-10s%s │"), COLOR_BOLD_ON, settings.app_version().c_str(), COLOR_BOLD_OFF);
|
||||
#endif
|
||||
printfln(F("│ %sEMS-ESP version %-10s%s │"), COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_BOLD_OFF);
|
||||
printfln(F("│ %s%shttps://github.com/proddy/EMS-ESP%s │"), COLOR_BRIGHT_GREEN, COLOR_UNDERLINE, COLOR_RESET);
|
||||
printfln(F("│ │"));
|
||||
|
||||
if (System::safe_mode()) {
|
||||
printfln(F("│ %sIN SAFE MODE. EMS BUS IS DISABLED%s │"), COLOR_BRIGHT_RED_BACKGROUND, COLOR_RESET);
|
||||
#ifdef EMSESP_SAFE_MODE
|
||||
printfln(F("│ %s!FORCED AT COMPILE TIME!%s │"), COLOR_BRIGHT_RED, COLOR_RESET);
|
||||
#endif
|
||||
printfln(F("│ │"));
|
||||
}
|
||||
|
||||
printfln(F("│ type %shelp%s to show available commands │"), COLOR_UNDERLINE, COLOR_RESET);
|
||||
printfln(F("└──────────────────────────────────────────┘"));
|
||||
println();
|
||||
|
||||
// set console name
|
||||
EMSESP::esp8266React.getWiFiSettingsService()->read([&](WiFiSettings & wifiSettings) { console_hostname_ = wifiSettings.hostname.c_str(); });
|
||||
|
||||
if (console_hostname_.empty()) {
|
||||
console_hostname_.resize(16, '\0');
|
||||
#if defined(ESP8266)
|
||||
snprintf_P(&console_hostname_[0], console_hostname_.capacity() + 1, PSTR("esp8266"));
|
||||
#else
|
||||
snprintf_P(&console_hostname_[0], console_hostname_.capacity() + 1, PSTR("esp32"));
|
||||
#endif
|
||||
}
|
||||
|
||||
// load the list of commands
|
||||
add_console_commands();
|
||||
|
||||
@@ -98,7 +97,7 @@ void EMSESPShell::display_banner() {
|
||||
void EMSESPShell::add_console_commands() {
|
||||
// if we already have these commands loaded, stop adding duplicates
|
||||
// for example when opening multiple serial/telnet sessions
|
||||
if (_console_commands_loaded) {
|
||||
if (console_commands_loaded_) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -111,50 +110,136 @@ void EMSESPShell::add_console_commands() {
|
||||
flash_string_vector{F_(refresh)},
|
||||
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.printfln(F("Requesting data from EMS devices"));
|
||||
_console_commands_loaded = false;
|
||||
console_commands_loaded_ = false;
|
||||
add_console_commands();
|
||||
EMSESP::fetch_device_values();
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show), F_(version)},
|
||||
flash_string_vector{F_(show)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.printfln(F("%s%sEMS-ESP version %s%s"), COLOR_BRIGHT_GREEN, COLOR_BOLD_ON, Settings().app_version().c_str(), COLOR_RESET);
|
||||
shell.printfln(F("%s%sEMS-ESP version %s%s"), COLOR_BRIGHT_GREEN, COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_RESET);
|
||||
shell.println();
|
||||
EMSESP::show_device_values(shell);
|
||||
EMSESP::show_sensor_values(shell);
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show)},
|
||||
flash_string_vector{F_(show), F_(devices)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::show_devices(shell); });
|
||||
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show), F_(ems)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::show_ems(shell); });
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show), F_(values)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::show_device_values(shell); });
|
||||
|
||||
commands->add_command(
|
||||
ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(bus_id)},
|
||||
flash_string_vector{F_(deviceid_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint8_t device_id = Helpers::hextoint(arguments.front().c_str());
|
||||
if ((device_id == 0x0B) || (device_id == 0x0D) || (device_id == 0x0A) || (device_id == 0x0F) || (device_id == 0x12)) {
|
||||
EMSESP::emsespSettingsService.update(
|
||||
[&](EMSESPSettings & settings) {
|
||||
settings.ems_bus_id = device_id;
|
||||
shell.printfln(F_(bus_id_fmt), settings.ems_bus_id);
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
} else {
|
||||
shell.println(F("Must be 0B, 0D, 0A, 0F or 12"));
|
||||
}
|
||||
},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
|
||||
return std::vector<std::string>{
|
||||
read_flash_string(F("0B")),
|
||||
read_flash_string(F("0D")),
|
||||
read_flash_string(F("0A")),
|
||||
read_flash_string(F("0F")),
|
||||
read_flash_string(F("12")),
|
||||
|
||||
};
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(tx_mode)},
|
||||
flash_string_vector{F_(n_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint8_t tx_mode = std::strtol(arguments[0].c_str(), nullptr, 10);
|
||||
|
||||
EMSESP::emsespSettingsService.update(
|
||||
[&](EMSESPSettings & settings) {
|
||||
settings.tx_mode = tx_mode;
|
||||
shell.printfln(F_(tx_mode_fmt), tx_mode);
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
EMSESP::reset_tx(tx_mode); // reset counters and set tx_mode
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(scan), F_(devices)},
|
||||
flash_string_vector{F_(deep_optional)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.size() == 0) {
|
||||
EMSESP::send_read_request(EMSdevice::EMS_TYPE_UBADevices, EMSdevice::EMS_DEVICE_ID_BOILER);
|
||||
} else {
|
||||
shell.printfln(F("Performing a deep scan..."));
|
||||
std::vector<uint8_t> Device_Ids;
|
||||
|
||||
Device_Ids.push_back(0x08); // Boilers - 0x08
|
||||
Device_Ids.push_back(0x38); // HeatPump - 0x38
|
||||
Device_Ids.push_back(0x30); // Solar Module - 0x30
|
||||
Device_Ids.push_back(0x09); // Controllers - 0x09
|
||||
Device_Ids.push_back(0x02); // Connect - 0x02
|
||||
Device_Ids.push_back(0x48); // Gateway - 0x48
|
||||
Device_Ids.push_back(0x20); // Mixing Devices - 0x20
|
||||
Device_Ids.push_back(0x21); // Mixing Devices - 0x21
|
||||
Device_Ids.push_back(0x22); // Mixing Devices - 0x22
|
||||
Device_Ids.push_back(0x23); // Mixing Devices - 0x23
|
||||
Device_Ids.push_back(0x28); // Mixing Devices WW- 0x28
|
||||
Device_Ids.push_back(0x29); // Mixing Devices WW- 0x29
|
||||
Device_Ids.push_back(0x10); // Thermostats - 0x10
|
||||
Device_Ids.push_back(0x17); // Thermostats - 0x17
|
||||
Device_Ids.push_back(0x18); // Thermostat remote - 0x18
|
||||
Device_Ids.push_back(0x19); // Thermostat remote - 0x19
|
||||
Device_Ids.push_back(0x1A); // Thermostat remote - 0x1A
|
||||
Device_Ids.push_back(0x1B); // Thermostat remote - 0x1B
|
||||
Device_Ids.push_back(0x11); // Switches - 0x11
|
||||
|
||||
// send the read command with Version command
|
||||
for (const uint8_t device_id : Device_Ids) {
|
||||
EMSESP::send_read_request(EMSdevice::EMS_TYPE_VERSION, device_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(set)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
// shell.printfln(F("%s%sEMS-ESP version %s%s"), COLOR_BRIGHT_GREEN, COLOR_BOLD_ON, Settings().app_version().c_str(), COLOR_RESET);
|
||||
// shell.println();
|
||||
// EMSESP::show_emsbus(shell);
|
||||
// EMSESP::show_devices(shell);
|
||||
EMSESP::show_values(shell);
|
||||
EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) {
|
||||
shell.printfln(F_(tx_mode_fmt), settings.tx_mode);
|
||||
shell.printfln(F_(bus_id_fmt), settings.ems_bus_id);
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* add all the submenu contexts...
|
||||
*/
|
||||
|
||||
// MQTT
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(mqtt)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
Mqtt::console_commands(shell, ShellContext::MQTT);
|
||||
});
|
||||
|
||||
// EMS
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(ems)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
EMSESP::console_commands(shell, ShellContext::EMS);
|
||||
});
|
||||
|
||||
// System
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
@@ -169,30 +254,11 @@ void EMSESPShell::add_console_commands() {
|
||||
|
||||
Console::load_standard_commands(ShellContext::MAIN);
|
||||
|
||||
_console_commands_loaded = true;
|
||||
console_commands_loaded_ = true;
|
||||
}
|
||||
|
||||
std::string EMSESPShell::hostname_text() {
|
||||
Settings settings;
|
||||
std::string hostname = settings.hostname();
|
||||
|
||||
if (hostname.empty()) {
|
||||
hostname.resize(16, '\0');
|
||||
|
||||
#if defined(ESP8266)
|
||||
snprintf_P(&hostname[0], hostname.capacity() + 1, PSTR("esp8266"));
|
||||
#else
|
||||
snprintf_P(&hostname[0], hostname.capacity() + 1, PSTR("esp32"));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
if (System::safe_mode()) {
|
||||
return std::string{"safemode@"} + hostname;
|
||||
}
|
||||
*/
|
||||
|
||||
return hostname;
|
||||
return console_hostname_;
|
||||
}
|
||||
|
||||
// remove commands from the current context to save memory before exiting
|
||||
@@ -211,7 +277,7 @@ bool EMSESPShell::exit_context() {
|
||||
void Console::enter_custom_context(Shell & shell, unsigned int context) {
|
||||
load_standard_commands(context);
|
||||
|
||||
// don't enter context if we're already at the root
|
||||
// don't go into the new context if it's already the root (to prevent double loading)
|
||||
if (context != ShellContext::MAIN) {
|
||||
shell.enter_context(context);
|
||||
}
|
||||
@@ -237,7 +303,6 @@ void Console::load_standard_commands(unsigned int context) {
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (!arguments.empty()) {
|
||||
uuid::log::Level level;
|
||||
|
||||
if (uuid::log::parse_level_lowercase(arguments[0], level)) {
|
||||
shell.log_level(level);
|
||||
} else {
|
||||
@@ -245,8 +310,6 @@ void Console::load_standard_commands(unsigned int context) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// print out logging settings
|
||||
shell.printfln(F_(log_level_fmt), uuid::log::format_level_uppercase(shell.log_level()));
|
||||
},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> std::vector<std::string> {
|
||||
@@ -285,29 +348,78 @@ void Console::load_standard_commands(unsigned int context) {
|
||||
if (completed) {
|
||||
uint64_t now = uuid::get_uptime_ms();
|
||||
|
||||
if (!password.empty() && password == Settings().admin_password()) {
|
||||
become_admin(shell);
|
||||
} else {
|
||||
shell.delay_until(now + INVALID_PASSWORD_DELAY_MS, [](Shell & shell) {
|
||||
shell.logger().log(LogLevel::NOTICE, LogFacility::AUTH, F("Invalid admin password on console"));
|
||||
shell.println(F("su: incorrect password"));
|
||||
});
|
||||
}
|
||||
EMSESP::esp8266React.getSecuritySettingsService()->read([&](SecuritySettings & securitySettings) {
|
||||
if (securitySettings.jwtSecret.equals(password.c_str())) {
|
||||
become_admin(shell);
|
||||
} else {
|
||||
shell.delay_until(now + INVALID_PASSWORD_DELAY_MS, [](Shell & shell) {
|
||||
shell.logger().log(LogLevel::NOTICE, LogFacility::AUTH, F("Invalid admin password on console"));
|
||||
shell.println(F("su: incorrect password"));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
EMSESPShell::commands->add_command(
|
||||
context, CommandFlags::ADMIN, flash_string_vector{F_(debug)}, [&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.printfln(F("%s%sEMS-ESP version %s%s"), COLOR_BRIGHT_GREEN, COLOR_BOLD_ON, Settings().app_version().c_str(), COLOR_RESET);
|
||||
Settings settings;
|
||||
settings.commit();
|
||||
settings.show_settings(shell);
|
||||
shell.println();
|
||||
});
|
||||
#endif
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(send), F_(telegram)},
|
||||
flash_string_vector{F_(data_mandatory)},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
|
||||
EMSESP::send_raw_telegram(arguments.front().c_str());
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(watch)},
|
||||
flash_string_vector{F_(watch_format_mandatory), F_(watchid_optional)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
// get raw/pretty
|
||||
if (arguments[0] == read_flash_string(F_(raw))) {
|
||||
emsesp::EMSESP::watch(EMSESP::WATCH_RAW); // raw
|
||||
} else if (arguments[0] == read_flash_string(F_(on))) {
|
||||
emsesp::EMSESP::watch(EMSESP::WATCH_ON); // on
|
||||
} else if (arguments[0] == read_flash_string(F_(off))) {
|
||||
emsesp::EMSESP::watch(EMSESP::WATCH_OFF); // off
|
||||
} else {
|
||||
shell.printfln(F_(invalid_watch));
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t watch_id;
|
||||
if (arguments.size() == 2) {
|
||||
// get the watch_id if its set
|
||||
watch_id = Helpers::hextoint(arguments[1].c_str());
|
||||
} else {
|
||||
watch_id = WATCH_ID_NONE;
|
||||
}
|
||||
|
||||
emsesp::EMSESP::watch_id(watch_id);
|
||||
|
||||
uint8_t watch = emsesp::EMSESP::watch();
|
||||
if (watch == EMSESP::WATCH_OFF) {
|
||||
shell.printfln(F("Watching telegrams is off"));
|
||||
return;
|
||||
}
|
||||
|
||||
// if logging is off, the watch won't show anything, show force it back to NOTICE
|
||||
if (!shell.logger().enabled(Level::NOTICE)) {
|
||||
shell.log_level(Level::NOTICE);
|
||||
}
|
||||
|
||||
if (watch == EMSESP::WATCH_ON) {
|
||||
shell.printfln(F("Watching incoming telegrams, displayed in decoded format"));
|
||||
} else {
|
||||
shell.printfln(F("Watching incoming telegrams, displayed as raw bytes")); // WATCH_RAW
|
||||
}
|
||||
|
||||
watch_id = emsesp::EMSESP::watch_id();
|
||||
if (watch_id != WATCH_ID_NONE) {
|
||||
shell.printfln(F("Filtering only telegrams that match a device ID or telegram type of 0x%02X"), watch_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// prompt, change per context
|
||||
@@ -316,12 +428,6 @@ std::string EMSESPShell::context_text() {
|
||||
case ShellContext::MAIN:
|
||||
return std::string{'/'};
|
||||
|
||||
case ShellContext::EMS:
|
||||
return std::string{"/ems"};
|
||||
|
||||
case ShellContext::MQTT:
|
||||
return std::string{"/mqtt"};
|
||||
|
||||
case ShellContext::BOILER:
|
||||
return std::string{"/boiler"};
|
||||
|
||||
@@ -403,23 +509,19 @@ std::string EMSESPStreamConsole::console_name() {
|
||||
void Console::start() {
|
||||
// if we've detected a boot into safe mode on ESP8266, start the Serial console too
|
||||
// Serial is always on with the ESP32 as it has 2 UARTs
|
||||
#if defined(ESP32) || defined(EMSESP_STANDALONE)
|
||||
if (true) {
|
||||
#elif defined(ESP8266)
|
||||
if (System::safe_mode()) {
|
||||
#endif
|
||||
serial_console_.begin(SERIAL_CONSOLE_BAUD_RATE);
|
||||
serial_console_.println();
|
||||
#if defined(ESP32) || defined(EMSESP_STANDALONE) || defined(EMSESP_FORCE_SERIAL)
|
||||
serial_console_.begin(SERIAL_CONSOLE_BAUD_RATE);
|
||||
serial_console_.println();
|
||||
|
||||
shell = std::make_shared<EMSESPStreamConsole>(serial_console_, true);
|
||||
shell->maximum_log_messages(100); // default is 50
|
||||
shell->start();
|
||||
shell->log_level(uuid::log::Level::DEBUG); // order is: err, warning, notice, info, debug, trace, all
|
||||
shell = std::make_shared<EMSESPStreamConsole>(serial_console_, true);
|
||||
shell->maximum_log_messages(100); // default is 50
|
||||
shell->start();
|
||||
shell->log_level(uuid::log::Level::DEBUG); // order is: err, warning, notice, info, debug, trace, all
|
||||
#endif
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
shell->add_flags(CommandFlags::ADMIN);
|
||||
shell->add_flags(CommandFlags::ADMIN);
|
||||
#endif
|
||||
}
|
||||
|
||||
// always start the telnet service, except on an ESP8266
|
||||
// default idle is 10 minutes, default write timeout is 0 (automatic)
|
||||
|
||||
@@ -28,9 +28,7 @@
|
||||
#include <uuid/log.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "settings.h"
|
||||
#include "system.h"
|
||||
#include "network.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
using uuid::flash_string_vector;
|
||||
@@ -39,8 +37,6 @@ using uuid::console::Commands;
|
||||
using uuid::console::Shell;
|
||||
using uuid::log::Level;
|
||||
|
||||
// clang-format off
|
||||
|
||||
#define LOG_DEBUG(...) logger_.debug(__VA_ARGS__)
|
||||
#define LOG_INFO(...) logger_.info(__VA_ARGS__)
|
||||
#define LOG_TRACE(...) logger_.trace(__VA_ARGS__)
|
||||
@@ -48,10 +44,10 @@ using uuid::log::Level;
|
||||
#define LOG_WARNING(...) logger_.warning(__VA_ARGS__)
|
||||
#define LOG_ERROR(...) logger_.err(__VA_ARGS__)
|
||||
|
||||
// clang-format off
|
||||
#define MAKE_PSTR(string_name, string_literal) static const char __pstr__##string_name[] __attribute__((__aligned__(sizeof(int)))) PROGMEM = string_literal;
|
||||
#define MAKE_PSTR_WORD(string_name) MAKE_PSTR(string_name, #string_name)
|
||||
#define F_(string_name) FPSTR(__pstr__##string_name)
|
||||
|
||||
// clang-format on
|
||||
|
||||
// common words
|
||||
@@ -82,19 +78,25 @@ MAKE_PSTR_WORD(version)
|
||||
MAKE_PSTR_WORD(values)
|
||||
MAKE_PSTR_WORD(system)
|
||||
MAKE_PSTR_WORD(refresh)
|
||||
MAKE_PSTR_WORD(change)
|
||||
MAKE_PSTR_WORD(disconnect)
|
||||
MAKE_PSTR_WORD(debug)
|
||||
MAKE_PSTR_WORD(restart)
|
||||
MAKE_PSTR_WORD(reconnect)
|
||||
MAKE_PSTR_WORD(format)
|
||||
MAKE_PSTR_WORD(raw)
|
||||
MAKE_PSTR_WORD(watch)
|
||||
|
||||
// context menus
|
||||
MAKE_PSTR_WORD(mqtt)
|
||||
MAKE_PSTR_WORD(send)
|
||||
MAKE_PSTR_WORD(telegram)
|
||||
MAKE_PSTR_WORD(bus_id)
|
||||
MAKE_PSTR_WORD(tx_mode)
|
||||
MAKE_PSTR_WORD(ems)
|
||||
MAKE_PSTR_WORD(devices)
|
||||
|
||||
MAKE_PSTR(deep_optional, "[deep]")
|
||||
MAKE_PSTR(tx_mode_fmt, "Tx mode = %d")
|
||||
MAKE_PSTR(bus_id_fmt, "Bus ID = %02X")
|
||||
MAKE_PSTR(watchid_optional, "[ID]")
|
||||
MAKE_PSTR(watch_format_mandatory, "<off | on | raw>")
|
||||
MAKE_PSTR(invalid_watch, "Invalid watch type")
|
||||
MAKE_PSTR(data_mandatory, "<\"XX XX ...\">")
|
||||
MAKE_PSTR(percent, "%")
|
||||
MAKE_PSTR(degrees, "°C")
|
||||
MAKE_PSTR(degrees_mandatory, "<degrees>")
|
||||
@@ -106,8 +108,6 @@ MAKE_PSTR(typeid_mandatory, "<type ID>")
|
||||
MAKE_PSTR(deviceid_mandatory, "<device ID>")
|
||||
MAKE_PSTR(deviceid_optional, "[device ID]")
|
||||
MAKE_PSTR(invalid_log_level, "Invalid log level")
|
||||
MAKE_PSTR(ip_address_optional, "[IP address]")
|
||||
MAKE_PSTR(ip_address_mandatory, "<IP address>")
|
||||
MAKE_PSTR(port_mandatory, "<port>")
|
||||
MAKE_PSTR(log_level_fmt, "Log level = %s")
|
||||
MAKE_PSTR(log_level_optional, "[level]")
|
||||
@@ -116,8 +116,6 @@ MAKE_PSTR(name_optional, "[name]")
|
||||
MAKE_PSTR(new_password_prompt1, "Enter new password: ")
|
||||
MAKE_PSTR(new_password_prompt2, "Retype new password: ")
|
||||
MAKE_PSTR(password_prompt, "Password: ")
|
||||
MAKE_PSTR(seconds_optional, "[seconds]")
|
||||
MAKE_PSTR(seconds_mandatory, "<seconds>")
|
||||
MAKE_PSTR(unset, "<unset>")
|
||||
|
||||
#ifdef LOCAL
|
||||
@@ -143,10 +141,10 @@ enum ShellContext : uint8_t {
|
||||
|
||||
MAIN = 0,
|
||||
SYSTEM,
|
||||
EMS,
|
||||
MQTT,
|
||||
BOILER,
|
||||
THERMOSTAT
|
||||
THERMOSTAT,
|
||||
SOLAR,
|
||||
MIXING
|
||||
|
||||
};
|
||||
|
||||
@@ -173,8 +171,9 @@ class EMSESPShell : virtual public uuid::console::Shell {
|
||||
bool exit_context() override;
|
||||
|
||||
private:
|
||||
void add_console_commands();
|
||||
bool _console_commands_loaded = false; // set to true when the initial commands are loaded
|
||||
void add_console_commands();
|
||||
bool console_commands_loaded_ = false; // set to true when the initial commands are loaded
|
||||
std::string console_hostname_;
|
||||
};
|
||||
|
||||
class EMSESPStreamConsole : public uuid::console::StreamConsole, public EMSESPShell {
|
||||
|
||||
@@ -793,7 +793,7 @@ void Boiler::console_commands(Shell & shell, unsigned int context) {
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::BOILER,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(change), F_(wwtemp)},
|
||||
flash_string_vector{F_(wwtemp)},
|
||||
flash_string_vector{F_(degrees_mandatory)},
|
||||
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
|
||||
set_warmwater_temp(Helpers::atoint(arguments.front().c_str()));
|
||||
@@ -801,7 +801,7 @@ void Boiler::console_commands(Shell & shell, unsigned int context) {
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::BOILER,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(change), F_(flowtemp)},
|
||||
flash_string_vector{F_(flowtemp)},
|
||||
flash_string_vector{F_(degrees_mandatory)},
|
||||
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
|
||||
set_flow_temp(Helpers::atoint(arguments.front().c_str()));
|
||||
@@ -810,7 +810,7 @@ void Boiler::console_commands(Shell & shell, unsigned int context) {
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::BOILER,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(change), F_(wwactive)},
|
||||
flash_string_vector{F_(wwactive)},
|
||||
flash_string_vector{F_(bool_mandatory)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments[0] == read_flash_string(F_(on))) {
|
||||
@@ -829,7 +829,7 @@ void Boiler::console_commands(Shell & shell, unsigned int context) {
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::BOILER,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(change), F_(wwonetime)},
|
||||
flash_string_vector{F_(wwonetime)},
|
||||
flash_string_vector{F_(bool_mandatory)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments[0] == read_flash_string(F_(on))) {
|
||||
@@ -848,7 +848,7 @@ void Boiler::console_commands(Shell & shell, unsigned int context) {
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::BOILER,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(change), F_(wwcirculation)},
|
||||
flash_string_vector{F_(wwcirculation)},
|
||||
flash_string_vector{F_(bool_mandatory)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments[0] == read_flash_string(F_(on))) {
|
||||
@@ -867,7 +867,7 @@ void Boiler::console_commands(Shell & shell, unsigned int context) {
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::BOILER,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(change), F_(comfort)},
|
||||
flash_string_vector{F_(comfort)},
|
||||
flash_string_vector{F_(comfort_mandatory)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments[0] == read_flash_string(F_(hot))) {
|
||||
@@ -889,65 +889,6 @@ void Boiler::console_commands(Shell & shell, unsigned int context) {
|
||||
flash_string_vector{F_(show)},
|
||||
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { show_values(shell); });
|
||||
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::BOILER,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(shower), F_(timer)},
|
||||
flash_string_vector{F_(bool_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
bool value;
|
||||
if (arguments[0] == read_flash_string(F_(on))) {
|
||||
value = true;
|
||||
} else if (arguments[0] == read_flash_string(F_(off))) {
|
||||
value = false;
|
||||
} else {
|
||||
shell.println(F("Must be on or off"));
|
||||
return;
|
||||
}
|
||||
Settings settings;
|
||||
settings.shower_timer(value);
|
||||
settings.commit();
|
||||
shell.printfln(F_(shower_timer_fmt), settings.shower_timer() ? uuid::read_flash_string(F_(on)).c_str() : uuid::read_flash_string(F_(off)).c_str());
|
||||
},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
|
||||
return std::vector<std::string>{read_flash_string(F_(on)), read_flash_string(F_(off))};
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::BOILER,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(shower), F_(alert)},
|
||||
flash_string_vector{F_(bool_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
bool value;
|
||||
if (arguments[0] == read_flash_string(F_(on))) {
|
||||
value = true;
|
||||
} else if (arguments[0] == read_flash_string(F_(off))) {
|
||||
value = false;
|
||||
} else {
|
||||
shell.println(F("Must be on or off"));
|
||||
return;
|
||||
}
|
||||
Settings settings;
|
||||
settings.shower_alert(value);
|
||||
settings.commit();
|
||||
shell.printfln(F_(shower_timer_fmt), settings.shower_alert() ? uuid::read_flash_string(F_(on)).c_str() : uuid::read_flash_string(F_(off)).c_str());
|
||||
},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
|
||||
return std::vector<std::string>{read_flash_string(F_(on)), read_flash_string(F_(off))};
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::BOILER,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(set)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
Settings settings;
|
||||
shell.printfln(F_(shower_timer_fmt), settings.shower_timer() ? F_(enabled) : F_(disabled));
|
||||
shell.printfln(F_(shower_alert_fmt), settings.shower_alert() ? F_(enabled) : F_(disabled));
|
||||
shell.println();
|
||||
});
|
||||
|
||||
|
||||
// enter the context
|
||||
Console::enter_custom_context(shell, context);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
#include "connect.h"
|
||||
|
||||
// MAKE_PSTR_WORD(connect)
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
REGISTER_FACTORY(Connect, EMSdevice::DeviceType::CONNECT);
|
||||
@@ -33,7 +31,7 @@ Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, con
|
||||
// register_telegram_type(EMS_TYPE_XX, "XX", false, std::bind(&Controller::process_XX, this, _1));
|
||||
|
||||
// MQTT callbacks
|
||||
// register_mqtt_topic("cmd", std::bind(&Controller::cmd, this, _1));
|
||||
// register_mqtt_topic("cmd", std::bind(&Connect::cmd, this, _1));
|
||||
}
|
||||
|
||||
void Connect::add_context_menu() {
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include "emsdevice.h"
|
||||
#include "telegram.h"
|
||||
#include "emsesp.h"
|
||||
#include "helpers.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include "emsdevice.h"
|
||||
#include "telegram.h"
|
||||
#include "emsesp.h"
|
||||
#include "helpers.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ Gateway::Gateway(uint8_t device_type, uint8_t device_id, uint8_t product_id, con
|
||||
// register_telegram_type(EMS_TYPE_XX, "XX", false, std::bind(&Controller::process_XX, this, _1));
|
||||
|
||||
// MQTT callbacks
|
||||
// register_mqtt_topic("cmd", std::bind(&Controller::cmd, this, _1));
|
||||
// register_mqtt_topic("cmd", std::bind(&Gateway::cmd, this, _1));
|
||||
}
|
||||
|
||||
void Gateway::add_context_menu() {
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include "emsdevice.h"
|
||||
#include "telegram.h"
|
||||
#include "emsesp.h"
|
||||
#include "helpers.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include "emsdevice.h"
|
||||
#include "telegram.h"
|
||||
#include "emsesp.h"
|
||||
#include "helpers.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include "emsdevice.h"
|
||||
#include "telegram.h"
|
||||
#include "emsesp.h"
|
||||
#include "helpers.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
|
||||
@@ -73,6 +73,9 @@ void Solar::show_values(uuid::console::Shell & shell) {
|
||||
shell.printfln(F(" Pump working time: %d days %d hours %d minutes"), pumpWorkMin_ / 1440, (pumpWorkMin_ % 1440) / 60, pumpWorkMin_ % 60);
|
||||
}
|
||||
|
||||
print_value(shell, 2, F("Tank Heated"), tankHeated_, nullptr, EMS_VALUE_BOOL);
|
||||
print_value(shell, 2, F("Collector"), collectorOnOff_, nullptr, EMS_VALUE_BOOL);
|
||||
|
||||
print_value(shell, 2, F("Energy last hour"), energyLastHour_, F_(wh), 10);
|
||||
print_value(shell, 2, F("Energy today"), energyToday_, F_(wh));
|
||||
print_value(shell, 2, F("Energy total"), energyTotal_, F_(kwh), 10);
|
||||
@@ -87,30 +90,47 @@ void Solar::publish_values() {
|
||||
if (Helpers::hasValue(collectorTemp_)) {
|
||||
doc["collectortemp"] = (float)collectorTemp_ / 10;
|
||||
}
|
||||
|
||||
if (Helpers::hasValue(bottomTemp_)) {
|
||||
doc["bottomtemp"] = (float)bottomTemp_ / 10;
|
||||
}
|
||||
|
||||
if (Helpers::hasValue(bottomTemp2_)) {
|
||||
doc["bottomtemp2"] = (float)bottomTemp2_ / 10;
|
||||
}
|
||||
|
||||
if (Helpers::hasValue(pumpModulation_)) {
|
||||
doc["pumpmodulation"] = pumpModulation_;
|
||||
}
|
||||
|
||||
if (Helpers::hasValue(pump_, true)) {
|
||||
doc["pump"] = Helpers::render_value(s, pump_, EMS_VALUE_BOOL);
|
||||
}
|
||||
|
||||
if (Helpers::hasValue(valveStatus_, true)) {
|
||||
doc["valvestatus"] = Helpers::render_value(s, valveStatus_, EMS_VALUE_BOOL);
|
||||
}
|
||||
|
||||
if (Helpers::hasValue(pumpWorkMin_)) {
|
||||
doc["pumpWorkMin"] = (float)pumpWorkMin_;
|
||||
}
|
||||
|
||||
if (Helpers::hasValue(tankHeated_, true)) {
|
||||
doc["tankHeated"] = Helpers::render_value(s, tankHeated_, EMS_VALUE_BOOL);
|
||||
}
|
||||
|
||||
if (Helpers::hasValue(collectorOnOff_, true)) {
|
||||
doc["collectorOnOff"] = Helpers::render_value(s, collectorOnOff_, EMS_VALUE_BOOL);
|
||||
}
|
||||
|
||||
if (Helpers::hasValue(energyLastHour_)) {
|
||||
doc["energylasthour"] = (float)energyLastHour_ / 10;
|
||||
}
|
||||
|
||||
if (Helpers::hasValue(energyToday_)) {
|
||||
doc["energytoday"] = energyToday_;
|
||||
}
|
||||
|
||||
if (Helpers::hasValue(energyTotal_)) {
|
||||
doc["energytotal"] = (float)energyTotal_ / 10;
|
||||
}
|
||||
@@ -186,6 +206,9 @@ void Solar::process_SM100Status(std::shared_ptr<const Telegram> telegram) {
|
||||
if (pumpmod == 0 && pumpModulation_ == 100) { // mask out boosts
|
||||
pumpModulation_ = 15; // set to minimum
|
||||
}
|
||||
|
||||
telegram->read_bitvalue(tankHeated_, 3, 1); // issue #422
|
||||
telegram->read_bitvalue(collectorOnOff_, 3, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -221,12 +244,11 @@ void Solar::process_ISM1StatusMessage(std::shared_ptr<const Telegram> telegram)
|
||||
if (Wh != 0xFFFF) {
|
||||
energyLastHour_ = Wh * 10; // set to *10
|
||||
}
|
||||
telegram->read_bitvalue(pump_, 8, 0); // Solar pump on (1) or off (0)
|
||||
telegram->read_value(pumpWorkMin_, 10, 3); // force to 3 bytes
|
||||
telegram->read_bitvalue(collectorOnOff_, 9, 0); // collector on/off
|
||||
telegram->read_bitvalue(tankHeated_, 9, 2); // tank full
|
||||
}
|
||||
|
||||
/*
|
||||
* Junkers ISM1 Solar Module - type 0x0101 EMS+ for setting values
|
||||
* e.g. 90 30 FF 06 00 01 50
|
||||
*/
|
||||
void Solar::process_ISM1Set(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include "emsdevice.h"
|
||||
#include "telegram.h"
|
||||
#include "emsesp.h"
|
||||
#include "helpers.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
@@ -57,6 +56,8 @@ class Solar : public EMSdevice {
|
||||
uint32_t energyToday_ = EMS_VALUE_ULONG_NOTSET;
|
||||
uint32_t energyTotal_ = EMS_VALUE_ULONG_NOTSET;
|
||||
uint32_t pumpWorkMin_ = EMS_VALUE_ULONG_NOTSET; // Total solar pump operating time
|
||||
uint8_t tankHeated_ = EMS_VALUE_BOOL_NOTSET;
|
||||
uint8_t collectorOnOff_ = EMS_VALUE_BOOL_NOTSET;
|
||||
|
||||
uint8_t availabilityFlag_ = EMS_VALUE_BOOL_NOTSET;
|
||||
uint8_t configFlag_ = EMS_VALUE_BOOL_NOTSET;
|
||||
|
||||
@@ -33,7 +33,7 @@ Switch::Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const
|
||||
// register_telegram_type(EMS_TYPE_XX, "XX", false, std::bind(&Controller::process_XX, this, _1));
|
||||
|
||||
// MQTT callbacks
|
||||
// register_mqtt_topic("cmd", std::bind(&Controller::cmd, this, _1));
|
||||
// register_mqtt_topic("cmd", std::bind(&Switch::cmd, this, _1));
|
||||
}
|
||||
|
||||
void Switch::add_context_menu() {
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include "emsdevice.h"
|
||||
#include "telegram.h"
|
||||
#include "emsesp.h"
|
||||
#include "helpers.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
|
||||
@@ -126,11 +126,17 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
}
|
||||
}
|
||||
|
||||
Settings settings;
|
||||
uint8_t master_thermostat = settings.master_thermostat(); // what the user has defined
|
||||
uint8_t actual_master_thermostat = EMSESP::actual_master_thermostat(); // what we're actually using
|
||||
uint8_t num_devices = EMSESP::count_devices(EMSdevice::DeviceType::THERMOSTAT) + 1; // including this thermostat
|
||||
mqtt_format_ = settings.mqtt_format(); // single, nested or ha
|
||||
uint8_t master_thermostat = 0;
|
||||
EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) {
|
||||
master_thermostat = settings.master_thermostat; // what the user has defined
|
||||
});
|
||||
|
||||
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) {
|
||||
mqtt_format_ = settings.mqtt_format; // single, nested or ha
|
||||
});
|
||||
|
||||
uint8_t actual_master_thermostat = EMSESP::actual_master_thermostat(); // what we're actually using
|
||||
uint8_t num_devices = EMSESP::count_devices(EMSdevice::DeviceType::THERMOSTAT) + 1; // including this thermostat
|
||||
|
||||
// if we're on auto mode, register this thermostat if it has a device id of 0x10 or 0x17
|
||||
// or if its the master thermostat we defined
|
||||
@@ -161,7 +167,7 @@ void Thermostat::init_mqtt() {
|
||||
|
||||
// if the MQTT format type is ha then send the config to HA (via the mqtt discovery service)
|
||||
// for each of the heating circuits
|
||||
if (mqtt_format_ == Settings::MQTT_format::HA) {
|
||||
if (mqtt_format_ == MQTT_format::HA) {
|
||||
for (uint8_t hc = 0; hc < monitor_typeids.size(); hc++) {
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
|
||||
|
||||
@@ -304,7 +310,8 @@ void Thermostat::thermostat_cmd(const char * message) {
|
||||
std::string holiday = doc[hc_name]["holiday"];
|
||||
set_holiday(holiday.c_str(), hc_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nullptr != doc["wwmode"]) {
|
||||
std::string mode = doc["wwmode"];
|
||||
set_ww_mode(mode);
|
||||
@@ -502,7 +509,7 @@ void Thermostat::thermostat_cmd(const char * message) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // namespace emsesp
|
||||
|
||||
void Thermostat::thermostat_cmd_temp(const char * message) {
|
||||
float f = strtof((char *)message, 0);
|
||||
@@ -559,8 +566,7 @@ void Thermostat::publish_values() {
|
||||
JsonObject dataThermostat;
|
||||
|
||||
// add external temp
|
||||
if ((flags == EMS_DEVICE_FLAG_RC35 || flags == EMS_DEVICE_FLAG_RC30_1)
|
||||
&& (mqtt_format_ == Settings::MQTT_format::SINGLE || mqtt_format_ == Settings::MQTT_format::CUSTOM)) {
|
||||
if ((flags == EMS_DEVICE_FLAG_RC35 || flags == EMS_DEVICE_FLAG_RC30_1) && (mqtt_format_ == MQTT_format::SINGLE || mqtt_format_ == MQTT_format::CUSTOM)) {
|
||||
if (datetime_.size()) {
|
||||
rootThermostat["time"] = datetime_.c_str();
|
||||
}
|
||||
@@ -597,7 +603,7 @@ void Thermostat::publish_values() {
|
||||
rootThermostat["wwmode"] = "auto";
|
||||
}
|
||||
}
|
||||
if (mqtt_format_ == Settings::MQTT_format::SINGLE) {
|
||||
if (mqtt_format_ == MQTT_format::SINGLE) {
|
||||
Mqtt::publish("thermostat_data", doc);
|
||||
rootThermostat = doc.to<JsonObject>(); // clear object
|
||||
}
|
||||
@@ -611,7 +617,7 @@ void Thermostat::publish_values() {
|
||||
|
||||
has_data = true;
|
||||
// if the MQTT format is 'nested' or 'ha' then create the parent object hc<n>
|
||||
if (mqtt_format_ != Settings::MQTT_format::SINGLE) {
|
||||
if (mqtt_format_ != MQTT_format::SINGLE) {
|
||||
// create nested json for each HC
|
||||
char hc_name[10]; // hc{1-4}
|
||||
strlcpy(hc_name, "hc", 10);
|
||||
@@ -674,10 +680,10 @@ void Thermostat::publish_values() {
|
||||
}
|
||||
|
||||
// when using HA always send the mode otherwise it'll break the component/widget and report an error
|
||||
if ((Helpers::hasValue(hc->mode)) || (mqtt_format_ == Settings::MQTT_format::HA)) {
|
||||
if ((Helpers::hasValue(hc->mode)) || (mqtt_format_ == MQTT_format::HA)) {
|
||||
uint8_t hc_mode = hc->get_mode(flags);
|
||||
// if we're sending to HA the only valid mode types are heat, auto and off
|
||||
if (mqtt_format_ == Settings::MQTT_format::HA) {
|
||||
if (mqtt_format_ == MQTT_format::HA) {
|
||||
if ((hc_mode == HeatingCircuit::Mode::MANUAL) || (hc_mode == HeatingCircuit::Mode::DAY)) {
|
||||
hc_mode = HeatingCircuit::Mode::HEAT;
|
||||
} else if ((hc_mode == HeatingCircuit::Mode::NIGHT) || (hc_mode == HeatingCircuit::Mode::OFF)) {
|
||||
@@ -700,7 +706,7 @@ void Thermostat::publish_values() {
|
||||
}
|
||||
|
||||
// if format is single, send immediately and clear object for next hc
|
||||
if (mqtt_format_ == Settings::MQTT_format::SINGLE) {
|
||||
if (mqtt_format_ == MQTT_format::SINGLE) {
|
||||
char topic[30];
|
||||
char s[3]; // for formatting strings
|
||||
strlcpy(topic, "thermostat_data", 30);
|
||||
@@ -715,11 +721,11 @@ void Thermostat::publish_values() {
|
||||
}
|
||||
|
||||
// if we're using nested json, send all in one go
|
||||
if (mqtt_format_ == Settings::MQTT_format::NESTED) {
|
||||
if (mqtt_format_ == MQTT_format::NESTED) {
|
||||
Mqtt::publish("thermostat_data", doc);
|
||||
} else if (mqtt_format_ == Settings::MQTT_format::HA) {
|
||||
} else if (mqtt_format_ == MQTT_format::HA) {
|
||||
Mqtt::publish("homeassistant/climate/ems-esp/state", doc);
|
||||
} else if (mqtt_format_ == Settings::MQTT_format::CUSTOM) {
|
||||
} else if (mqtt_format_ == MQTT_format::CUSTOM) {
|
||||
Mqtt::publish("thermostat_data", doc);
|
||||
}
|
||||
}
|
||||
@@ -727,7 +733,6 @@ void Thermostat::publish_values() {
|
||||
// returns the heating circuit object based on the hc number
|
||||
// of nullptr if it doesn't exist yet
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(const uint8_t hc_num) {
|
||||
// uint8_t hc = (hc_num) ? hc_num : DEFAULT_HEATING_CIRCUIT;
|
||||
if (hc_num == 0) {
|
||||
// return first existing hc
|
||||
for (const auto & heating_circuit : heating_circuits_) {
|
||||
@@ -1454,16 +1459,16 @@ void Thermostat::set_party(const uint8_t hrs, const uint8_t hc_num) {
|
||||
|
||||
// set date&time as string hh:mm:ss-dd.mm.yyyy-dw-dst
|
||||
// dw - day of week (0..6), dst- summertime (0/1)
|
||||
void Thermostat::set_datetime(const char * dt) {
|
||||
void Thermostat::set_datetime(const char * dt) {
|
||||
uint8_t data[9];
|
||||
data[0] = (dt[16] - '0') * 100 + (dt[17] - '0') * 10 + (dt[18] - '0'); // year
|
||||
data[1] = (dt[12] - '0') * 10 + (dt[13] - '0'); // month
|
||||
data[2] = (dt[0] - '0') * 10 + (dt[1] - '0'); // hour
|
||||
data[3] = (dt[9] - '0') * 10 + (dt[10] - '0'); // day
|
||||
data[4] = (dt[3] - '0') * 10 + (dt[4] - '0'); // min
|
||||
data[5] = (dt[6] - '0') * 10 + (dt[7] - '0'); // sec
|
||||
data[6] = (dt[20] - '0'); // day of week
|
||||
data[7] = (dt[22] - '0'); // summerime
|
||||
data[1] = (dt[12] - '0') * 10 + (dt[13] - '0'); // month
|
||||
data[2] = (dt[0] - '0') * 10 + (dt[1] - '0'); // hour
|
||||
data[3] = (dt[9] - '0') * 10 + (dt[10] - '0'); // day
|
||||
data[4] = (dt[3] - '0') * 10 + (dt[4] - '0'); // min
|
||||
data[5] = (dt[6] - '0') * 10 + (dt[7] - '0'); // sec
|
||||
data[6] = (dt[20] - '0'); // day of week
|
||||
data[7] = (dt[22] - '0'); // summerime
|
||||
if ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC35 || (flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) {
|
||||
LOG_INFO(F("Setting date and time"));
|
||||
write_command(6, 0, data, 8, 0);
|
||||
@@ -1490,8 +1495,8 @@ void Thermostat::set_mode(const std::string & mode, const uint8_t hc_num) {
|
||||
set_mode(HeatingCircuit::Mode::NOFROST, hc_num);
|
||||
} else if (mode_tostring(HeatingCircuit::Mode::ECO) == mode) {
|
||||
set_mode(HeatingCircuit::Mode::ECO, hc_num);
|
||||
// } else if (mode_tostring(HeatingCircuit::Mode::HOLIDAY) == mode) {
|
||||
// set_mode(HeatingCircuit::Mode::HOLIDAY, hc_num);
|
||||
} else if (mode_tostring(HeatingCircuit::Mode::HOLIDAY) == mode) {
|
||||
set_mode(HeatingCircuit::Mode::HOLIDAY, hc_num);
|
||||
} else if (mode_tostring(HeatingCircuit::Mode::COMFORT) == mode) {
|
||||
set_mode(HeatingCircuit::Mode::COMFORT, hc_num);
|
||||
} else {
|
||||
@@ -1509,7 +1514,7 @@ void Thermostat::set_mode(const uint8_t mode, const uint8_t hc_num) {
|
||||
// get hc based on number
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
|
||||
if (hc == nullptr) {
|
||||
LOG_WARNING(F("Set mode: Heating Circuit %d not found or activated"), hc_num);
|
||||
LOG_WARNING(F("set mode: Heating Circuit %d not found or activated"), hc_num);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1593,7 +1598,7 @@ void Thermostat::set_mode(const uint8_t mode, const uint8_t hc_num) {
|
||||
|
||||
// add the write command to the Tx queue
|
||||
// post validate is the corresponding monitor or set type IDs as they can differ per model
|
||||
write_command(set_typeids[hc_p], offset, set_mode_value, validate_typeid);
|
||||
write_command(set_typeids[hc->hc_num() - 1], offset, set_mode_value, validate_typeid);
|
||||
}
|
||||
|
||||
// sets the thermostat temp, where mode is a string
|
||||
@@ -1641,7 +1646,7 @@ void Thermostat::set_temperature(const float temperature, const uint8_t mode, co
|
||||
|
||||
uint8_t model = flags() & 0x0F;
|
||||
int8_t offset = -1; // we use -1 to check if there is a value
|
||||
uint8_t factor = 2; // some temperatures only use 1
|
||||
uint8_t factor = 2; // some temperatures only use 1
|
||||
uint16_t validate_typeid = monitor_typeids[hc->hc_num() - 1];
|
||||
|
||||
if (model == EMS_DEVICE_FLAG_RC10) {
|
||||
@@ -1771,6 +1776,7 @@ void Thermostat::set_temperature(const float temperature, const uint8_t mode, co
|
||||
mode_tostring(mode).c_str());
|
||||
|
||||
// add the write command to the Tx queue
|
||||
// value is *2
|
||||
// post validate is the corresponding monitor or set type IDs as they can differ per model
|
||||
write_command(set_typeids[hc->hc_num() - 1], offset, (uint8_t)((float)temperature * (float)factor), validate_typeid);
|
||||
}
|
||||
@@ -1789,14 +1795,17 @@ void Thermostat::console_commands(Shell & shell, unsigned int context) {
|
||||
} else {
|
||||
value = Helpers::hextoint(arguments.front().c_str());
|
||||
}
|
||||
Settings settings;
|
||||
settings.master_thermostat(value);
|
||||
settings.commit();
|
||||
EMSESP::actual_master_thermostat(value); // set the internal value too
|
||||
char buffer[5];
|
||||
shell.printfln(F_(master_thermostat_fmt),
|
||||
settings.master_thermostat() == 0 ? uuid::read_flash_string(F_(auto)).c_str()
|
||||
: Helpers::hextoa(buffer, settings.master_thermostat()));
|
||||
|
||||
EMSESP::emsespSettingsService.update(
|
||||
[&](EMSESPSettings & settings) {
|
||||
settings.master_thermostat = value;
|
||||
EMSESP::actual_master_thermostat(value); // set the internal value too
|
||||
char buffer[5];
|
||||
shell.printfln(F_(master_thermostat_fmt),
|
||||
!value ? uuid::read_flash_string(F_(auto)).c_str() : Helpers::hextoa(buffer, value));
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::THERMOSTAT,
|
||||
@@ -1810,7 +1819,7 @@ void Thermostat::console_commands(Shell & shell, unsigned int context) {
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::THERMOSTAT,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(change), F_(temp)},
|
||||
flash_string_vector{F_(temp)},
|
||||
flash_string_vector{F_(degrees_mandatory), F_(hc_optional), F_(mode_optional)},
|
||||
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
|
||||
uint8_t hc = (arguments.size() >= 2) ? arguments[1].at(0) - '0' : AUTO_HEATING_CIRCUIT;
|
||||
@@ -1826,7 +1835,7 @@ void Thermostat::console_commands(Shell & shell, unsigned int context) {
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::THERMOSTAT,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(change), F_(mode)},
|
||||
flash_string_vector{F_(mode)},
|
||||
flash_string_vector{F_(mode_mandatory), F_(hc_optional)},
|
||||
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
|
||||
uint8_t hc = (arguments.size() == 2) ? arguments[1].at(0) - '0' : AUTO_HEATING_CIRCUIT;
|
||||
@@ -1850,12 +1859,11 @@ void Thermostat::console_commands(Shell & shell, unsigned int context) {
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::THERMOSTAT,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(change), F_(wwmode)},
|
||||
flash_string_vector{F_(wwmode)},
|
||||
flash_string_vector{F_(mode_mandatory)},
|
||||
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) { set_ww_mode(arguments.front()); },
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
|
||||
return std::vector<std::string>{read_flash_string(F("off")), read_flash_string(F("on")), read_flash_string(F("auto"))
|
||||
};
|
||||
return std::vector<std::string>{read_flash_string(F("off")), read_flash_string(F("on")), read_flash_string(F("auto"))};
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::THERMOSTAT,
|
||||
@@ -1867,12 +1875,13 @@ void Thermostat::console_commands(Shell & shell, unsigned int context) {
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(set)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
Settings settings;
|
||||
char buffer[4];
|
||||
shell.printfln(F_(master_thermostat_fmt),
|
||||
settings.master_thermostat() == 0 ? uuid::read_flash_string(F_(auto)).c_str()
|
||||
: Helpers::hextoa(buffer, settings.master_thermostat()));
|
||||
shell.println();
|
||||
EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) {
|
||||
char buffer[4];
|
||||
shell.printfln(F_(master_thermostat_fmt),
|
||||
settings.master_thermostat == 0 ? uuid::read_flash_string(F_(auto)).c_str()
|
||||
: Helpers::hextoa(buffer, settings.master_thermostat));
|
||||
shell.println();
|
||||
});
|
||||
});
|
||||
|
||||
// enter the context
|
||||
|
||||
@@ -63,7 +63,7 @@ class Thermostat : public EMSdevice {
|
||||
int8_t offsettemp = EMS_VALUE_INT_NOTSET; // heatingcurve offest temp at roomtemp signed!
|
||||
|
||||
uint8_t hc_num() const {
|
||||
return hc_num_;
|
||||
return hc_num_; // 1..10
|
||||
}
|
||||
|
||||
uint8_t get_mode(uint8_t flags) const;
|
||||
@@ -85,7 +85,7 @@ class Thermostat : public EMSdevice {
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t hc_num_;
|
||||
uint8_t hc_num_; // 1..10
|
||||
uint16_t monitor_typeid_;
|
||||
uint16_t set_typeid_;
|
||||
};
|
||||
|
||||
@@ -48,7 +48,7 @@ std::string EMSdevice::brand_to_string() const {
|
||||
break;
|
||||
case EMSdevice::Brand::NO_BRAND:
|
||||
default:
|
||||
return read_flash_string(F("?"));
|
||||
return read_flash_string(F(""));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ bool EMSdevice::handle_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DEBUG(F("Decoding %s"), uuid::read_flash_string(tf.telegram_type_name_).c_str());
|
||||
LOG_DEBUG(F("Received %s"), uuid::read_flash_string(tf.telegram_type_name_).c_str());
|
||||
tf.process_function_(telegram);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -82,6 +82,10 @@ class EMSdevice {
|
||||
version_ = version;
|
||||
}
|
||||
|
||||
inline std::string version() const {
|
||||
return version_;
|
||||
}
|
||||
|
||||
inline void brand(uint8_t brand) {
|
||||
brand_ = brand;
|
||||
}
|
||||
@@ -94,6 +98,10 @@ class EMSdevice {
|
||||
name_ = name;
|
||||
}
|
||||
|
||||
inline std::string name() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
std::string brand_to_string() const;
|
||||
static uint8_t decode_brand(uint8_t value);
|
||||
|
||||
|
||||
383
src/emsesp.cpp
383
src/emsesp.cpp
@@ -18,24 +18,6 @@
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
MAKE_PSTR_WORD(bus_id)
|
||||
MAKE_PSTR_WORD(tx_mode)
|
||||
MAKE_PSTR_WORD(read_only)
|
||||
MAKE_PSTR_WORD(emsbus)
|
||||
MAKE_PSTR_WORD(devices)
|
||||
MAKE_PSTR_WORD(send)
|
||||
MAKE_PSTR_WORD(telegram)
|
||||
|
||||
MAKE_PSTR(deep_optional, "[deep]")
|
||||
MAKE_PSTR(data_mandatory, "<\"XX XX ...\">")
|
||||
MAKE_PSTR(tx_mode_fmt, "Tx mode = %d")
|
||||
MAKE_PSTR(bus_id_fmt, "Bus ID = %02X")
|
||||
MAKE_PSTR(read_only_fmt, "Read-only mode is %s")
|
||||
|
||||
MAKE_PSTR(watchid_optional, "[ID]")
|
||||
MAKE_PSTR(watch_format_mandatory, "<off | on | raw>")
|
||||
MAKE_PSTR(invalid_watch, "Invalid watch type")
|
||||
|
||||
MAKE_PSTR(logger_name, "emsesp")
|
||||
|
||||
namespace emsesp {
|
||||
@@ -43,6 +25,22 @@ namespace emsesp {
|
||||
using DeviceFlags = emsesp::EMSdevice;
|
||||
using DeviceType = emsesp::EMSdevice::DeviceType;
|
||||
|
||||
AsyncWebServer webServer(80);
|
||||
|
||||
#if defined(ESP32)
|
||||
ESP8266React EMSESP::esp8266React(&webServer, &SPIFFS);
|
||||
EMSESPSettingsService EMSESP::emsespSettingsService = EMSESPSettingsService(&webServer, &SPIFFS, EMSESP::esp8266React.getSecurityManager());
|
||||
#else
|
||||
ESP8266React EMSESP::esp8266React(&webServer, &LittleFS);
|
||||
EMSESPSettingsService EMSESP::emsespSettingsService = EMSESPSettingsService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager());
|
||||
#endif
|
||||
|
||||
EMSESPStatusService EMSESP::emsespStatusService = EMSESPStatusService(&webServer, EMSESP::esp8266React.getSecurityManager());
|
||||
|
||||
EMSESPDevicesService EMSESP::emsespDevicesService = EMSESPDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager());
|
||||
|
||||
EMSESPScanDevicesService EMSESP::emsespScanDevicesService = EMSESPScanDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager());
|
||||
|
||||
std::vector<std::unique_ptr<EMSdevice>> EMSESP::emsdevices; // array of all the detected EMS devices
|
||||
std::vector<emsesp::EMSESP::Device_record> EMSESP::device_library_; // libary of all our known EMS devices so far
|
||||
|
||||
@@ -55,7 +53,6 @@ Mqtt EMSESP::mqtt_; // mqtt handler
|
||||
System EMSESP::system_; // core system services
|
||||
Console EMSESP::console_; // telnet and serial console
|
||||
Sensors EMSESP::sensors_; // Dallas sensors
|
||||
Network EMSESP::network_; // WiFi
|
||||
Shower EMSESP::shower_; // Shower logic
|
||||
|
||||
// static/common variables
|
||||
@@ -63,11 +60,10 @@ uint8_t EMSESP::actual_master_thermostat_ = EMSESP_DEFAULT_MASTER_THERMOSTAT; /
|
||||
uint16_t EMSESP::watch_id_ = WATCH_ID_NONE; // for when log is TRACE. 0 means no trace set
|
||||
uint8_t EMSESP::watch_ = 0; // trace off
|
||||
bool EMSESP::tap_water_active_ = false; // for when Boiler states we having running warm water. used in Shower()
|
||||
bool EMSESP::ems_read_only_;
|
||||
uint32_t EMSESP::last_fetch_ = 0;
|
||||
uint32_t EMSESP::last_fetch_ = 0;
|
||||
|
||||
// for a specific EMS device go and request data values
|
||||
// or if device_id is 0 it will fetch from all known devices
|
||||
// or if device_id is 0 it will fetch from all our known and active devices
|
||||
void EMSESP::fetch_device_values(const uint8_t device_id) {
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice) {
|
||||
@@ -110,8 +106,48 @@ void EMSESP::watch_id(uint16_t watch_id) {
|
||||
}
|
||||
}
|
||||
|
||||
// change the tx_mode
|
||||
// resets all counters and bumps the UART
|
||||
void EMSESP::reset_tx(uint8_t const tx_mode) {
|
||||
txservice_.telegram_read_count(0);
|
||||
txservice_.telegram_write_count(0);
|
||||
txservice_.telegram_fail_count(0);
|
||||
if (tx_mode) {
|
||||
EMSuart::stop();
|
||||
EMSuart::start(tx_mode);
|
||||
EMSESP::fetch_device_values();
|
||||
}
|
||||
}
|
||||
|
||||
// return status of bus: connected, connected but Tx is broken, disconnected
|
||||
uint8_t EMSESP::bus_status() {
|
||||
if (!rxservice_.bus_connected()) {
|
||||
return BUS_STATUS_OFFLINE;
|
||||
}
|
||||
|
||||
// check if we have Tx issues.
|
||||
uint32_t total_sent = txservice_.telegram_read_count() + txservice_.telegram_write_count();
|
||||
|
||||
// nothing sent successfully, also no errors - must be ok
|
||||
if ((total_sent == 0) && (txservice_.telegram_fail_count() == 0)) {
|
||||
return BUS_STATUS_CONNECTED;
|
||||
}
|
||||
|
||||
// nothing sent successfully, but have Tx errors
|
||||
if ((total_sent == 0) && (txservice_.telegram_fail_count() != 0)) {
|
||||
return BUS_STATUS_TX_ERRORS;
|
||||
}
|
||||
|
||||
// Tx Failure rate > 5%
|
||||
if (((txservice_.telegram_fail_count() * 100) / total_sent) > EMSbus::EMS_TX_ERROR_LIMIT) {
|
||||
return BUS_STATUS_TX_ERRORS;
|
||||
}
|
||||
|
||||
return BUS_STATUS_CONNECTED;
|
||||
}
|
||||
|
||||
// show the EMS bus status plus both Rx and Tx queues
|
||||
void EMSESP::show_emsbus(uuid::console::Shell & shell) {
|
||||
void EMSESP::show_ems(uuid::console::Shell & shell) {
|
||||
// EMS bus information
|
||||
if (rxservice_.bus_connected()) {
|
||||
uint8_t success_rate = 0;
|
||||
@@ -120,7 +156,7 @@ void EMSESP::show_emsbus(uuid::console::Shell & shell) {
|
||||
}
|
||||
|
||||
shell.printfln(F("EMS Bus info:"));
|
||||
shell.printfln(F(" Tx mode: %d"), Settings().ems_tx_mode());
|
||||
EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) { shell.printfln(F(" Tx mode: %d"), settings.tx_mode); });
|
||||
shell.printfln(F(" Bus protocol: %s"), EMSbus::is_ht3() ? F("HT3") : F("Buderus"));
|
||||
shell.printfln(F(" #telegrams received: %d"), rxservice_.telegram_count());
|
||||
shell.printfln(F(" #read requests sent: %d"), txservice_.telegram_read_count());
|
||||
@@ -174,12 +210,6 @@ void EMSESP::show_emsbus(uuid::console::Shell & shell) {
|
||||
shell.println();
|
||||
}
|
||||
|
||||
// display in the console all the data we have from ems devices and external sensors
|
||||
void EMSESP::show_values(uuid::console::Shell & shell) {
|
||||
show_device_values(shell);
|
||||
show_sensor_values(shell);
|
||||
}
|
||||
|
||||
// show EMS device values
|
||||
void EMSESP::show_device_values(uuid::console::Shell & shell) {
|
||||
if (emsdevices.empty()) {
|
||||
@@ -330,8 +360,6 @@ void EMSESP::process_UBADevices(std::shared_ptr<const Telegram> telegram) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t ems_bus_id = Settings().ems_bus_id();
|
||||
|
||||
// for each byte, check the bits and determine the device_id
|
||||
for (uint8_t data_byte = 0; data_byte < telegram->message_length; data_byte++) {
|
||||
uint8_t next_byte = telegram->message_data[data_byte];
|
||||
@@ -342,7 +370,7 @@ void EMSESP::process_UBADevices(std::shared_ptr<const Telegram> telegram) {
|
||||
uint8_t device_id = ((data_byte + 1) * 8) + bit;
|
||||
// if we haven't already detected this device, request it's version details, unless its us (EMS-ESP)
|
||||
// when the version info is received, it will automagically add the device
|
||||
if ((device_id != ems_bus_id) && !(EMSESP::device_exists(device_id))) {
|
||||
if ((device_id != EMSbus::ems_bus_id()) && !(EMSESP::device_exists(device_id))) {
|
||||
LOG_DEBUG(F("New EMS device detected with ID 0x%02X. Requesting version information."), device_id);
|
||||
send_read_request(EMSdevice::EMS_TYPE_VERSION, device_id);
|
||||
}
|
||||
@@ -593,7 +621,8 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
|
||||
// if we ask ourself at roomcontrol for version e.g. 0B 98 02 00 20
|
||||
Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data);
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_TRACE(F("[DEBUG] Echo after %d ms: %s"), ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str());
|
||||
// get_uptime is only updated once per loop, does not give the right time
|
||||
LOG_DEBUG(F("[DEBUG] Echo after %d ms: %s"), ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str());
|
||||
#endif
|
||||
return; // it's an echo
|
||||
}
|
||||
@@ -643,7 +672,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
|
||||
if (length == 1) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
char s[4];
|
||||
if(first_value & 0x80) {
|
||||
if (first_value & 0x80) {
|
||||
LOG_TRACE(F("[DEBUG] next Poll %s after %d ms"), Helpers::hextoa(s, first_value), ::millis() - rx_time_);
|
||||
// time measurement starts here, use millis because get_uptime is only updated once per loop
|
||||
rx_time_ = ::millis();
|
||||
@@ -677,231 +706,6 @@ void EMSESP::send_raw_telegram(const char * data) {
|
||||
txservice_.send_raw(data);
|
||||
}
|
||||
|
||||
// sets the ems read only flag preventing any Tx from going out
|
||||
void EMSESP::set_ems_read_only() {
|
||||
ems_read_only_ = Settings().ems_read_only();
|
||||
LOG_DEBUG(F("Setting EMS read-only mode to %s"), ems_read_only_ ? F("on") : F("off"));
|
||||
}
|
||||
|
||||
// console commands to add
|
||||
void EMSESP::console_commands(Shell & shell, unsigned int context) {
|
||||
EMSESPShell::commands->add_command(ShellContext::EMS,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show), F_(devices)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { show_devices(shell); });
|
||||
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::EMS,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show), F_(emsbus)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { show_emsbus(shell); });
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::EMS,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
show_devices(shell);
|
||||
show_emsbus(shell);
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::EMS,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show), F_(values)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
EMSESP::show_device_values(shell);
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::EMS,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(refresh)},
|
||||
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.printfln(F("Requesting data from EMS devices"));
|
||||
EMSESP::fetch_device_values();
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::EMS,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(bus_id)},
|
||||
flash_string_vector{F_(deviceid_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint8_t device_id = Helpers::hextoint(arguments.front().c_str());
|
||||
if ((device_id == 0x0B) || (device_id == 0x0D) || (device_id == 0x0A) || (device_id == 0x0F) || (device_id == 0x12)) {
|
||||
Settings settings;
|
||||
settings.ems_bus_id(device_id);
|
||||
settings.commit();
|
||||
shell.printfln(F_(bus_id_fmt), settings.ems_bus_id());
|
||||
} else {
|
||||
shell.println(F("Must be 0B, 0D, 0A, 0F or 12"));
|
||||
}
|
||||
},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
|
||||
return std::vector<std::string>{
|
||||
read_flash_string(F("0B")),
|
||||
read_flash_string(F("0D")),
|
||||
read_flash_string(F("0A")),
|
||||
read_flash_string(F("0F")),
|
||||
read_flash_string(F("12")),
|
||||
|
||||
};
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::EMS,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(tx_mode)},
|
||||
flash_string_vector{F_(n_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint8_t tx_mode = std::strtol(arguments[0].c_str(), nullptr, 10);
|
||||
Settings settings;
|
||||
settings.ems_tx_mode(tx_mode);
|
||||
settings.commit();
|
||||
shell.printfln(F_(tx_mode_fmt), settings.ems_tx_mode());
|
||||
// reset counters
|
||||
txservice_.telegram_read_count(0);
|
||||
txservice_.telegram_write_count(0);
|
||||
txservice_.telegram_fail_count(0);
|
||||
// reset the UART
|
||||
EMSuart::stop();
|
||||
EMSuart::start(tx_mode);
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::EMS,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(read_only)},
|
||||
flash_string_vector{F_(bool_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
Settings settings;
|
||||
if (arguments[0] == read_flash_string(F_(on))) {
|
||||
settings.ems_read_only(true);
|
||||
settings.commit();
|
||||
EMSESP::ems_read_only();
|
||||
} else if (arguments[0] == read_flash_string(F_(off))) {
|
||||
settings.ems_read_only(false);
|
||||
settings.commit();
|
||||
EMSESP::ems_read_only();
|
||||
} else {
|
||||
shell.println(F("Must be on or off"));
|
||||
return;
|
||||
}
|
||||
},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
|
||||
return std::vector<std::string>{read_flash_string(F_(on)), read_flash_string(F_(off))};
|
||||
});
|
||||
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::EMS,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(send), F_(telegram)},
|
||||
flash_string_vector{F_(data_mandatory)},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
|
||||
EMSESP::send_raw_telegram(arguments.front().c_str());
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::EMS,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(scan), F_(devices)},
|
||||
flash_string_vector{F_(deep_optional)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.size() == 0) {
|
||||
EMSESP::send_read_request(EMSdevice::EMS_TYPE_UBADevices, EMSdevice::EMS_DEVICE_ID_BOILER);
|
||||
} else {
|
||||
shell.printfln(F("Performing a deep scan by pinging our device library..."));
|
||||
std::vector<uint8_t> Device_Ids;
|
||||
|
||||
Device_Ids.push_back(0x08); // Boilers - 0x08
|
||||
Device_Ids.push_back(0x38); // HeatPump - 0x38
|
||||
Device_Ids.push_back(0x30); // Solar Module - 0x30
|
||||
Device_Ids.push_back(0x09); // Controllers - 0x09
|
||||
Device_Ids.push_back(0x02); // Connect - 0x02
|
||||
Device_Ids.push_back(0x48); // Gateway - 0x48
|
||||
Device_Ids.push_back(0x20); // Mixing Devices - 0x20
|
||||
Device_Ids.push_back(0x21); // Mixing Devices - 0x21
|
||||
Device_Ids.push_back(0x22); // Mixing Devices - 0x22
|
||||
Device_Ids.push_back(0x23); // Mixing Devices - 0x23
|
||||
Device_Ids.push_back(0x28); // Mixing Devices WW- 0x28
|
||||
Device_Ids.push_back(0x29); // Mixing Devices WW- 0x29
|
||||
Device_Ids.push_back(0x10); // Thermostats - 0x10
|
||||
Device_Ids.push_back(0x17); // Thermostats - 0x17
|
||||
Device_Ids.push_back(0x18); // Thermostat remote - 0x18
|
||||
Device_Ids.push_back(0x19); // Thermostat remote - 0x19
|
||||
Device_Ids.push_back(0x1A); // Thermostat remote - 0x1A
|
||||
Device_Ids.push_back(0x1B); // Thermostat remote - 0x1B
|
||||
Device_Ids.push_back(0x11); // Switches - 0x11
|
||||
|
||||
// send the read command with Version command
|
||||
for (const uint8_t device_id : Device_Ids) {
|
||||
EMSESP::send_read_request(EMSdevice::EMS_TYPE_VERSION, device_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::EMS,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(set)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
Settings settings;
|
||||
shell.printfln(F_(tx_mode_fmt), settings.ems_tx_mode());
|
||||
shell.printfln(F_(bus_id_fmt), settings.ems_bus_id());
|
||||
shell.printfln(F_(read_only_fmt), settings.ems_read_only() ? F_(on) : F_(off));
|
||||
});
|
||||
|
||||
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::EMS,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(watch)},
|
||||
flash_string_vector{F_(watch_format_mandatory), F_(watchid_optional)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
// get raw/pretty
|
||||
if (arguments[0] == read_flash_string(F_(raw))) {
|
||||
emsesp::EMSESP::watch(WATCH_RAW); // raw
|
||||
} else if (arguments[0] == read_flash_string(F_(on))) {
|
||||
emsesp::EMSESP::watch(WATCH_ON); // on
|
||||
} else if (arguments[0] == read_flash_string(F_(off))) {
|
||||
emsesp::EMSESP::watch(WATCH_OFF); // off
|
||||
} else {
|
||||
shell.printfln(F_(invalid_watch));
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t watch_id;
|
||||
if (arguments.size() == 2) {
|
||||
// get the watch_id if its set
|
||||
watch_id = Helpers::hextoint(arguments[1].c_str());
|
||||
} else {
|
||||
watch_id = WATCH_ID_NONE;
|
||||
}
|
||||
|
||||
emsesp::EMSESP::watch_id(watch_id);
|
||||
|
||||
uint8_t watch = emsesp::EMSESP::watch();
|
||||
if (watch == WATCH_OFF) {
|
||||
shell.printfln(F("Watching telegrams is off"));
|
||||
return;
|
||||
}
|
||||
|
||||
// if logging is off, the watch won't show anything, show force it back to INFO
|
||||
if (!logger_.enabled(Level::NOTICE)) {
|
||||
shell.log_level(Level::NOTICE);
|
||||
}
|
||||
|
||||
if (watch == WATCH_ON) {
|
||||
shell.printfln(F("Watching incoming telegrams, displayed in decoded format"));
|
||||
} else {
|
||||
shell.printfln(F("Watching incoming telegrams, displayed as raw bytes")); // WATCH_RAW
|
||||
}
|
||||
|
||||
watch_id = emsesp::EMSESP::watch_id();
|
||||
if (watch_id != WATCH_ID_NONE) {
|
||||
shell.printfln(F("Filtering only telegrams that match a device ID or telegram type of 0x%02X"), watch_id);
|
||||
}
|
||||
});
|
||||
|
||||
// enter the context
|
||||
Console::enter_custom_context(shell, context);
|
||||
}
|
||||
|
||||
// kick off the party, start all the services
|
||||
void EMSESP::start() {
|
||||
// Load our library of known devices
|
||||
@@ -909,40 +713,47 @@ void EMSESP::start() {
|
||||
#include "device_library.h"
|
||||
};
|
||||
|
||||
system_.start();
|
||||
network_.start();
|
||||
console_.start();
|
||||
sensors_.start();
|
||||
txservice_.start();
|
||||
shower_.start();
|
||||
mqtt_.start();
|
||||
#ifdef ESP32
|
||||
SPIFFS.begin(true);
|
||||
#elif defined(ESP8266)
|
||||
LittleFS.begin();
|
||||
#endif
|
||||
|
||||
set_ems_read_only(); // see if we have Tx disabled and set the flag
|
||||
esp8266React.begin(); // starts wifi, ap, ota, security, mqtt services
|
||||
emsespSettingsService.begin(); // load settings
|
||||
console_.start(); // telnet and serial console
|
||||
system_.start(); // starts syslog, uart, sets version, initializes LED. Requires pre-loaded settings.
|
||||
mqtt_.start(EMSESP::esp8266React.getMqttClient()); // mqtt init
|
||||
sensors_.start(); // dallas external sensors
|
||||
shower_.start(); // initialize shower timer and shower alert
|
||||
txservice_.start(); // sets bus ID, sends out request for EMS devices
|
||||
webServer.begin(); // start web server
|
||||
}
|
||||
|
||||
// loop de loop
|
||||
void EMSESP::loop() {
|
||||
// network returns false if an OTA is being carried out
|
||||
// so we disable all services when an OTA is happening
|
||||
if (network_.loop()) {
|
||||
system_.loop(); // does LED and checks system health, and syslog service
|
||||
mqtt_.loop(); // starts mqtt, and sends out anything in the queue
|
||||
rxservice_.loop(); // process what ever is in the rx queue
|
||||
txservice_.loop(); // check that the Tx is all ok
|
||||
shower_.loop(); // check for shower on/off
|
||||
sensors_.loop(); // this will also send out via MQTT
|
||||
console_.loop(); // telnet/serial console
|
||||
#ifndef EMSESP_STANDALONE
|
||||
esp8266React.loop();
|
||||
#endif
|
||||
|
||||
// force a query on the EMS devices to fetch latest data at a set interval (1 min)
|
||||
if ((uuid::get_uptime() - last_fetch_ > EMS_FETCH_FREQUENCY)) {
|
||||
last_fetch_ = uuid::get_uptime();
|
||||
fetch_device_values();
|
||||
}
|
||||
system_.loop(); // does LED and checks system health, and syslog service
|
||||
mqtt_.loop(); // starts mqtt, and sends out anything in the queue
|
||||
rxservice_.loop(); // process what ever is in the rx queue
|
||||
txservice_.loop(); // check that the Tx is all ok
|
||||
shower_.loop(); // check for shower on/off
|
||||
sensors_.loop(); // this will also send out via MQTT
|
||||
console_.loop(); // telnet/serial console
|
||||
|
||||
// helps ease wifi dropouts effecting MQTT and Telnet services
|
||||
// https://github.com/esp8266/Arduino/blob/e721089e601985e633641ab7323f81a84ea0cd1b/cores/esp8266/core_esp8266_wiring.cpp#L41-L57
|
||||
delay(1);
|
||||
// force a query on the EMS devices to fetch latest data at a set interval (1 min)
|
||||
if ((uuid::get_uptime() - last_fetch_ > EMS_FETCH_FREQUENCY)) {
|
||||
last_fetch_ = uuid::get_uptime();
|
||||
fetch_device_values();
|
||||
}
|
||||
|
||||
#if defined(ESP32)
|
||||
delay(1);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
42
src/emsesp.h
42
src/emsesp.h
@@ -33,11 +33,16 @@
|
||||
#include <uuid/telnet.h>
|
||||
#endif
|
||||
|
||||
#include <ESP8266React.h>
|
||||
#include "EMSESPStatusService.h"
|
||||
#include "EMSESPDevicesService.h"
|
||||
#include "EMSESPSettingsService.h"
|
||||
#include "EMSESPScanDevicesService.h"
|
||||
|
||||
#include "emsdevice.h"
|
||||
#include "emsfactory.h"
|
||||
#include "telegram.h"
|
||||
#include "mqtt.h"
|
||||
#include "settings.h"
|
||||
#include "system.h"
|
||||
#include "sensors.h"
|
||||
#include "console.h"
|
||||
@@ -84,15 +89,16 @@ class EMSESP {
|
||||
static uint8_t actual_master_thermostat();
|
||||
static void actual_master_thermostat(const uint8_t device_id);
|
||||
|
||||
static void show_values(uuid::console::Shell & shell);
|
||||
static void show_device_values(uuid::console::Shell & shell);
|
||||
static void show_sensor_values(uuid::console::Shell & shell);
|
||||
|
||||
static void show_devices(uuid::console::Shell & shell);
|
||||
static void show_emsbus(uuid::console::Shell & shell);
|
||||
static void show_ems(uuid::console::Shell & shell);
|
||||
|
||||
static void add_context_menus();
|
||||
|
||||
static void reset_tx(uint8_t const tx_mode);
|
||||
|
||||
static void incoming_telegram(uint8_t * data, const uint8_t length);
|
||||
|
||||
static const std::vector<Sensors::Device> sensor_devices() {
|
||||
@@ -114,6 +120,9 @@ class EMSESP {
|
||||
return watch_;
|
||||
}
|
||||
|
||||
enum Bus_status : uint8_t { BUS_STATUS_CONNECTED = 0, BUS_STATUS_TX_ERRORS, BUS_STATUS_OFFLINE };
|
||||
static uint8_t bus_status();
|
||||
|
||||
static bool tap_water_active() {
|
||||
return tap_water_active_;
|
||||
}
|
||||
@@ -122,27 +131,32 @@ class EMSESP {
|
||||
tap_water_active_ = tap_water_active;
|
||||
}
|
||||
|
||||
static void set_ems_read_only();
|
||||
|
||||
static bool ems_read_only() {
|
||||
return ems_read_only_;
|
||||
}
|
||||
|
||||
static void console_commands(Shell & shell, unsigned int context);
|
||||
|
||||
static void fetch_device_values(const uint8_t device_id = 0);
|
||||
|
||||
static bool add_device(const uint8_t device_id, const uint8_t product_id, std::string & version, const uint8_t brand);
|
||||
|
||||
static std::vector<std::unique_ptr<EMSdevice>> emsdevices;
|
||||
|
||||
// services
|
||||
static Mqtt mqtt_;
|
||||
static System system_;
|
||||
static Network network_;
|
||||
static Sensors sensors_;
|
||||
static Console console_;
|
||||
static Shower shower_;
|
||||
static RxService rxservice_;
|
||||
static TxService txservice_;
|
||||
|
||||
// web controllers
|
||||
static ESP8266React esp8266React;
|
||||
static EMSESPSettingsService emsespSettingsService;
|
||||
static EMSESPStatusService emsespStatusService;
|
||||
static EMSESPDevicesService emsespDevicesService;
|
||||
static EMSESPScanDevicesService emsespScanDevicesService;
|
||||
|
||||
static uuid::log::Logger logger() {
|
||||
return logger_;
|
||||
}
|
||||
|
||||
private:
|
||||
EMSESP() = delete;
|
||||
|
||||
@@ -163,14 +177,12 @@ class EMSESP {
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
static std::vector<std::unique_ptr<EMSdevice>> emsdevices;
|
||||
static std::vector<Device_record> device_library_;
|
||||
static std::vector<Device_record> device_library_;
|
||||
|
||||
static uint8_t actual_master_thermostat_;
|
||||
static uint16_t watch_id_;
|
||||
static uint8_t watch_;
|
||||
static bool tap_water_active_;
|
||||
static bool ems_read_only_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
547
src/mqtt.cpp
547
src/mqtt.cpp
@@ -18,52 +18,27 @@
|
||||
|
||||
#include "mqtt.h"
|
||||
#include "emsesp.h"
|
||||
#include "version.h"
|
||||
|
||||
MAKE_PSTR_WORD(username)
|
||||
MAKE_PSTR_WORD(qos)
|
||||
MAKE_PSTR_WORD(base)
|
||||
MAKE_PSTR_WORD(heartbeat)
|
||||
MAKE_PSTR_WORD(ip)
|
||||
MAKE_PSTR_WORD(port)
|
||||
MAKE_PSTR_WORD(nested)
|
||||
MAKE_PSTR_WORD(single)
|
||||
MAKE_PSTR_WORD(ha)
|
||||
MAKE_PSTR_WORD(custom)
|
||||
MAKE_PSTR_WORD(publish_time)
|
||||
MAKE_PSTR_WORD(publish)
|
||||
MAKE_PSTR_WORD(connected)
|
||||
MAKE_PSTR_WORD(disconnected)
|
||||
|
||||
MAKE_PSTR(mqtt_ip_fmt, "IP = %s")
|
||||
MAKE_PSTR(mqtt_user_fmt, "Username = %s")
|
||||
MAKE_PSTR(mqtt_password_fmt, "Password = %S")
|
||||
MAKE_PSTR(mqtt_port_fmt, "Port = %d")
|
||||
MAKE_PSTR(mqtt_enabled_fmt, "MQTT is %s")
|
||||
MAKE_PSTR(mqtt_base_fmt, "Base = %s")
|
||||
MAKE_PSTR(mqtt_qos_fmt, "QOS = %ld")
|
||||
MAKE_PSTR(mqtt_retain_fmt, "Retain Flag = %s")
|
||||
MAKE_PSTR(mqtt_format_fmt, "Format for JSON = %s")
|
||||
MAKE_PSTR(mqtt_heartbeat_fmt, "Heartbeat = %s")
|
||||
MAKE_PSTR(mqtt_publish_time_fmt, "Publish time = %d seconds")
|
||||
|
||||
MAKE_PSTR(logger_name, "mqtt")
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// exposing static stuff to compiler/linker
|
||||
#ifndef EMSESP_STANDALONE
|
||||
AsyncMqttClient Mqtt::mqttClient_;
|
||||
AsyncMqttClient * Mqtt::mqttClient_;
|
||||
#endif
|
||||
|
||||
// static parameters we make global
|
||||
std::string Mqtt::hostname_;
|
||||
uint8_t Mqtt::mqtt_qos_;
|
||||
uint16_t Mqtt::publish_time_;
|
||||
|
||||
std::vector<Mqtt::MQTTFunction> Mqtt::mqtt_functions_;
|
||||
bool Mqtt::mqtt_retain_;
|
||||
uint8_t Mqtt::mqtt_qos_;
|
||||
std::string Mqtt::mqtt_hostname_; // copy of hostname
|
||||
uint8_t Mqtt::mqtt_format_;
|
||||
std::string Mqtt::mqtt_base_;
|
||||
uint16_t Mqtt::mqtt_publish_fails_ = 0;
|
||||
size_t Mqtt::maximum_mqtt_messages_ = Mqtt::MAX_MQTT_MESSAGES;
|
||||
bool Mqtt::force_publish_ = false;
|
||||
uint16_t Mqtt::mqtt_message_id_ = 0;
|
||||
std::deque<Mqtt::QueuedMqttMessage> Mqtt::mqtt_messages_;
|
||||
char will_topic_[Mqtt::MQTT_TOPIC_MAX_SIZE]; // because MQTT library keeps only char pointer
|
||||
@@ -84,116 +59,6 @@ MqttMessage::MqttMessage(uint8_t operation, const std::string & topic, const std
|
||||
, retain(retain) {
|
||||
}
|
||||
|
||||
// empty queue
|
||||
void Mqtt::flush_message_queue() {
|
||||
mqtt_messages_.clear();
|
||||
mqtt_message_id_ = 0;
|
||||
}
|
||||
|
||||
// restart MQTT services
|
||||
void Mqtt::reconnect() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
mqttClient_.disconnect();
|
||||
#endif
|
||||
LOG_DEBUG(F("Reconnecting..."));
|
||||
}
|
||||
|
||||
// MQTT setup
|
||||
void Mqtt::setup() {
|
||||
// exit if already initialized
|
||||
if (mqtt_start_) {
|
||||
return;
|
||||
}
|
||||
mqtt_start_ = true;
|
||||
|
||||
// get some settings and store them locally. This is also because the asyncmqtt library uses references for char *
|
||||
Settings settings;
|
||||
mqtt_enabled_ = settings.mqtt_enabled();
|
||||
mqtt_hostname_ = settings.hostname();
|
||||
mqtt_base_ = settings.mqtt_base();
|
||||
mqtt_qos_ = settings.mqtt_qos();
|
||||
mqtt_format_ = settings.mqtt_format();
|
||||
mqtt_retain_ = settings.mqtt_retain();
|
||||
mqtt_heartbeat_ = settings.mqtt_heartbeat();
|
||||
mqtt_publish_time_ = settings.mqtt_publish_time() * 1000; // convert to seconds
|
||||
mqtt_ip_ = settings.mqtt_ip();
|
||||
mqtt_user_ = settings.mqtt_user();
|
||||
mqtt_password_ = settings.mqtt_password();
|
||||
mqtt_port_ = settings.mqtt_port();
|
||||
|
||||
// if IP is empty, disable MQTT
|
||||
if (settings.mqtt_ip().empty()) {
|
||||
mqtt_enabled_ = false;
|
||||
}
|
||||
|
||||
init(); // set up call backs. only done once.
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
mqtt_enabled_ = true; // force it on for debugging standalone
|
||||
#else
|
||||
mqttClient_.setServer(mqtt_ip_.c_str(), mqtt_port_);
|
||||
mqttClient_.setClientId(mqtt_hostname_.c_str());
|
||||
mqttClient_.setWill(make_topic(will_topic_, "status"), 1, true, "offline"); // qos 1, retain true
|
||||
mqttClient_.setKeepAlive(MQTT_KEEP_ALIVE);
|
||||
mqttClient_.setCleanSession(false);
|
||||
|
||||
// set credentials if we have them
|
||||
if (!mqtt_user_.empty()) {
|
||||
mqttClient_.setCredentials(mqtt_user_.c_str(), mqtt_password_.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
mqtt_connecting_ = false;
|
||||
mqtt_last_connection_ = uuid::get_uptime();
|
||||
mqtt_reconnect_delay_ = Mqtt::MQTT_RECONNECT_DELAY_MIN;
|
||||
|
||||
LOG_DEBUG(F("Configuring MQTT service..."));
|
||||
}
|
||||
|
||||
// MQTT init callbacks
|
||||
// This should only be executed once
|
||||
void Mqtt::init() {
|
||||
if (mqtt_init_) {
|
||||
return;
|
||||
}
|
||||
mqtt_init_ = true;
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
mqttClient_.onConnect([this](bool sessionPresent) { on_connect(); });
|
||||
|
||||
mqttClient_.onDisconnect([this](AsyncMqttClientDisconnectReason reason) {
|
||||
if (reason == AsyncMqttClientDisconnectReason::TCP_DISCONNECTED) {
|
||||
LOG_DEBUG(F("Disconnected from server"));
|
||||
}
|
||||
if (reason == AsyncMqttClientDisconnectReason::MQTT_IDENTIFIER_REJECTED) {
|
||||
LOG_ERROR(F("Server identifier Rejected"));
|
||||
}
|
||||
if (reason == AsyncMqttClientDisconnectReason::MQTT_SERVER_UNAVAILABLE) {
|
||||
LOG_ERROR(F("Server unavailable"));
|
||||
}
|
||||
if (reason == AsyncMqttClientDisconnectReason::MQTT_MALFORMED_CREDENTIALS) {
|
||||
LOG_ERROR(F("Malformed credentials"));
|
||||
}
|
||||
if (reason == AsyncMqttClientDisconnectReason::MQTT_NOT_AUTHORIZED) {
|
||||
LOG_ERROR(F("Not authorized"));
|
||||
}
|
||||
|
||||
// Reset reconnection delay
|
||||
mqtt_last_connection_ = uuid::get_uptime();
|
||||
mqtt_connecting_ = false;
|
||||
mqtt_start_ = false; // will force a new start()
|
||||
});
|
||||
|
||||
// mqttClient.onSubscribe([this](uint16_t packetId, uint8_t qos) { LOG_DEBUG(F("Subscribe ACK for PID %d"), packetId); });
|
||||
|
||||
mqttClient_.onPublish([this](uint16_t packetId) { on_publish(packetId); });
|
||||
|
||||
mqttClient_.onMessage([this](char * topic, char * payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
|
||||
on_message(topic, payload, len);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
Mqtt::MQTTFunction::MQTTFunction(uint8_t device_id, const std::string && topic, mqtt_function_p mqtt_function)
|
||||
: device_id_(device_id)
|
||||
, topic_(topic)
|
||||
@@ -219,64 +84,27 @@ void Mqtt::subscribe(const std::string & topic, mqtt_function_p cb) {
|
||||
// Checks for connection, establishes a connection if not
|
||||
// sends out top item on publish queue
|
||||
void Mqtt::loop() {
|
||||
// exit if MQTT is not enabled, there is no WIFI or we're still in the MQTT connection process
|
||||
// exit if MQTT is not enabled or ig there is no WIFI
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!mqtt_enabled_ || mqtt_connecting_ || (WiFi.status() != WL_CONNECTED)) {
|
||||
if (!connected()) {
|
||||
#else
|
||||
if (false) {
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// if we're already connected....
|
||||
if (connected()) {
|
||||
if (force_publish_) {
|
||||
force_publish_ = false;
|
||||
send_heartbeat(); // create a heartbeat payload
|
||||
EMSESP::publish_all_values(); // add sensors and mqtt to queue
|
||||
// process_all_queue(); // publish everything on queue
|
||||
}
|
||||
|
||||
// send out heartbeat
|
||||
uint32_t currentMillis = uuid::get_uptime();
|
||||
if ((currentMillis - last_heartbeat_ > MQTT_HEARTBEAT_INTERVAL)) {
|
||||
last_heartbeat_ = currentMillis;
|
||||
send_heartbeat();
|
||||
}
|
||||
|
||||
// create publish messages for each of the EMS device values, adding to queue
|
||||
if (currentMillis - last_publish_ >= mqtt_publish_time_) {
|
||||
last_publish_ = currentMillis;
|
||||
EMSESP::publish_all_values();
|
||||
}
|
||||
|
||||
// publish top item from MQTT queue to stop flooding
|
||||
if ((uint32_t)(currentMillis - last_mqtt_poll_) > MQTT_PUBLISH_WAIT) {
|
||||
last_mqtt_poll_ = currentMillis;
|
||||
process_queue();
|
||||
}
|
||||
|
||||
return;
|
||||
uint32_t currentMillis = uuid::get_uptime();
|
||||
// create publish messages for each of the EMS device values, adding to queue
|
||||
if (publish_time_ && (currentMillis - last_publish_ > publish_time_)) {
|
||||
last_publish_ = currentMillis;
|
||||
EMSESP::publish_all_values();
|
||||
}
|
||||
|
||||
// We need to reconnect. Check when was the last time we tried this
|
||||
if (mqtt_last_connection_ && (uuid::get_uptime() - mqtt_last_connection_ < mqtt_reconnect_delay_)) {
|
||||
return;
|
||||
// publish top item from MQTT queue to stop flooding
|
||||
if ((uint32_t)(currentMillis - last_mqtt_poll_) > MQTT_PUBLISH_WAIT) {
|
||||
last_mqtt_poll_ = currentMillis;
|
||||
process_queue();
|
||||
}
|
||||
|
||||
mqtt_connecting_ = true; // we're doing a connection now
|
||||
|
||||
// Increase the reconnect delay for next time
|
||||
mqtt_reconnect_delay_ += MQTT_RECONNECT_DELAY_STEP;
|
||||
if (mqtt_reconnect_delay_ > MQTT_RECONNECT_DELAY_MAX) {
|
||||
mqtt_reconnect_delay_ = MQTT_RECONNECT_DELAY_MAX;
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
setup();
|
||||
LOG_INFO(F("Connecting to the MQTT server..."));
|
||||
mqttClient_.connect(); // Connect to the MQTT broker
|
||||
#endif
|
||||
}
|
||||
|
||||
// print MQTT log and other stuff to console
|
||||
@@ -379,7 +207,7 @@ void Mqtt::show_topic_handlers(uuid::console::Shell & shell, const uint8_t devic
|
||||
}
|
||||
|
||||
// called when an MQTT Publish ACK is received
|
||||
// its a poor-mans QOS we assume the ACK represents the last Publish sent
|
||||
// its a poor man's QOS we assume the ACK represents the last Publish sent
|
||||
// check if ACK matches the last Publish we sent, if not report an error. Only if qos is 1 or 2
|
||||
// and always remove from queue
|
||||
void Mqtt::on_publish(uint16_t packetId) {
|
||||
@@ -403,64 +231,58 @@ void Mqtt::on_publish(uint16_t packetId) {
|
||||
mqtt_messages_.pop_front(); // always remove from queue, regardless if there was a successful ACK
|
||||
}
|
||||
|
||||
// builds up a topic by prefixing the base and hostname
|
||||
// builds up a topic by prefixing the hostname
|
||||
char * Mqtt::make_topic(char * result, const std::string & topic) {
|
||||
if (!mqtt_base_.empty()) {
|
||||
strlcpy(result, mqtt_base_.c_str(), MQTT_TOPIC_MAX_SIZE);
|
||||
strlcat(result, "/", MQTT_TOPIC_MAX_SIZE);
|
||||
strlcat(result, mqtt_hostname_.c_str(), MQTT_TOPIC_MAX_SIZE);
|
||||
} else {
|
||||
strlcpy(result, mqtt_hostname_.c_str(), MQTT_TOPIC_MAX_SIZE);
|
||||
}
|
||||
|
||||
strlcpy(result, hostname_.c_str(), MQTT_TOPIC_MAX_SIZE);
|
||||
strlcat(result, "/", MQTT_TOPIC_MAX_SIZE);
|
||||
strlcat(result, topic.c_str(), MQTT_TOPIC_MAX_SIZE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Mqtt::start() {
|
||||
send_start_topic();
|
||||
void Mqtt::start(AsyncMqttClient * mqttClient) {
|
||||
mqttClient_ = mqttClient; // copy over from esp8266-react's MQTT service
|
||||
|
||||
// get the hostname, which we'll use to prefix to all topics
|
||||
EMSESP::esp8266React.getWiFiSettingsService()->read([&](WiFiSettings & wifiSettings) { hostname_ = wifiSettings.hostname.c_str(); });
|
||||
|
||||
// fetch MQTT settings
|
||||
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & mqttSettings) {
|
||||
publish_time_ = mqttSettings.publish_time * 1000; // convert to milliseconds
|
||||
mqtt_qos_ = mqttSettings.mqtt_qos;
|
||||
});
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
mqttClient_->onConnect([this](bool sessionPresent) { on_connect(); });
|
||||
mqttClient_->setWill(make_topic(will_topic_, "status"), 1, true, "offline"); // with qos 1, retain true
|
||||
mqttClient_->onMessage([this](char * topic, char * payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
|
||||
on_message(topic, payload, len);
|
||||
mqttClient_->onPublish([this](uint16_t packetId) { on_publish(packetId); });
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
// send online appended with the version information as JSON
|
||||
void Mqtt::send_start_topic() {
|
||||
StaticJsonDocument<90> doc;
|
||||
doc["event"] = "start";
|
||||
doc["version"] = Settings().app_version();
|
||||
publish("info", doc, false); // send with retain off
|
||||
void Mqtt::set_publish_time(uint16_t publish_time) {
|
||||
publish_time_ = publish_time * 1000; // convert to milliseconds
|
||||
}
|
||||
|
||||
void Mqtt::set_qos(uint8_t mqtt_qos) {
|
||||
mqtt_qos_ = mqtt_qos;
|
||||
}
|
||||
|
||||
// MQTT onConnect - when a connect is established
|
||||
void Mqtt::on_connect() {
|
||||
mqtt_reconnect_delay_ = Mqtt::MQTT_RECONNECT_DELAY_MIN;
|
||||
mqtt_last_connection_ = uuid::get_uptime();
|
||||
mqtt_connecting_ = false;
|
||||
// send info topic appended with the version information as JSON
|
||||
StaticJsonDocument<90> doc;
|
||||
doc["event"] = "start";
|
||||
doc["version"] = EMSESP_APP_VERSION;
|
||||
doc["ip"] = WiFi.localIP().toString();
|
||||
publish("info", doc, false); // send with retain off
|
||||
|
||||
publish("status", "online", true); // say we're alive to the Last Will topic, with retain on
|
||||
|
||||
send_heartbeat(); // send heartbeat if enabled
|
||||
|
||||
LOG_INFO(F("MQTT connected"));
|
||||
}
|
||||
|
||||
// send periodic MQTT message with system information
|
||||
void Mqtt::send_heartbeat() {
|
||||
if (!mqtt_heartbeat_) {
|
||||
return;
|
||||
}
|
||||
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
|
||||
doc["rssid"] = Network::wifi_quality();
|
||||
doc["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||
doc["uptime_sec"] = uuid::get_uptime_sec();
|
||||
doc["freemem"] = System::free_mem();
|
||||
doc["mqttpublishfails"] = mqtt_publish_fails_;
|
||||
|
||||
publish("heartbeat", doc, false); // send to MQTT with retain off
|
||||
}
|
||||
|
||||
// add MQTT message to queue, payload is a string
|
||||
void Mqtt::queue_publish_message(const std::string & topic, const std::string & payload, const bool retain) {
|
||||
// can't have bogus topics, but empty payloads are ok
|
||||
@@ -485,7 +307,7 @@ void Mqtt::queue_subscribe_message(const std::string & topic) {
|
||||
}
|
||||
|
||||
auto message = std::make_shared<MqttMessage>(Operation::SUBSCRIBE, topic, "", false);
|
||||
LOG_DEBUG(F("Adding a subscription for %s"), topic.c_str());
|
||||
// LOG_DEBUG(F("Adding a subscription for %s"), topic.c_str());
|
||||
|
||||
// if the queue is full, make room but removing the last one
|
||||
if (mqtt_messages_.size() >= maximum_mqtt_messages_) {
|
||||
@@ -495,14 +317,6 @@ void Mqtt::queue_subscribe_message(const std::string & topic) {
|
||||
mqtt_messages_.emplace_back(mqtt_message_id_++, std::move(message));
|
||||
}
|
||||
|
||||
// Publish using the user's custom retain flag
|
||||
void Mqtt::publish(const std::string & topic, const std::string & payload) {
|
||||
publish(topic, payload, mqtt_retain_);
|
||||
}
|
||||
void Mqtt::publish(const std::string & topic, const JsonDocument & payload) {
|
||||
publish(topic, payload, mqtt_retain_);
|
||||
}
|
||||
|
||||
// MQTT Publish, using a specific retain flag
|
||||
void Mqtt::publish(const std::string & topic, const std::string & payload, bool retain) {
|
||||
queue_publish_message(topic, payload, retain);
|
||||
@@ -517,12 +331,12 @@ void Mqtt::publish(const std::string & topic, const JsonDocument & payload, bool
|
||||
|
||||
// for booleans, which get converted to string values 1 and 0
|
||||
void Mqtt::publish(const std::string & topic, const bool value) {
|
||||
queue_publish_message(topic, value ? "1" : "0", mqtt_retain_);
|
||||
queue_publish_message(topic, value ? "1" : "0", false);
|
||||
}
|
||||
|
||||
// no payload
|
||||
void Mqtt::publish(const std::string & topic) {
|
||||
queue_publish_message(topic, "", mqtt_retain_);
|
||||
queue_publish_message(topic, "", false);
|
||||
}
|
||||
|
||||
// publish all queued messages to MQTT
|
||||
@@ -543,10 +357,9 @@ void Mqtt::process_queue() {
|
||||
auto mqtt_message = mqtt_messages_.front();
|
||||
auto message = mqtt_message.content_;
|
||||
|
||||
// append the hostname and base to the topic, unless we're doing native HA which has a different format
|
||||
// append the hostname to the topic, unless we're doing native HA which has a different format
|
||||
// if the topic starts with "homeassistant" we leave it untouched, otherwise append host
|
||||
char full_topic[MQTT_TOPIC_MAX_SIZE];
|
||||
|
||||
// if the topic starts with "homeassistant" we leave it untouched, otherwise append host and base
|
||||
if (strncmp(message->topic.c_str(), "homeassistant/", 13) == 0) {
|
||||
strcpy(full_topic, message->topic.c_str());
|
||||
} else {
|
||||
@@ -557,7 +370,7 @@ void Mqtt::process_queue() {
|
||||
if (message->operation == Operation::SUBSCRIBE) {
|
||||
LOG_DEBUG(F("Subscribing to topic: %s"), full_topic);
|
||||
#ifndef EMSESP_STANDALONE
|
||||
uint16_t packet_id = mqttClient_.subscribe(full_topic, mqtt_qos_);
|
||||
uint16_t packet_id = mqttClient_->subscribe(full_topic, mqtt_qos_);
|
||||
#else
|
||||
uint16_t packet_id = 1;
|
||||
#endif
|
||||
@@ -579,8 +392,7 @@ void Mqtt::process_queue() {
|
||||
// else try and publish it
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// uint16_t packet_id = mqttClient_.publish(full_topic, mqtt_qos_, message->retain, message->payload.c_str());
|
||||
uint16_t packet_id = mqttClient_.publish(full_topic, mqtt_qos_, message->retain, message->payload.c_str(), message->payload.size(), false, mqtt_message.id_);
|
||||
uint16_t packet_id = mqttClient_->publish(full_topic, mqtt_qos_, message->retain, message->payload.c_str(), message->payload.size(), false, mqtt_message.id_);
|
||||
#else
|
||||
uint16_t packet_id = 1;
|
||||
#endif
|
||||
@@ -611,243 +423,4 @@ void Mqtt::process_queue() {
|
||||
mqtt_messages_.pop_front(); // remove the message from the queue
|
||||
}
|
||||
|
||||
// add console commands
|
||||
void Mqtt::console_commands(Shell & shell, unsigned int context) {
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::MQTT,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(heartbeat)},
|
||||
flash_string_vector{F_(bool_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
Settings settings;
|
||||
if (arguments[0] == read_flash_string(F_(on))) {
|
||||
settings.mqtt_heartbeat(true);
|
||||
settings.commit();
|
||||
} else if (arguments[0] == read_flash_string(F_(off))) {
|
||||
settings.mqtt_heartbeat(false);
|
||||
settings.commit();
|
||||
} else {
|
||||
shell.println(F("Must be on or off"));
|
||||
return;
|
||||
}
|
||||
},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
|
||||
return std::vector<std::string>{read_flash_string(F_(on)), read_flash_string(F_(off))};
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::MQTT,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(format)},
|
||||
flash_string_vector{F_(name_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
Settings settings;
|
||||
uint8_t value;
|
||||
if (arguments[0] == read_flash_string(F_(single))) {
|
||||
value = Settings::MQTT_format::SINGLE;
|
||||
} else if (arguments[0] == read_flash_string(F_(nested))) {
|
||||
value = Settings::MQTT_format::NESTED;
|
||||
} else if (arguments[0] == read_flash_string(F_(ha))) {
|
||||
value = Settings::MQTT_format::HA;
|
||||
} else if (arguments[0] == read_flash_string(F_(custom))) {
|
||||
value = Settings::MQTT_format::CUSTOM;
|
||||
} else {
|
||||
shell.println(F("Must be single, nested or ha"));
|
||||
return;
|
||||
}
|
||||
settings.mqtt_format(value);
|
||||
settings.commit();
|
||||
shell.println(F("Please restart EMS-ESP"));
|
||||
},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
|
||||
return std::vector<std::string>{read_flash_string(F_(single)), read_flash_string(F_(nested)), read_flash_string(F_(ha)), read_flash_string(F_(custom))};
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::MQTT,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(publish_time)},
|
||||
flash_string_vector{F_(seconds_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint16_t publish_time = Helpers::atoint(arguments.front().c_str());
|
||||
Settings settings;
|
||||
settings.mqtt_publish_time(publish_time);
|
||||
settings.commit();
|
||||
shell.printfln(F_(mqtt_publish_time_fmt), settings.mqtt_publish_time());
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::MQTT,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(enabled)},
|
||||
flash_string_vector{F_(bool_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
Settings settings;
|
||||
if (arguments[0] == read_flash_string(F_(on))) {
|
||||
settings.mqtt_enabled(true);
|
||||
settings.commit();
|
||||
reconnect();
|
||||
} else if (arguments[0] == read_flash_string(F_(off))) {
|
||||
settings.mqtt_enabled(false);
|
||||
settings.commit();
|
||||
reconnect();
|
||||
} else {
|
||||
shell.println(F("Value must be on or off"));
|
||||
return;
|
||||
}
|
||||
},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
|
||||
return std::vector<std::string>{read_flash_string(F_(on)), read_flash_string(F_(off))};
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::MQTT,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(ip)},
|
||||
flash_string_vector{F_(ip_address_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
Settings settings;
|
||||
|
||||
if (!arguments.empty()) {
|
||||
settings.mqtt_ip(arguments.front());
|
||||
settings.commit();
|
||||
}
|
||||
auto ip = settings.mqtt_ip();
|
||||
shell.printfln(F_(mqtt_ip_fmt), ip.empty() ? uuid::read_flash_string(F_(unset)).c_str() : ip.c_str());
|
||||
reconnect();
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::MQTT,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(base)},
|
||||
flash_string_vector{F_(name_mandatory)},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
|
||||
Settings settings;
|
||||
if (!arguments.empty()) {
|
||||
settings.mqtt_base(arguments.front());
|
||||
settings.commit();
|
||||
}
|
||||
reconnect();
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::MQTT,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(port)},
|
||||
flash_string_vector{F_(port_mandatory)},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
|
||||
Settings settings;
|
||||
if (!arguments.empty()) {
|
||||
settings.mqtt_port(atoi(arguments.front().c_str()));
|
||||
settings.commit();
|
||||
}
|
||||
reconnect();
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::MQTT,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(qos)},
|
||||
flash_string_vector{F_(n_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint8_t value = (arguments[0]).at(0) - '0';
|
||||
|
||||
if (value <= 2) {
|
||||
Settings settings;
|
||||
settings.mqtt_qos(value);
|
||||
settings.commit();
|
||||
reconnect();
|
||||
} else {
|
||||
shell.printfln(F("Value must be 0, 1 or 2"));
|
||||
}
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::MQTT,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(publish)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.printfln(F("Publishing all values to MQTT"));
|
||||
force_publish_ = true;
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::MQTT,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(reconnect)},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
reconnect();
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::MQTT,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(password)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.enter_password(F_(new_password_prompt1), [=](Shell & shell, bool completed, const std::string & password1) {
|
||||
if (completed) {
|
||||
shell.enter_password(F_(new_password_prompt2),
|
||||
[password1](Shell & shell, bool completed, const std::string & password2) {
|
||||
if (completed) {
|
||||
if (password1 == password2) {
|
||||
Settings settings;
|
||||
settings.mqtt_password(password2);
|
||||
settings.commit();
|
||||
shell.println(F("MQTT password updated"));
|
||||
reconnect();
|
||||
} else {
|
||||
shell.println(F("Passwords do not match"));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::MQTT,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(username)},
|
||||
flash_string_vector{F_(name_optional)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
Settings settings;
|
||||
if (arguments.empty()) {
|
||||
settings.mqtt_user("");
|
||||
} else {
|
||||
settings.mqtt_user(arguments.front());
|
||||
}
|
||||
settings.commit();
|
||||
shell.printfln(F_(mqtt_user_fmt),
|
||||
settings.mqtt_user().empty() ? uuid::read_flash_string(F_(unset)).c_str()
|
||||
: settings.mqtt_user().c_str());
|
||||
reconnect();
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::MQTT,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { show_mqtt(shell); });
|
||||
|
||||
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::MQTT, CommandFlags::USER, flash_string_vector{F_(set)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
Settings settings;
|
||||
shell.printfln(F_(mqtt_enabled_fmt), settings.mqtt_enabled() ? F_(enabled) : F_(disabled));
|
||||
shell.printfln(F_(mqtt_ip_fmt), settings.mqtt_ip().empty() ? uuid::read_flash_string(F_(unset)).c_str() : settings.mqtt_ip().c_str());
|
||||
shell.printfln(F_(mqtt_user_fmt), settings.mqtt_user().empty() ? uuid::read_flash_string(F_(unset)).c_str() : settings.mqtt_user().c_str());
|
||||
shell.printfln(F_(mqtt_password_fmt), settings.mqtt_password().empty() ? F_(unset) : F_(asterisks));
|
||||
shell.printfln(F_(mqtt_port_fmt), settings.mqtt_port());
|
||||
shell.printfln(F_(mqtt_base_fmt), settings.mqtt_base().empty() ? uuid::read_flash_string(F_(unset)).c_str() : settings.mqtt_base().c_str());
|
||||
shell.printfln(F_(mqtt_qos_fmt), settings.mqtt_qos());
|
||||
shell.printfln(F_(mqtt_retain_fmt), settings.mqtt_retain() ? F_(enabled) : F_(disabled));
|
||||
if (settings.mqtt_format() == Settings::MQTT_format::SINGLE) {
|
||||
shell.printfln(F_(mqtt_format_fmt), F_(single));
|
||||
} else if (settings.mqtt_format() == Settings::MQTT_format::NESTED) {
|
||||
shell.printfln(F_(mqtt_format_fmt), F_(nested));
|
||||
} else if (settings.mqtt_format() == Settings::MQTT_format::HA) {
|
||||
shell.printfln(F_(mqtt_format_fmt), F_(ha));
|
||||
} else if (settings.mqtt_format() == Settings::MQTT_format::CUSTOM) {
|
||||
shell.printfln(F_(mqtt_format_fmt), F_(custom));
|
||||
}
|
||||
shell.printfln(F_(mqtt_heartbeat_fmt), settings.mqtt_heartbeat() ? F_(enabled) : F_(disabled));
|
||||
shell.printfln(F_(mqtt_publish_time_fmt), settings.mqtt_publish_time());
|
||||
shell.println();
|
||||
});
|
||||
|
||||
// enter the context
|
||||
Console::enter_custom_context(shell, context);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
86
src/mqtt.h
86
src/mqtt.h
@@ -32,9 +32,7 @@
|
||||
#endif
|
||||
|
||||
#include "helpers.h"
|
||||
#include "settings.h"
|
||||
#include "system.h"
|
||||
#include "network.h"
|
||||
#include "console.h"
|
||||
|
||||
#include <uuid/log.h>
|
||||
@@ -63,38 +61,53 @@ struct MqttMessage {
|
||||
class Mqtt {
|
||||
public:
|
||||
void loop();
|
||||
void start();
|
||||
void send_heartbeat();
|
||||
|
||||
void start(AsyncMqttClient * mqttClient);
|
||||
|
||||
void set_publish_time(uint16_t publish_time);
|
||||
void set_qos(uint8_t mqtt_qos);
|
||||
|
||||
enum Operation { PUBLISH, SUBSCRIBE };
|
||||
|
||||
static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 60; // include host and base etc
|
||||
|
||||
// are static to be accessed from EMS devices
|
||||
static void subscribe(const uint8_t device_id, const std::string & topic, mqtt_function_p cb);
|
||||
static void subscribe(const std::string & topic, mqtt_function_p cb);
|
||||
|
||||
static void publish(const std::string & topic, const std::string & payload);
|
||||
static void publish(const std::string & topic, const std::string & payload, bool retain);
|
||||
static void publish(const std::string & topic, const JsonDocument & payload);
|
||||
static void publish(const std::string & topic, const JsonDocument & payload, bool retain);
|
||||
static void publish(const std::string & topic, const std::string & payload, bool retain = false);
|
||||
static void publish(const std::string & topic, const JsonDocument & payload, bool retain = false);
|
||||
static void publish(const std::string & topic, const bool value);
|
||||
static void publish(const std::string & topic);
|
||||
|
||||
static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_id);
|
||||
|
||||
static void console_commands(Shell & shell, unsigned int context);
|
||||
static void show_mqtt(uuid::console::Shell & shell);
|
||||
|
||||
static void on_connect();
|
||||
|
||||
void disconnect() {
|
||||
#ifdef EMSESP_STANDALONE
|
||||
return;
|
||||
#else
|
||||
mqttClient_->disconnect();
|
||||
#endif
|
||||
}
|
||||
|
||||
void incoming(char * topic, char * payload); // for testing
|
||||
|
||||
static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 60; // include host and base etc
|
||||
|
||||
static bool connected() {
|
||||
#ifdef EMSESP_STANDALONE
|
||||
return true;
|
||||
#else
|
||||
return mqttClient_.connected();
|
||||
return mqttClient_->connected();
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint32_t publish_fails() {
|
||||
return mqtt_publish_fails_;
|
||||
}
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
@@ -111,40 +124,28 @@ class Mqtt {
|
||||
static std::deque<QueuedMqttMessage> mqtt_messages_;
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
static AsyncMqttClient mqttClient_;
|
||||
static AsyncMqttClient * mqttClient_;
|
||||
#endif
|
||||
|
||||
void flush_message_queue();
|
||||
void setup();
|
||||
|
||||
static constexpr size_t MAX_MQTT_MESSAGES = 50;
|
||||
static size_t maximum_mqtt_messages_;
|
||||
static uint16_t mqtt_message_id_;
|
||||
static bool mqtt_retain_;
|
||||
|
||||
static constexpr uint8_t MQTT_QUEUE_MAX_SIZE = 50;
|
||||
static constexpr uint32_t MQTT_PUBLISH_WAIT = 250; // delay between sending publishes, to account for large payloads
|
||||
static constexpr uint8_t MQTT_PUBLISH_MAX_RETRY = 3; // max retries for giving up on publishing
|
||||
static constexpr uint8_t MQTT_KEEP_ALIVE = 60; // 60 seconds. This could also be less, like 30 seconds
|
||||
static constexpr uint32_t MQTT_RECONNECT_DELAY_MIN = 2000; // Try to reconnect in 2 seconds upon disconnection
|
||||
static constexpr uint32_t MQTT_RECONNECT_DELAY_STEP = 3000; // Increase the reconnect delay in 3 seconds after each failed attempt
|
||||
static constexpr uint32_t MQTT_RECONNECT_DELAY_MAX = 120000; // Set reconnect time to 2 minutes at most
|
||||
static constexpr uint32_t MQTT_HEARTBEAT_INTERVAL = 60000; // in milliseconds, how often the MQTT heartbeat is sent (1 min)
|
||||
static constexpr uint8_t MQTT_QUEUE_MAX_SIZE = 50;
|
||||
static constexpr uint32_t MQTT_PUBLISH_WAIT = 250; // delay between sending publishes, to account for large payloads
|
||||
static constexpr uint8_t MQTT_PUBLISH_MAX_RETRY = 3; // max retries for giving up on publishing
|
||||
|
||||
static void queue_publish_message(const std::string & topic, const std::string & payload, const bool retain);
|
||||
static void queue_subscribe_message(const std::string & topic);
|
||||
|
||||
void on_publish(uint16_t packetId);
|
||||
void on_message(char * topic, char * payload, size_t len);
|
||||
void on_connect();
|
||||
static char * make_topic(char * result, const std::string & topic);
|
||||
void process_queue();
|
||||
void process_all_queue();
|
||||
void send_start_topic();
|
||||
static void reconnect();
|
||||
void init();
|
||||
|
||||
static void show_mqtt(uuid::console::Shell & shell);
|
||||
static uint16_t mqtt_publish_fails_;
|
||||
|
||||
class MQTTFunction {
|
||||
public:
|
||||
@@ -157,33 +158,14 @@ class Mqtt {
|
||||
};
|
||||
static std::vector<MQTTFunction> mqtt_functions_; // list of mqtt callbacks for all devices
|
||||
|
||||
static uint16_t mqtt_publish_fails_;
|
||||
|
||||
uint32_t mqtt_last_connection_ = 0;
|
||||
uint32_t mqtt_reconnect_delay_ = MQTT_RECONNECT_DELAY_MIN;
|
||||
bool mqtt_init_ = false;
|
||||
bool mqtt_start_ = false;
|
||||
bool mqtt_connecting_ = false;
|
||||
uint16_t mqtt_publish_time_;
|
||||
|
||||
uint32_t last_heartbeat_ = 0;
|
||||
uint32_t last_mqtt_poll_ = 0;
|
||||
uint32_t last_publish_ = 0;
|
||||
|
||||
static bool force_publish_;
|
||||
|
||||
// settings
|
||||
static std::string mqtt_hostname_;
|
||||
static std::string mqtt_base_;
|
||||
// settings, copied over
|
||||
static std::string hostname_;
|
||||
static uint8_t mqtt_qos_;
|
||||
static uint8_t mqtt_format_;
|
||||
std::string mqtt_ip_;
|
||||
std::string mqtt_user_;
|
||||
std::string mqtt_password_;
|
||||
bool mqtt_enabled_ = true; // start off assuming we want to connect
|
||||
bool mqtt_heartbeat_;
|
||||
uint16_t mqtt_port_;
|
||||
};
|
||||
static uint16_t publish_time_;
|
||||
}; // namespace emsesp
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
|
||||
392
src/network.cpp
392
src/network.cpp
@@ -1,392 +0,0 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/proddy/EMS-ESP
|
||||
* Copyright 2019 Paul Derbyshire
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// code by nomis - https://github.com/nomis
|
||||
|
||||
#include "network.h"
|
||||
|
||||
MAKE_PSTR(logger_name, "network")
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
uuid::log::Logger Network::logger_{F_(logger_name), uuid::log::Facility::KERN};
|
||||
|
||||
void Network::start() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// We want the device to come up in opmode=0 (WIFI_OFF), when erasing the flash this is not the default.
|
||||
// If needed, we save opmode=0 before disabling persistence so the device boots with WiFi disabled in the future.
|
||||
if (WiFi.getMode() != WIFI_OFF) {
|
||||
WiFi.mode(WIFI_OFF);
|
||||
}
|
||||
WiFi.persistent(false);
|
||||
WiFi.disconnect(true);
|
||||
WiFi.setAutoReconnect(false);
|
||||
WiFi.mode(WIFI_STA);
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266)
|
||||
sta_mode_connected_ = WiFi.onStationModeConnected(std::bind(&Network::sta_mode_connected, this, _1));
|
||||
sta_mode_disconnected_ = WiFi.onStationModeDisconnected(std::bind(&Network::sta_mode_disconnected, this, _1));
|
||||
sta_mode_got_ip_ = WiFi.onStationModeGotIP(std::bind(&Network::sta_mode_got_ip, this, _1));
|
||||
|
||||
WiFi.setSleepMode(WIFI_NONE_SLEEP); // added to possibly fix wifi dropouts in arduino core 2.5.0
|
||||
// ref: https://github.com/esp8266/Arduino/issues/6471
|
||||
// ref: https://github.com/esp8266/Arduino/issues/6366
|
||||
// high tx power causing weird behavior, slightly lowering from 20.5 to 20.0 may help stability
|
||||
// WiFi.setOutputPower(20.0); // in DBM
|
||||
|
||||
connect(); // connect to WiFi
|
||||
ota_setup(); // initialize OTA
|
||||
|
||||
#elif defined(ESP32)
|
||||
WiFi.mode(WIFI_MODE_MAX);
|
||||
WiFi.mode(WIFI_MODE_NULL);
|
||||
|
||||
WiFi.onEvent(std::bind(&Network::sta_mode_connected, this, _1, _2), WiFiEvent_t::SYSTEM_EVENT_STA_CONNECTED);
|
||||
WiFi.onEvent(std::bind(&Network::sta_mode_disconnected, this, _1, _2), WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
|
||||
WiFi.onEvent(std::bind(&Network::sta_mode_got_ip, this, _1, _2), WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
|
||||
WiFi.onEvent(std::bind(&Network::sta_mode_start, this, _1, _2), WiFiEvent_t::SYSTEM_EVENT_STA_START);
|
||||
|
||||
connect(); // connect to WiFi
|
||||
ota_setup(); // initialize OTA
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ESP32)
|
||||
// set the ESP32 Wifi hostname. Didn't work during setup and I didn't want to use a delay().
|
||||
// see https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino#L79
|
||||
void Network::sta_mode_start(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
WiFi.setHostname(Settings().hostname().c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266)
|
||||
void Network::sta_mode_connected(const WiFiEventStationModeConnected & event) {
|
||||
LOG_INFO(F("Connected to %s (%02X:%02X:%02X:%02X:%02X:%02X) on channel %u"),
|
||||
event.ssid.c_str(),
|
||||
event.bssid[0],
|
||||
event.bssid[1],
|
||||
event.bssid[2],
|
||||
event.bssid[3],
|
||||
event.bssid[4],
|
||||
event.bssid[5],
|
||||
event.channel);
|
||||
|
||||
// turn off safe mode
|
||||
System::save_safe_mode(false);
|
||||
}
|
||||
#elif defined(ESP32)
|
||||
void Network::sta_mode_connected(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
LOG_INFO(F("Connected to %s (%02X:%02X:%02X:%02X:%02X:%02X) on channel %u"),
|
||||
info.connected.ssid,
|
||||
info.sta_connected.mac[0],
|
||||
info.sta_connected.mac[1],
|
||||
info.sta_connected.mac[2],
|
||||
info.sta_connected.mac[3],
|
||||
info.sta_connected.mac[4],
|
||||
info.sta_connected.mac[5],
|
||||
info.connected.channel);
|
||||
|
||||
// turn off safe mode
|
||||
System::save_safe_mode(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266)
|
||||
void Network::sta_mode_disconnected(const WiFiEventStationModeDisconnected & event) {
|
||||
// disconnect if failed to connect a few times, unless already in safe mode!
|
||||
if (event.reason == 201) {
|
||||
if (++disconnect_count_ == 3) {
|
||||
if (System::safe_mode()) {
|
||||
LOG_ERROR(F("Failed to connect to WiFi %s after %d attempts"), event.ssid.c_str(), disconnect_count_ - 1);
|
||||
disconnect_count_ = 0;
|
||||
} else {
|
||||
LOG_ERROR(F("Failed to connect to WiFi. Rebooting into Safe mode"));
|
||||
System::restart(true); // set safe mode and restart
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif defined(ESP32)
|
||||
void Network::sta_mode_disconnected(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
// LOG_ERROR(F("Failed to connect to WiFi %s, reason code %d"), info.disconnected.ssid, info.disconnected.reason);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266)
|
||||
void Network::sta_mode_got_ip(const WiFiEventStationModeGotIP & event) {
|
||||
LOG_INFO(F("Obtained IPv4 address %s/%s and gateway %s"),
|
||||
uuid::printable_to_string(event.ip).c_str(),
|
||||
uuid::printable_to_string(event.mask).c_str(),
|
||||
uuid::printable_to_string(event.gw).c_str());
|
||||
}
|
||||
#elif defined(ESP32)
|
||||
void Network::sta_mode_got_ip(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
LOG_INFO(F("Obtained IPv4 address %s/%s and gateway %s"),
|
||||
uuid::printable_to_string(IPAddress(info.got_ip.ip_info.ip.addr)).c_str(),
|
||||
uuid::printable_to_string(IPAddress(info.got_ip.ip_info.netmask.addr)).c_str(),
|
||||
uuid::printable_to_string(IPAddress(info.got_ip.ip_info.gw.addr)).c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
void Network::connect() {
|
||||
Settings settings;
|
||||
|
||||
if (settings.wifi_ssid().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
|
||||
if (!settings.hostname().empty()) {
|
||||
#if defined(ESP8266)
|
||||
// experiment with fixed IP
|
||||
// IPAddress ip(10, 10, 10, 140);
|
||||
// IPAddress gateway(10, 10, 10, 1);
|
||||
// IPAddress subnet(255, 255, 255, 0);
|
||||
// WiFi.config(ip, gateway, subnet);
|
||||
|
||||
WiFi.config(INADDR_ANY, INADDR_ANY, INADDR_ANY);
|
||||
|
||||
WiFi.hostname(settings.hostname().c_str());
|
||||
#elif defined(ESP32)
|
||||
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
|
||||
#endif
|
||||
}
|
||||
|
||||
LOG_INFO(F("Connecting to wifi ssid %s..."), settings.wifi_ssid().c_str());
|
||||
WiFi.begin(settings.wifi_ssid().c_str(), settings.wifi_password().c_str());
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void Network::reconnect() {
|
||||
disconnect();
|
||||
connect();
|
||||
}
|
||||
|
||||
void Network::disconnect() {
|
||||
#if defined(ESP8266)
|
||||
WiFi.disconnect();
|
||||
#elif defined(ESP32)
|
||||
WiFi.disconnect(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
// OTA Setup
|
||||
void Network::ota_setup() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
|
||||
if (ota_) {
|
||||
delete ota_;
|
||||
ota_ = nullptr;
|
||||
}
|
||||
|
||||
ota_ = new ArduinoOTAClass;
|
||||
|
||||
Settings settings;
|
||||
|
||||
ota_->setPort(OTA_PORT);
|
||||
|
||||
if (settings.hostname().empty()) {
|
||||
ota_->setHostname(EMSESP_DEFAULT_HOSTNAME);
|
||||
} else {
|
||||
ota_->setHostname(settings.hostname().c_str());
|
||||
}
|
||||
|
||||
ota_->setPassword(settings.admin_password().c_str());
|
||||
|
||||
ota_->onStart([this]() {
|
||||
LOG_DEBUG(F("OTA starting (send type %d)..."), ota_->getCommand());
|
||||
|
||||
// turn off UART stuff to stop interference on an ESP8266 only
|
||||
#if defined(ESP8266)
|
||||
EMSuart::stop(); // UART stop
|
||||
#endif
|
||||
in_ota_ = true; // set flag so all other services stop
|
||||
});
|
||||
|
||||
ota_->onEnd([this]() { LOG_DEBUG(F("OTA done, automatically restarting")); });
|
||||
|
||||
ota_->onProgress([this](unsigned int progress, unsigned int total) {
|
||||
/*
|
||||
static unsigned int _progOld;
|
||||
unsigned int _prog = (progress / (total / 100));
|
||||
if (_prog != _progOld) {
|
||||
LOG_DEBUG(F("[OTA] Progress: %u%%"), _prog);
|
||||
_progOld = _prog;
|
||||
}
|
||||
*/
|
||||
});
|
||||
|
||||
ota_->onError([this](ota_error_t error) {
|
||||
if (error == OTA_AUTH_ERROR) {
|
||||
LOG_ERROR(F("[OTA] Auth Failed"));
|
||||
} else if (error == OTA_BEGIN_ERROR) {
|
||||
LOG_ERROR(F("[OTA] Begin Failed"));
|
||||
} else if (error == OTA_CONNECT_ERROR) {
|
||||
LOG_ERROR(F("[OTA] Connect Failed"));
|
||||
} else if (error == OTA_RECEIVE_ERROR) {
|
||||
LOG_ERROR(F("[OTA] Receive Failed"));
|
||||
} else if (error == OTA_END_ERROR) {
|
||||
LOG_ERROR(F("[OTA] End Failed"));
|
||||
} else {
|
||||
LOG_ERROR(F("[OTA] Error %d"), error);
|
||||
};
|
||||
});
|
||||
|
||||
// start ota service
|
||||
ota_->begin();
|
||||
LOG_INFO(F("Listening to firmware updates on %s.local:%u"), ota_->getHostname().c_str(), OTA_PORT);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// loop - mainly calls OTA
|
||||
bool Network::loop() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (ota_) {
|
||||
ota_->handle();
|
||||
}
|
||||
#endif
|
||||
|
||||
return !in_ota_;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
void Network::scan(uuid::console::Shell & shell) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
int8_t ret = WiFi.scanNetworks(true);
|
||||
if (ret == WIFI_SCAN_RUNNING) {
|
||||
shell.println(F("Scanning for WiFi networks..."));
|
||||
|
||||
shell.block_with([](uuid::console::Shell & shell, bool stop) -> bool {
|
||||
int8_t ret = WiFi.scanComplete();
|
||||
|
||||
if (ret == WIFI_SCAN_RUNNING) {
|
||||
return stop;
|
||||
} else if (ret == WIFI_SCAN_FAILED || ret < 0) {
|
||||
shell.println(F("WiFi scan failed"));
|
||||
return true;
|
||||
} else {
|
||||
shell.printfln(F("Found %u networks:"), ret);
|
||||
shell.println();
|
||||
|
||||
for (uint8_t i = 0; i < (uint8_t)ret; i++) {
|
||||
shell.printfln(F(" %s (channel %u at %d dBm) %s"), WiFi.SSID(i).c_str(), WiFi.channel(i), WiFi.RSSI(i), WiFi.BSSIDstr(i).c_str());
|
||||
}
|
||||
shell.println();
|
||||
|
||||
WiFi.scanDelete();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
shell.println(F("WiFi scan failed"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Network::show_network(uuid::console::Shell & shell) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
switch (WiFi.status()) {
|
||||
case WL_IDLE_STATUS:
|
||||
shell.printfln(F("WiFi: idle"));
|
||||
break;
|
||||
|
||||
case WL_NO_SSID_AVAIL:
|
||||
shell.printfln(F("WiFi: network not found"));
|
||||
break;
|
||||
|
||||
case WL_SCAN_COMPLETED:
|
||||
shell.printfln(F("WiFi: network scan complete"));
|
||||
break;
|
||||
|
||||
case WL_CONNECTED: {
|
||||
shell.printfln(F("WiFi: connected"));
|
||||
shell.println();
|
||||
|
||||
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());
|
||||
shell.println();
|
||||
|
||||
shell.printfln(F("MAC address: %s"), WiFi.macAddress().c_str());
|
||||
#if defined(ESP8266)
|
||||
shell.printfln(F("Hostname: %s"), WiFi.hostname().c_str());
|
||||
#elif defined(ESP32)
|
||||
shell.printfln(F("Hostname: %s"), WiFi.getHostname());
|
||||
#endif
|
||||
|
||||
shell.println();
|
||||
shell.printfln(F("IPv4 address: %s/%s"), uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str());
|
||||
shell.printfln(F("IPv4 gateway: %s"), uuid::printable_to_string(WiFi.gatewayIP()).c_str());
|
||||
shell.printfln(F("IPv4 nameserver: %s"), uuid::printable_to_string(WiFi.dnsIP()).c_str());
|
||||
} break;
|
||||
|
||||
case WL_CONNECT_FAILED:
|
||||
shell.printfln(F("WiFi: connection failed"));
|
||||
break;
|
||||
|
||||
case WL_CONNECTION_LOST:
|
||||
shell.printfln(F("WiFi: connection lost"));
|
||||
break;
|
||||
|
||||
case WL_DISCONNECTED:
|
||||
shell.printfln(F("WiFi: disconnected"));
|
||||
break;
|
||||
|
||||
case WL_NO_SHIELD:
|
||||
default:
|
||||
shell.printfln(F("WiFi: unknown"));
|
||||
break;
|
||||
}
|
||||
|
||||
shell.println();
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
// Return the quality (Received Signal Strength Indicator) of the WiFi network as a %. Or -1 if disconnected.
|
||||
// High quality: 90% ~= -55dBm
|
||||
// Medium quality: 50% ~= -75dBm
|
||||
// Low quality: 30% ~= -85dBm
|
||||
// Unusable quality: 8% ~= -96dBm
|
||||
int8_t Network::wifi_quality() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
return -1;
|
||||
}
|
||||
int dBm = WiFi.RSSI();
|
||||
#else
|
||||
int8_t dBm = -70;
|
||||
#endif
|
||||
if (dBm <= -100) {
|
||||
return 0;
|
||||
}
|
||||
if (dBm >= -50) {
|
||||
return 100;
|
||||
}
|
||||
return 2 * (dBm + 100);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
102
src/network.h
102
src/network.h
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/proddy/EMS-ESP
|
||||
* Copyright 2019 Paul Derbyshire
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// code by nomis - https://github.com/nomis
|
||||
|
||||
|
||||
#ifndef EMSESP_NETWORK_H_
|
||||
#define EMSESP_NETWORK_H_
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#elif defined(ESP32)
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
#include <ArduinoOTA.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include "settings.h"
|
||||
#include "system.h"
|
||||
#include "helpers.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
using namespace std::placeholders; // for `_1`
|
||||
|
||||
class Network {
|
||||
public:
|
||||
static void connect();
|
||||
static void reconnect();
|
||||
static void disconnect();
|
||||
static void scan(uuid::console::Shell & shell);
|
||||
static void show_network(uuid::console::Shell & shell);
|
||||
|
||||
static int8_t wifi_quality();
|
||||
|
||||
void start();
|
||||
bool loop();
|
||||
|
||||
bool connected() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
return (WiFi.status() == WL_CONNECTED);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr uint16_t OTA_PORT = 8266;
|
||||
|
||||
#if defined(ESP8266)
|
||||
void sta_mode_connected(const WiFiEventStationModeConnected & event);
|
||||
void sta_mode_disconnected(const WiFiEventStationModeDisconnected & event);
|
||||
void sta_mode_got_ip(const WiFiEventStationModeGotIP & event);
|
||||
|
||||
WiFiEventHandler sta_mode_connected_;
|
||||
WiFiEventHandler sta_mode_disconnected_;
|
||||
WiFiEventHandler sta_mode_got_ip_;
|
||||
|
||||
ArduinoOTAClass * ota_;
|
||||
|
||||
#elif defined(ESP32)
|
||||
|
||||
void sta_mode_connected(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
void sta_mode_disconnected(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
void sta_mode_got_ip(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
void sta_mode_start(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
|
||||
ArduinoOTAClass * ota_;
|
||||
|
||||
#endif
|
||||
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
void ota_setup();
|
||||
|
||||
uint8_t disconnect_count_ = 0;
|
||||
bool in_ota_ = false;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
@@ -65,18 +65,17 @@ void Roomctrl::check(const uint8_t addr, const uint8_t * data) {
|
||||
if (hc_ > 3) {
|
||||
return;
|
||||
}
|
||||
// no reply if the temperature is not set
|
||||
if (remotetemp[hc_] == EMS_VALUE_SHORT_NOTSET) {
|
||||
return;
|
||||
}
|
||||
// reply to writes with write nack byte
|
||||
if (addr & 0x80) { // it's a write to us
|
||||
nack_write(); // we don't accept writes.
|
||||
return;
|
||||
}
|
||||
// for now we only reply to version and remote temperature
|
||||
// reads: for now we only reply to version and remote temperature
|
||||
// empty message back if temperature not set or unknown message type
|
||||
if (data[2] == 0x02) {
|
||||
version(addr, data[0]);
|
||||
} else if (remotetemp[hc_] == EMS_VALUE_SHORT_NOTSET) {
|
||||
unknown(addr, data[0], data[2], data[3]);
|
||||
} else if (data[2] == 0xAF && data[3] == 0) {
|
||||
temperature(addr, data[0]);
|
||||
} else {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
// code written by nomis - https://github.com/nomis
|
||||
|
||||
#include "sensors.h"
|
||||
#include "emsesp.h"
|
||||
|
||||
MAKE_PSTR(logger_name, "sensors")
|
||||
|
||||
@@ -28,11 +29,13 @@ uuid::log::Logger Sensors::logger_{F_(logger_name), uuid::log::Facility::DAEMON}
|
||||
|
||||
void Sensors::start() {
|
||||
// copy over values from MQTT so we don't keep on quering the filesystem
|
||||
mqtt_format_ = Settings().mqtt_format();
|
||||
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) {
|
||||
mqtt_format_ = settings.mqtt_format; // single, nested or ha
|
||||
});
|
||||
|
||||
// if we're using HA MQTT Discovery, send out the config
|
||||
// currently we just do this for a single sensor (sensor1)
|
||||
if (mqtt_format_ == Settings::MQTT_format::HA) {
|
||||
if (mqtt_format_ == MQTT_format::HA) {
|
||||
// Mqtt::publish(topic); // empty payload, this remove any previous config sent to HA
|
||||
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
|
||||
@@ -246,7 +249,7 @@ void Sensors::publish_values() {
|
||||
// if we're not using nested JSON, send each sensor out seperately
|
||||
// sensor1, sensor2 etc...
|
||||
// e.g. sensor_1 = {"temp":20.2}
|
||||
if (mqtt_format_ == Settings::MQTT_format::SINGLE) {
|
||||
if (mqtt_format_ == MQTT_format::SINGLE) {
|
||||
StaticJsonDocument<100> doc;
|
||||
for (const auto & device : devices_) {
|
||||
char s[5];
|
||||
@@ -274,7 +277,7 @@ void Sensors::publish_values() {
|
||||
|
||||
uint8_t i = 1;
|
||||
for (const auto & device : devices_) {
|
||||
if (mqtt_format_ == Settings::MQTT_format::CUSTOM) {
|
||||
if (mqtt_format_ == MQTT_format::CUSTOM) {
|
||||
char s[5];
|
||||
doc[device.to_string()] = Helpers::render_value(s, device.temperature_c_, 2);
|
||||
} else {
|
||||
@@ -288,7 +291,7 @@ void Sensors::publish_values() {
|
||||
}
|
||||
}
|
||||
|
||||
if (mqtt_format_ == Settings::MQTT_format::HA) {
|
||||
if (mqtt_format_ == MQTT_format::HA) {
|
||||
Mqtt::publish("homeassistant/sensor/ems-esp/external/state", doc);
|
||||
} else {
|
||||
Mqtt::publish("sensors", doc);
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "settings.h"
|
||||
#include "mqtt.h"
|
||||
#include "console.h"
|
||||
|
||||
|
||||
300
src/settings.cpp
300
src/settings.cpp
@@ -1,300 +0,0 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/proddy/EMS-ESP
|
||||
* Copyright 2019 Paul Derbyshire
|
||||
*
|
||||
* 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 "settings.h"
|
||||
|
||||
MAKE_PSTR(settings_filename, "/settings.msgpack")
|
||||
MAKE_PSTR(settings_backup_filename, "/settings.msgpack~")
|
||||
|
||||
MAKE_PSTR(logger_name, "settings")
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// clang-format off
|
||||
#define EMSESP_SETTINGS_DATA \
|
||||
EMSESP_SETTINGS_SIMPLE(std::string, "", app_version, "", (), "2.0") \
|
||||
EMSESP_SETTINGS_SIMPLE(std::string, "", admin_password, "", (), EMSESP_DEFAULT_ADMIN_PASSWORD) \
|
||||
EMSESP_SETTINGS_SIMPLE(std::string, "", hostname, "", (), EMSESP_DEFAULT_HOSTNAME) \
|
||||
EMSESP_SETTINGS_SIMPLE(std::string, "", wifi_ssid, "", (), "") \
|
||||
EMSESP_SETTINGS_SIMPLE(std::string, "", wifi_password, "", (), "") \
|
||||
EMSESP_SETTINGS_CUSTOM(std::string, "", syslog_host, "", (), "") \
|
||||
EMSESP_SETTINGS_ENUM(uuid::log::Level, "", syslog_level, "", (), uuid::log::Level::OFF) \
|
||||
EMSESP_SETTINGS_SIMPLE(unsigned long, "", syslog_mark_interval, "", (), EMSESP_DEFAULT_SYSLOG_INTERVAL) \
|
||||
EMSESP_SETTINGS_SIMPLE(uint8_t, "", ems_bus_id, "", (), EMSESP_DEFAULT_BUS_ID) \
|
||||
EMSESP_SETTINGS_SIMPLE(uint8_t, "", ems_tx_mode, "", (), EMSESP_DEFAULT_TX_MODE) \
|
||||
EMSESP_SETTINGS_SIMPLE(bool, "", ems_read_only, "", (), EMSESP_DEFAULT_EMS_READ_ONLY) \
|
||||
EMSESP_SETTINGS_SIMPLE(bool, "", shower_timer, "", (), EMSESP_DEFAULT_SHOWER_TIMER) \
|
||||
EMSESP_SETTINGS_SIMPLE(bool, "", shower_alert, "", (), EMSESP_DEFAULT_SHOWER_ALERT) \
|
||||
EMSESP_SETTINGS_SIMPLE(uint8_t, "", master_thermostat, "", (), EMSESP_DEFAULT_MASTER_THERMOSTAT) \
|
||||
EMSESP_SETTINGS_SIMPLE(uint16_t, "", mqtt_publish_time, "", (), EMSESP_DEFAULT_MQTT_PUBLISH_TIME) \
|
||||
EMSESP_SETTINGS_SIMPLE(std::string, "", mqtt_ip, "", (), "") \
|
||||
EMSESP_SETTINGS_SIMPLE(std::string, "", mqtt_user, "", (), "") \
|
||||
EMSESP_SETTINGS_SIMPLE(std::string, "", mqtt_password, "", (), "") \
|
||||
EMSESP_SETTINGS_SIMPLE(uint16_t, "", mqtt_port, "", (), EMSESP_DEFAULT_MQTT_PORT) \
|
||||
EMSESP_SETTINGS_SIMPLE(bool, "", mqtt_enabled, "", (), EMSESP_DEFAULT_MQTT_ENABLED) \
|
||||
EMSESP_SETTINGS_SIMPLE(std::string, "", mqtt_base, "", (), EMSESP_DEFAULT_MQTT_BASE) \
|
||||
EMSESP_SETTINGS_SIMPLE(uint8_t, "", mqtt_qos, "", (), EMSESP_DEFAULT_MQTT_QOS) \
|
||||
EMSESP_SETTINGS_SIMPLE(bool, "", mqtt_retain, "", (), EMSESP_DEFAULT_MQTT_RETAIN) \
|
||||
EMSESP_SETTINGS_SIMPLE(uint8_t, "", mqtt_format, "", (), EMSESP_DEFAULT_MQTT_FORMAT) \
|
||||
EMSESP_SETTINGS_SIMPLE(bool, "", mqtt_heartbeat, "", (), EMSESP_DEFAULT_MQTT_HEARTBEAT)
|
||||
|
||||
#define EMSESP_SETTINGS_SIMPLE EMSESP_SETTINGS_GENERIC
|
||||
#define EMSESP_SETTINGS_CUSTOM EMSESP_SETTINGS_GENERIC
|
||||
#define EMSESP_SETTINGS_ENUM EMSESP_SETTINGS_GENERIC
|
||||
|
||||
/* Create member data and flash strings */
|
||||
#define EMSESP_SETTINGS_GENERIC(__type, __key_prefix, __name, __key_suffix, __get_function, __read_default, ...) \
|
||||
__type Settings::__name##_; \
|
||||
MAKE_PSTR(__name, __key_prefix #__name __key_suffix)
|
||||
EMSESP_SETTINGS_DATA
|
||||
#undef EMSESP_SETTINGS_GENERIC
|
||||
|
||||
#undef EMSESP_SETTINGS_ENUM
|
||||
|
||||
void Settings::read_settings(const ArduinoJson::JsonDocument &doc) {
|
||||
#define EMSESP_SETTINGS_GENERIC(__type, __key_prefix, __name, __key_suffix, __get_function, __read_default, ...) \
|
||||
__name(doc[FPSTR(__pstr__##__name)] | __read_default, ##__VA_ARGS__);
|
||||
#define EMSESP_SETTINGS_ENUM(__type, __key_prefix, __name, __key_suffix, __get_function, __read_default, ...) \
|
||||
__name(static_cast<__type>(doc[FPSTR(__pstr__##__name)] | static_cast<int>(__read_default)), ##__VA_ARGS__);
|
||||
EMSESP_SETTINGS_DATA
|
||||
#undef EMSESP_SETTINGS_GENERIC
|
||||
#undef EMSESP_SETTINGS_ENUM
|
||||
}
|
||||
|
||||
void Settings::write_settings(ArduinoJson::JsonDocument &doc) {
|
||||
#define EMSESP_SETTINGS_GENERIC(__type, __key_prefix, __name, __key_suffix, __get_function, __read_default, ...) \
|
||||
doc[FPSTR(__pstr__##__name)] = __name __get_function;
|
||||
#define EMSESP_SETTINGS_ENUM(__type, __key_prefix, __name, __key_suffix, __get_function, __read_default, ...) \
|
||||
doc[FPSTR(__pstr__##__name)] = static_cast<int>(__name __get_function);
|
||||
EMSESP_SETTINGS_DATA
|
||||
#undef EMSESP_SETTINGS_GENERIC
|
||||
}
|
||||
|
||||
#undef EMSESP_SETTINGS_GENERIC
|
||||
#undef EMSESP_SETTINGS_SIMPLE
|
||||
#undef EMSESP_SETTINGS_CUSTOM
|
||||
#undef EMSESP_SETTINGS_ENUM
|
||||
|
||||
/* Create getters/setters for simple settings items only */
|
||||
#define EMSESP_SETTINGS_SIMPLE(__type, __key_prefix, __name, __key_suffix, __get_function, __read_default, ...) \
|
||||
__type Settings::__name() const { \
|
||||
return __name##_; \
|
||||
} \
|
||||
void Settings::__name(const __type &__name) { \
|
||||
__name##_ = __name; \
|
||||
}
|
||||
#define EMSESP_SETTINGS_ENUM(__type, __key_prefix, __name, __key_suffix, __get_function, __read_default, ...) \
|
||||
__type Settings::__name() const { \
|
||||
return __name##_; \
|
||||
} \
|
||||
void Settings::__name(__type __name) { \
|
||||
__name##_ = __name; \
|
||||
}
|
||||
|
||||
/* Create getters for settings items with custom setters */
|
||||
#define EMSESP_SETTINGS_CUSTOM(__type, __key_prefix, __name, __key_suffix, __get_function, __read_default, ...) \
|
||||
__type Settings::__name() const { \
|
||||
return __name##_; \
|
||||
}
|
||||
|
||||
EMSESP_SETTINGS_DATA
|
||||
|
||||
#undef EMSESP_SETTINGS_SIMPLE
|
||||
#undef EMSESP_SETTINGS_CUSTOM
|
||||
#undef EMSESP_SETTINGS_ENUM
|
||||
// clang-format on
|
||||
|
||||
uuid::log::Logger Settings::logger_{F_(logger_name), uuid::log::Facility::DAEMON};
|
||||
|
||||
// Settings which we don't persist
|
||||
bool Settings::mounted_ = false;
|
||||
bool Settings::unavailable_ = false;
|
||||
bool Settings::loaded_ = false;
|
||||
|
||||
Settings::Settings() {
|
||||
if (!unavailable_ && !mounted_) {
|
||||
EMSuart::stop(); // temporary suspend UART because is can cause interference on the UART
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
if (true) {
|
||||
#else
|
||||
#if defined(ESP8266)
|
||||
if (EMSESP_FS.begin()) {
|
||||
#elif defined(ESP32)
|
||||
if (EMSESP_FS.begin(true)) {
|
||||
#endif
|
||||
#endif
|
||||
LOG_INFO(F("Mounted filesystem"));
|
||||
mounted_ = true;
|
||||
} else {
|
||||
LOG_ERROR(F("Unable to mount filesystem"));
|
||||
unavailable_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mounted_ && !loaded_) {
|
||||
if (read_settings(uuid::read_flash_string(F_(settings_filename))) || read_settings(uuid::read_flash_string(F_(settings_backup_filename)))) {
|
||||
loaded_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!loaded_) {
|
||||
LOG_ERROR(F("Failed to load settings. Using defaults"));
|
||||
read_settings(ArduinoJson::StaticJsonDocument<0>());
|
||||
loaded_ = true;
|
||||
}
|
||||
EMSuart::restart();
|
||||
}
|
||||
|
||||
// save settings to FS
|
||||
void Settings::commit() {
|
||||
if (mounted_) {
|
||||
std::string filename = uuid::read_flash_string(F_(settings_filename));
|
||||
std::string backup_filename = uuid::read_flash_string(F_(settings_backup_filename));
|
||||
|
||||
EMSuart::stop(); // temporary suspend UART because is can cause interference on the UART
|
||||
|
||||
LOG_DEBUG(F("Saving settings"));
|
||||
if (write_settings(filename)) {
|
||||
if (read_settings(filename, false)) {
|
||||
write_settings(backup_filename);
|
||||
}
|
||||
}
|
||||
|
||||
EMSuart::restart(); // re-enable EMS bus
|
||||
}
|
||||
}
|
||||
|
||||
// read the settings from FS
|
||||
bool Settings::read_settings(const std::string & filename, bool load) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
File file = EMSESP_FS.open(filename.c_str(), "r");
|
||||
if (file) {
|
||||
ArduinoJson::DynamicJsonDocument doc(BUFFER_SIZE);
|
||||
|
||||
auto error = ArduinoJson::deserializeMsgPack(doc, file);
|
||||
|
||||
if (error) {
|
||||
LOG_ERROR(F("Failed to parse settings file %s: %s"), filename.c_str(), error.c_str());
|
||||
return false;
|
||||
} else {
|
||||
if (load) {
|
||||
LOG_INFO(F("Loading settings from file %s"), filename.c_str());
|
||||
read_settings(doc);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR(F("Settings file %s does not exist"), filename.c_str());
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
LOG_INFO(F("Loading settings from file %s (%d)"), filename.c_str(), load);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// write settings from FS
|
||||
bool Settings::write_settings(const std::string & filename) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
File file = EMSESP_FS.open(filename.c_str(), "w");
|
||||
if (file) {
|
||||
ArduinoJson::DynamicJsonDocument doc(BUFFER_SIZE);
|
||||
|
||||
write_settings(doc);
|
||||
|
||||
ArduinoJson::serializeMsgPack(doc, file);
|
||||
|
||||
if (file.getWriteError()) {
|
||||
LOG_ERROR(F("Failed to write settings file %s: %u"), filename.c_str(), file.getWriteError());
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR(F("Unable to open settings file %s for writing"), filename.c_str());
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
LOG_DEBUG(F("Write settings file %s"), filename.c_str());
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// print the settings JSON to console (only in admin mode)
|
||||
void Settings::show_settings(uuid::console::Shell & shell) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
if (shell.has_flags(CommandFlags::ADMIN)) {
|
||||
shell.printf(F("[DEBUG] Settings JSON: "));
|
||||
ArduinoJson::DynamicJsonDocument doc(BUFFER_SIZE);
|
||||
write_settings(doc);
|
||||
ArduinoJson::serializeJson(doc, shell);
|
||||
shell.println();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
// format the FS. Wipes everything.
|
||||
void Settings::format(uuid::console::Shell & shell) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#if defined(ESP8266)
|
||||
EMSuart::stop();
|
||||
if (EMSESP_FS.begin()) {
|
||||
#elif defined(ESP32)
|
||||
if (EMSESP_FS.begin(true)) {
|
||||
#endif
|
||||
if (EMSESP_FS.remove(uuid::read_flash_string(F_(settings_filename)).c_str())) {
|
||||
(void)EMSESP_FS.remove(uuid::read_flash_string(F_(settings_backup_filename)).c_str());
|
||||
auto msg = F("Reset settings file to defaults");
|
||||
shell.logger().warning(msg);
|
||||
shell.flush();
|
||||
System::restart(false);
|
||||
} else {
|
||||
auto msg = F("Error removing settings file");
|
||||
shell.logger().emerg(msg);
|
||||
}
|
||||
} else {
|
||||
auto msg = F("Unable to mount filesystem");
|
||||
shell.logger().alert(msg);
|
||||
}
|
||||
#if defined(ESP8266)
|
||||
EMSuart::restart();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void Settings::syslog_host(const std::string & syslog_host) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
IPAddress addr;
|
||||
|
||||
if (addr.fromString(syslog_host.c_str())) {
|
||||
syslog_host_ = syslog_host;
|
||||
} else {
|
||||
syslog_host_.clear();
|
||||
}
|
||||
#else
|
||||
syslog_host_ = std::string("99.99.99.99");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
204
src/settings.h
204
src/settings.h
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/proddy/EMS-ESP
|
||||
* Copyright 2019 Paul Derbyshire
|
||||
*
|
||||
* 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 EMSESP_SETTINGS_H
|
||||
#define EMSESP_SETTINGS_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// we use LittleFS on the ESP8266 and the older SPIFFS on the ESP32
|
||||
#if defined(ESP8266)
|
||||
#include <FS.h>
|
||||
#include <LittleFS.h>
|
||||
#define EMSESP_FS LittleFS
|
||||
#elif defined(ESP32)
|
||||
#include <SPIFFS.h>
|
||||
#define EMSESP_FS SPIFFS
|
||||
#endif
|
||||
|
||||
#include <uuid/log.h>
|
||||
#include <uuid/console.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "console.h"
|
||||
|
||||
// default settings - these can be customized from within the application
|
||||
#define EMSESP_DEFAULT_HOSTNAME "ems-esp"
|
||||
#define EMSESP_DEFAULT_ADMIN_PASSWORD "neo"
|
||||
#define EMSESP_DEFAULT_BUS_ID 0x0B
|
||||
#define EMSESP_DEFAULT_TX_MODE 1
|
||||
#define EMSESP_DEFAULT_MQTT_ENABLED true
|
||||
#define EMSESP_DEFAULT_MQTT_BASE "home"
|
||||
#define EMSESP_DEFAULT_MQTT_PORT 1883
|
||||
#define EMSESP_DEFAULT_MQTT_QOS 1
|
||||
#define EMSESP_DEFAULT_MQTT_RETAIN false
|
||||
#define EMSESP_DEFAULT_MQTT_FORMAT 2 // 2=nested
|
||||
#define EMSESP_DEFAULT_MQTT_HEARTBEAT true
|
||||
#define EMSESP_DEFAULT_EMS_READ_ONLY false
|
||||
#define EMSESP_DEFAULT_SHOWER_TIMER false
|
||||
#define EMSESP_DEFAULT_SHOWER_ALERT false
|
||||
#define EMSESP_DEFAULT_SYSLOG_INTERVAL 0
|
||||
#define EMSESP_DEFAULT_MASTER_THERMOSTAT 0 // 0=not set
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#define EMSESP_DEFAULT_MQTT_PUBLISH_TIME 10
|
||||
#else
|
||||
#define EMSESP_DEFAULT_MQTT_PUBLISH_TIME 0
|
||||
#endif
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class Settings {
|
||||
public:
|
||||
Settings();
|
||||
~Settings() = default;
|
||||
|
||||
void commit();
|
||||
void show_settings(uuid::console::Shell & shell);
|
||||
void format(uuid::console::Shell & shell);
|
||||
|
||||
std::string app_version() const;
|
||||
void app_version(const std::string & app_version);
|
||||
|
||||
std::string admin_password() const;
|
||||
void admin_password(const std::string & admin_password);
|
||||
|
||||
std::string hostname() const;
|
||||
void hostname(const std::string & hostname);
|
||||
|
||||
std::string wifi_ssid() const;
|
||||
void wifi_ssid(const std::string & wifi_ssid);
|
||||
|
||||
std::string wifi_password() const;
|
||||
void wifi_password(const std::string & wifi_password);
|
||||
|
||||
std::string syslog_host() const;
|
||||
void syslog_host(const std::string & syslog_host);
|
||||
|
||||
uuid::log::Level syslog_level() const;
|
||||
void syslog_level(uuid::log::Level syslog_level);
|
||||
|
||||
unsigned long syslog_mark_interval() const;
|
||||
void syslog_mark_interval(const unsigned long & syslog_mark_interval);
|
||||
|
||||
uint8_t ems_bus_id() const;
|
||||
void ems_bus_id(const uint8_t & ems_bus_id);
|
||||
|
||||
uint8_t ems_tx_mode() const;
|
||||
void ems_tx_mode(const uint8_t & ems_tx_mode);
|
||||
|
||||
bool ems_read_only() const;
|
||||
void ems_read_only(const bool & ems_read_only);
|
||||
|
||||
uint16_t mqtt_publish_time() const;
|
||||
void mqtt_publish_time(const uint16_t & mqtt_publish_time);
|
||||
|
||||
std::string mqtt_ip() const;
|
||||
void mqtt_ip(const std::string & mqtt_ip);
|
||||
|
||||
std::string mqtt_user() const;
|
||||
void mqtt_user(const std::string & mqtt_user);
|
||||
|
||||
std::string mqtt_password() const;
|
||||
void mqtt_password(const std::string & mqtt_password);
|
||||
|
||||
uint16_t mqtt_port() const;
|
||||
void mqtt_port(const uint16_t & mqtt_port);
|
||||
|
||||
bool mqtt_enabled() const;
|
||||
void mqtt_enabled(const bool & mqtt_enabled);
|
||||
|
||||
std::string mqtt_base() const;
|
||||
void mqtt_base(const std::string & mqtt_base);
|
||||
|
||||
uint8_t mqtt_qos() const;
|
||||
void mqtt_qos(const uint8_t & mqtt_qos);
|
||||
|
||||
bool mqtt_retain() const;
|
||||
void mqtt_retain(const bool & mqtt_retain);
|
||||
|
||||
bool mqtt_heartbeat() const;
|
||||
void mqtt_heartbeat(const bool & mqtt_heartbeat);
|
||||
|
||||
bool shower_timer() const;
|
||||
void shower_timer(const bool & shower_timer);
|
||||
|
||||
bool shower_alert() const;
|
||||
void shower_alert(const bool & shower_alert);
|
||||
|
||||
uint8_t master_thermostat() const;
|
||||
void master_thermostat(const uint8_t & master_thermostat);
|
||||
|
||||
enum MQTT_format : uint8_t { SINGLE = 1, NESTED, HA, CUSTOM };
|
||||
uint8_t mqtt_format() const;
|
||||
void mqtt_format(const uint8_t & mqtt_format);
|
||||
|
||||
private:
|
||||
static constexpr size_t BUFFER_SIZE = 2048; // max size for the settings file
|
||||
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
// global, not saved
|
||||
static bool mounted_;
|
||||
static bool unavailable_;
|
||||
static bool loaded_;
|
||||
|
||||
bool read_settings(const std::string & filename, bool load = true);
|
||||
bool write_settings(const std::string & filename);
|
||||
void read_settings(const ArduinoJson::JsonDocument & doc);
|
||||
void write_settings(ArduinoJson::JsonDocument & doc);
|
||||
|
||||
static std::string app_version_;
|
||||
|
||||
static std::string admin_password_;
|
||||
static std::string hostname_;
|
||||
static std::string wifi_password_;
|
||||
static std::string wifi_ssid_;
|
||||
|
||||
static std::string syslog_host_;
|
||||
static uuid::log::Level syslog_level_;
|
||||
static unsigned long syslog_mark_interval_;
|
||||
|
||||
static bool shower_timer_; // true if we want to report back on shower times
|
||||
static bool shower_alert_; // true if we want the alert of cold water
|
||||
|
||||
static uint8_t master_thermostat_; // which thermostat device ID is leading
|
||||
|
||||
static uint8_t ems_tx_mode_; // Tx mode 1, 2 or 3
|
||||
static uint8_t ems_bus_id_; // EMS bus ID, default 0x0B for Service Key
|
||||
static bool ems_read_only_; // switch of Tx
|
||||
|
||||
static std::string mqtt_ip_;
|
||||
static std::string mqtt_user_;
|
||||
static std::string mqtt_password_;
|
||||
static uint16_t mqtt_port_;
|
||||
static bool mqtt_enabled_;
|
||||
static std::string mqtt_base_;
|
||||
static uint8_t mqtt_qos_;
|
||||
static bool mqtt_retain_;
|
||||
static bool mqtt_heartbeat_;
|
||||
static uint16_t mqtt_publish_time_; // seconds
|
||||
static uint8_t mqtt_format_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
@@ -25,10 +25,10 @@ namespace emsesp {
|
||||
uuid::log::Logger Shower::logger_{F_(logger_name), uuid::log::Facility::CONSOLE};
|
||||
|
||||
void Shower::start() {
|
||||
Settings settings;
|
||||
|
||||
shower_timer_ = settings.shower_timer();
|
||||
shower_alert_ = settings.shower_alert();
|
||||
EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) {
|
||||
shower_timer_ = settings.shower_timer;
|
||||
shower_alert_ = settings.shower_alert;
|
||||
});
|
||||
}
|
||||
|
||||
void Shower::loop() {
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "settings.h"
|
||||
#include "console.h"
|
||||
#include "mqtt.h"
|
||||
#include "telegram.h"
|
||||
@@ -31,7 +30,6 @@
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class Shower {
|
||||
|
||||
504
src/system.cpp
504
src/system.cpp
@@ -19,22 +19,20 @@
|
||||
#include "system.h"
|
||||
#include "emsesp.h" // for send_raw_telegram() command
|
||||
|
||||
#include "version.h" // version of EMS-ESP
|
||||
#include "version.h" // firmware version of EMS-ESP
|
||||
|
||||
MAKE_PSTR_WORD(syslog)
|
||||
MAKE_PSTR_WORD(mark)
|
||||
MAKE_PSTR_WORD(level)
|
||||
MAKE_PSTR_WORD(host)
|
||||
MAKE_PSTR_WORD(passwd)
|
||||
MAKE_PSTR_WORD(hostname)
|
||||
MAKE_PSTR_WORD(wifi)
|
||||
MAKE_PSTR_WORD(ssid)
|
||||
MAKE_PSTR_WORD(heartbeat)
|
||||
|
||||
MAKE_PSTR(host_fmt, "Host = %s")
|
||||
MAKE_PSTR(hostname_fmt, "Hostname = %s")
|
||||
MAKE_PSTR(mark_interval_fmt, "Mark interval = %lus");
|
||||
MAKE_PSTR(wifi_ssid_fmt, "WiFi SSID = %s");
|
||||
MAKE_PSTR(wifi_password_fmt, "WiFi Password = %S")
|
||||
MAKE_PSTR(system_heartbeat_fmt, "Heartbeat = %s")
|
||||
|
||||
MAKE_PSTR(logger_name, "system")
|
||||
|
||||
@@ -46,12 +44,7 @@ uuid::log::Logger System::logger_{F_(logger_name), uuid::log::Facility::KERN};
|
||||
uuid::syslog::SyslogService System::syslog_;
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266)
|
||||
RTCVars System::state_;
|
||||
#endif
|
||||
|
||||
uint32_t System::heap_start_ = 0;
|
||||
bool System::safe_mode_ = false;
|
||||
int System::reset_counter_;
|
||||
|
||||
// handle generic system related MQTT commands
|
||||
@@ -63,12 +56,6 @@ void System::mqtt_commands(const char * message) {
|
||||
return;
|
||||
}
|
||||
|
||||
// restart EMS-ESP
|
||||
if (strcmp(message, "restart") == 0) {
|
||||
LOG_INFO(F("Restart command received"));
|
||||
restart();
|
||||
}
|
||||
|
||||
if (doc["send"] != nullptr) {
|
||||
const char * data = doc["send"];
|
||||
EMSESP::send_raw_telegram(data);
|
||||
@@ -153,17 +140,10 @@ void System::mqtt_commands(const char * message) {
|
||||
}
|
||||
|
||||
// restart EMS-ESP
|
||||
// mode = safe mode. true to enable on next boot
|
||||
void System::restart(bool mode) {
|
||||
// check for safe mode
|
||||
if (mode) {
|
||||
LOG_NOTICE("Restarting system in safe mode...");
|
||||
} else {
|
||||
LOG_NOTICE("Restarting system...");
|
||||
}
|
||||
void System::restart() {
|
||||
LOG_NOTICE("Restarting system...");
|
||||
|
||||
Shell::loop_all();
|
||||
save_safe_mode(mode);
|
||||
delay(1000); // wait a second
|
||||
#if defined(ESP8266)
|
||||
ESP.reset();
|
||||
@@ -172,6 +152,27 @@ void System::restart(bool mode) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// format fs
|
||||
// format the FS. Wipes everything.
|
||||
void System::format(uuid::console::Shell & shell) {
|
||||
auto msg = F("Resetting all settings to defaults");
|
||||
shell.logger().warning(msg);
|
||||
shell.flush();
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
EMSuart::stop();
|
||||
|
||||
#if defined(ESP8266)
|
||||
LittleFS.format();
|
||||
#elif defined(ESP32)
|
||||
SPIFFS.format();
|
||||
#endif
|
||||
|
||||
System::restart();
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// return free heap mem as a percentage
|
||||
uint8_t System::free_mem() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
@@ -183,6 +184,35 @@ uint8_t System::free_mem() {
|
||||
return (100 * free_memory / heap_start_);
|
||||
}
|
||||
|
||||
void System::syslog_init() {
|
||||
// fetch settings
|
||||
EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) {
|
||||
syslog_level_ = settings.syslog_level;
|
||||
syslog_mark_interval_ = settings.syslog_mark_interval;
|
||||
syslog_host_ = settings.syslog_host;
|
||||
});
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
syslog_.start(); // syslog service
|
||||
|
||||
// configure syslog
|
||||
IPAddress addr;
|
||||
|
||||
if (!addr.fromString(syslog_host_.c_str())) {
|
||||
addr = (uint32_t)0;
|
||||
}
|
||||
|
||||
EMSESP::esp8266React.getWiFiSettingsService()->read([&](WiFiSettings & wifiSettings) { syslog_.hostname(wifiSettings.hostname.c_str()); });
|
||||
syslog_.log_level((uuid::log::Level)syslog_level_);
|
||||
syslog_.mark_interval(syslog_mark_interval_);
|
||||
syslog_.destination(addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void System::set_heartbeat(bool system_heartbeat) {
|
||||
system_heartbeat_ = system_heartbeat;
|
||||
}
|
||||
|
||||
// first call. Sets memory and starts up the UART Serial bridge
|
||||
void System::start() {
|
||||
// set the inital free mem
|
||||
@@ -194,93 +224,30 @@ void System::start() {
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
syslog_.start(); // syslog service
|
||||
#endif
|
||||
// fetch settings
|
||||
EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) { tx_mode_ = settings.tx_mode; });
|
||||
|
||||
config_syslog();
|
||||
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) { system_heartbeat_ = settings.system_heartbeat; });
|
||||
|
||||
Settings settings;
|
||||
|
||||
// update the version to the latest build
|
||||
settings.app_version(EMSESP_APP_VERSION);
|
||||
settings.commit();
|
||||
syslog_init();
|
||||
|
||||
#if defined(ESP32)
|
||||
LOG_INFO(F("System booted (EMS-ESP version %s ESP32)"), settings.app_version().c_str());
|
||||
LOG_INFO(F("System booted (EMS-ESP version %s ESP32)"), EMSESP_APP_VERSION);
|
||||
#else
|
||||
LOG_INFO(F("System booted (EMS-ESP version %s)"), settings.app_version().c_str());
|
||||
LOG_INFO(F("System booted (EMS-ESP version %s)"), EMSESP_APP_VERSION);
|
||||
#endif
|
||||
|
||||
if (LED_GPIO) {
|
||||
pinMode(LED_GPIO, OUTPUT); // LED pin, 0 is disabled
|
||||
pinMode(LED_GPIO, OUTPUT); // LED pin, 0 means disabled
|
||||
}
|
||||
|
||||
// register MQTT system commands
|
||||
Mqtt::subscribe("cmd", std::bind(&System::mqtt_commands, this, _1));
|
||||
|
||||
// RTC state variables - only for ESP8266
|
||||
#if defined(ESP8266)
|
||||
state_.registerVar(&reset_counter_); // we send a pointer to each of our variables
|
||||
state_.registerVar(&safe_mode_);
|
||||
state_.loadFromRTC();
|
||||
#endif
|
||||
|
||||
// safe mode is forced at compile time
|
||||
#ifdef EMSESP_SAFE_MODE
|
||||
safe_mode_ = true;
|
||||
#endif
|
||||
|
||||
// if there is no wifi ssid set (like an inital setup) go into safe mode
|
||||
if (settings.wifi_ssid().empty()) {
|
||||
safe_mode_ = true;
|
||||
#ifndef EMSESP_FORCE_SERIAL
|
||||
if (tx_mode_) {
|
||||
EMSuart::start(tx_mode_); // start UART, if tx_mode is not 0
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// if we're in safe switch is back so next boot it'll be normal. Safe mode is a one-off
|
||||
if (safe_mode()) {
|
||||
} else {
|
||||
save_safe_mode(false); // next time boot up in normal mode
|
||||
EMSuart::start(settings.ems_tx_mode());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// gets/sets the safe mode from RTC
|
||||
bool System::safe_mode() {
|
||||
return safe_mode_;
|
||||
}
|
||||
|
||||
// set safe mode and save it to RTC
|
||||
void System::safe_mode(const bool safe_mode) {
|
||||
safe_mode_ = safe_mode;
|
||||
}
|
||||
|
||||
// set safe mode and save it to RTC
|
||||
void System::save_safe_mode(const bool safe_mode) {
|
||||
bool curr_save_mode = safe_mode_; // remember current setting
|
||||
safe_mode_ = safe_mode;
|
||||
#if defined(ESP8266)
|
||||
state_.saveToRTC();
|
||||
#endif
|
||||
safe_mode_ = curr_save_mode; // restore
|
||||
}
|
||||
|
||||
// sets up syslog
|
||||
void System::config_syslog() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
Settings settings;
|
||||
IPAddress addr;
|
||||
|
||||
if (!addr.fromString(settings.syslog_host().c_str())) {
|
||||
addr = (uint32_t)0;
|
||||
}
|
||||
|
||||
syslog_.hostname(settings.hostname());
|
||||
syslog_.log_level(settings.syslog_level());
|
||||
syslog_.mark_interval(settings.syslog_mark_interval());
|
||||
syslog_.destination(addr);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -289,21 +256,33 @@ void System::loop() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
syslog_.loop();
|
||||
#endif
|
||||
|
||||
if (LED_GPIO) {
|
||||
led_monitor(); // check status and report back using the LED
|
||||
}
|
||||
|
||||
led_monitor(); // check status and report back using the LED
|
||||
system_check(); // check system health
|
||||
|
||||
// send out heartbeat
|
||||
uint32_t currentMillis = uuid::get_uptime();
|
||||
if (!last_heartbeat_ || (currentMillis - last_heartbeat_ > SYSTEM_HEARTBEAT_INTERVAL)) {
|
||||
last_heartbeat_ = currentMillis;
|
||||
if (system_heartbeat_) {
|
||||
send_heartbeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void System::show_mem(const char * text) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
uint32_t mem = ESP.getFreeHeap();
|
||||
#else
|
||||
uint32_t mem = 1000;
|
||||
#endif
|
||||
LOG_NOTICE(F("{%s} Free mem: %ld (%d%%)"), text, mem, (100 * mem / heap_start_));
|
||||
// send periodic MQTT message with system information
|
||||
void System::send_heartbeat() {
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
|
||||
int rssid = wifi_quality();
|
||||
if (rssid != -1) {
|
||||
doc["rssid"] = rssid;
|
||||
}
|
||||
doc["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||
doc["uptime_sec"] = uuid::get_uptime_sec();
|
||||
doc["freemem"] = free_mem();
|
||||
doc["mqttpublishfails"] = Mqtt::publish_fails();
|
||||
|
||||
Mqtt::publish("heartbeat", doc, false); // send to MQTT with retain off
|
||||
}
|
||||
|
||||
// sets rate of led flash
|
||||
@@ -320,7 +299,7 @@ void System::system_check() {
|
||||
last_system_check_ = uuid::get_uptime();
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if ((WiFi.status() != WL_CONNECTED) || safe_mode()) {
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
set_led_speed(LED_WARNING_BLINK_FAST);
|
||||
system_healthy_ = false;
|
||||
return;
|
||||
@@ -331,7 +310,7 @@ void System::system_check() {
|
||||
if (!EMSbus::bus_connected()) {
|
||||
system_healthy_ = false;
|
||||
set_led_speed(LED_WARNING_BLINK); // flash every 1/2 second from now on
|
||||
LOG_ERROR(F("No connection to the EMS bus!"));
|
||||
// LOG_ERROR(F("Error: No connection to the EMS bus"));
|
||||
} 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_) {
|
||||
@@ -346,6 +325,10 @@ void System::system_check() {
|
||||
|
||||
// flashes the LED
|
||||
void System::led_monitor() {
|
||||
if (!LED_GPIO) {
|
||||
return;
|
||||
}
|
||||
|
||||
static uint32_t led_last_blink_ = 0;
|
||||
|
||||
if (!led_last_blink_ || (uint32_t)(uuid::get_uptime() - led_last_blink_) >= led_flash_speed_) {
|
||||
@@ -358,6 +341,29 @@ void System::led_monitor() {
|
||||
}
|
||||
}
|
||||
|
||||
// Return the quality (Received Signal Strength Indicator) of the WiFi network as a %. Or -1 if disconnected.
|
||||
// High quality: 90% ~= -55dBm
|
||||
// Medium quality: 50% ~= -75dBm
|
||||
// Low quality: 30% ~= -85dBm
|
||||
// Unusable quality: 8% ~= -96dBm
|
||||
int8_t System::wifi_quality() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
return -1;
|
||||
}
|
||||
int dBm = WiFi.RSSI();
|
||||
#else
|
||||
int8_t dBm = -70;
|
||||
#endif
|
||||
if (dBm <= -100) {
|
||||
return 0;
|
||||
}
|
||||
if (dBm >= -50) {
|
||||
return 100;
|
||||
}
|
||||
return 2 * (dBm + 100);
|
||||
}
|
||||
|
||||
void System::show_system(uuid::console::Shell & shell) {
|
||||
shell.print(F("Uptime: "));
|
||||
shell.print(uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3));
|
||||
@@ -381,19 +387,70 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
shell.printfln(F("Maximum free block size: %lu bytes"), (unsigned long)ESP.getMaxFreeBlockSize());
|
||||
shell.printfln(F("Heap fragmentation: %u%"), ESP.getHeapFragmentation());
|
||||
shell.printfln(F("Free continuations stack: %lu bytes"), (unsigned long)ESP.getFreeContStack());
|
||||
|
||||
FSInfo info;
|
||||
if (EMSESP_FS.info(info)) {
|
||||
shell.printfln(F("FS size: %zu bytes (block size %zu bytes, page size %zu bytes)"), info.totalBytes, info.blockSize, info.pageSize);
|
||||
shell.printfln(F("FS used: %zu bytes"), info.usedBytes);
|
||||
}
|
||||
#elif defined(ESP32)
|
||||
|
||||
shell.printfln(F("SDK version: %s"), ESP.getSdkVersion());
|
||||
shell.printfln(F("CPU frequency: %u MHz"), ESP.getCpuFreqMHz());
|
||||
shell.printfln(F("Sketch size: %u bytes (%u bytes free)"), ESP.getSketchSize(), ESP.getFreeSketchSpace());
|
||||
shell.printfln(F("Free heap: %lu bytes"), (unsigned long)ESP.getFreeHeap());
|
||||
shell.printfln(F("Free mem: %d %%"), free_mem());
|
||||
#endif
|
||||
|
||||
shell.println();
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
|
||||
switch (WiFi.status()) {
|
||||
case WL_IDLE_STATUS:
|
||||
shell.printfln(F("WiFi: idle"));
|
||||
break;
|
||||
|
||||
case WL_NO_SSID_AVAIL:
|
||||
shell.printfln(F("WiFi: network not found"));
|
||||
break;
|
||||
|
||||
case WL_SCAN_COMPLETED:
|
||||
shell.printfln(F("WiFi: network scan complete"));
|
||||
break;
|
||||
|
||||
case WL_CONNECTED: {
|
||||
shell.printfln(F("WiFi: connected"));
|
||||
shell.println();
|
||||
|
||||
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());
|
||||
shell.println();
|
||||
|
||||
shell.printfln(F("MAC address: %s"), WiFi.macAddress().c_str());
|
||||
#if defined(ESP8266)
|
||||
shell.printfln(F("Hostname: %s"), WiFi.hostname().c_str());
|
||||
#elif defined(ESP32)
|
||||
shell.printfln(F("Hostname: %s"), WiFi.getHostname());
|
||||
#endif
|
||||
|
||||
shell.println();
|
||||
shell.printfln(F("IPv4 address: %s/%s"), uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str());
|
||||
shell.printfln(F("IPv4 gateway: %s"), uuid::printable_to_string(WiFi.gatewayIP()).c_str());
|
||||
shell.printfln(F("IPv4 nameserver: %s"), uuid::printable_to_string(WiFi.dnsIP()).c_str());
|
||||
} break;
|
||||
|
||||
case WL_CONNECT_FAILED:
|
||||
shell.printfln(F("WiFi: connection failed"));
|
||||
break;
|
||||
|
||||
case WL_CONNECTION_LOST:
|
||||
shell.printfln(F("WiFi: connection lost"));
|
||||
break;
|
||||
|
||||
case WL_DISCONNECTED:
|
||||
shell.printfln(F("WiFi: disconnected"));
|
||||
break;
|
||||
|
||||
case WL_NO_SHIELD:
|
||||
default:
|
||||
shell.printfln(F("WiFi: unknown"));
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -413,12 +470,13 @@ void System::console_commands(Shell & shell, unsigned int context) {
|
||||
[](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) {
|
||||
Settings settings;
|
||||
if ((settings.admin_password().empty()) || (!password.empty() && password == settings.admin_password())) {
|
||||
settings.format(shell);
|
||||
} else {
|
||||
shell.println(F("incorrect password"));
|
||||
}
|
||||
EMSESP::esp8266React.getSecuritySettingsService()->read([&](SecuritySettings & securitySettings) {
|
||||
if (securitySettings.jwtSecret.equals(password.c_str())) {
|
||||
format(shell);
|
||||
} else {
|
||||
shell.println(F("incorrect password"));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -433,9 +491,12 @@ void System::console_commands(Shell & shell, unsigned int context) {
|
||||
[password1](Shell & shell, bool completed, const std::string & password2) {
|
||||
if (completed) {
|
||||
if (password1 == password2) {
|
||||
Settings settings;
|
||||
settings.admin_password(password2);
|
||||
settings.commit();
|
||||
EMSESP::esp8266React.getSecuritySettingsService()->update(
|
||||
[&](SecuritySettings & securitySettings) {
|
||||
securitySettings.jwtSecret = password2.c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
shell.println(F("Admin password updated"));
|
||||
} else {
|
||||
shell.println(F("Passwords do not match"));
|
||||
@@ -446,116 +507,40 @@ void System::console_commands(Shell & shell, unsigned int context) {
|
||||
});
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(wifi), F_(disconnect)},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
Network::disconnect();
|
||||
});
|
||||
|
||||
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))) {
|
||||
Network::reconnect();
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(wifi), F_(scan)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { Network::scan(shell); });
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
Network::show_network(shell);
|
||||
show_system(shell);
|
||||
show_system(shell); // has to be static
|
||||
shell.println();
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(syslog), F_(host)},
|
||||
flash_string_vector{F_(ip_address_optional)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
Settings settings;
|
||||
if (!arguments.empty()) {
|
||||
settings.syslog_host(arguments[0]);
|
||||
settings.commit();
|
||||
shell.println(F("Please restart EMS-ESP"));
|
||||
}
|
||||
auto host = settings.syslog_host();
|
||||
shell.printfln(F_(host_fmt), !host.empty() ? host.c_str() : uuid::read_flash_string(F_(unset)).c_str());
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::SYSTEM,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(syslog), F_(level)},
|
||||
flash_string_vector{F_(log_level_optional)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
Settings settings;
|
||||
if (!arguments.empty()) {
|
||||
uuid::log::Level level;
|
||||
|
||||
if (uuid::log::parse_level_lowercase(arguments[0], level)) {
|
||||
settings.syslog_level(level);
|
||||
settings.commit();
|
||||
shell.println(F("Please restart EMS-ESP"));
|
||||
} else {
|
||||
shell.printfln(F_(invalid_log_level));
|
||||
return;
|
||||
}
|
||||
}
|
||||
shell.printfln(F_(log_level_fmt), uuid::log::format_level_uppercase(settings.syslog_level()));
|
||||
},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> std::vector<std::string> {
|
||||
return uuid::log::levels_lowercase();
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(syslog), F_(mark)},
|
||||
flash_string_vector{F_(seconds_optional)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
Settings settings;
|
||||
if (!arguments.empty()) {
|
||||
settings.syslog_mark_interval(String(arguments[0].c_str()).toInt());
|
||||
settings.commit();
|
||||
shell.println(F("Please restart EMS-ESP"));
|
||||
}
|
||||
shell.printfln(F_(mark_interval_fmt), settings.syslog_mark_interval());
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(wifi), F_(hostname)},
|
||||
flash_string_vector{F_(name_mandatory)},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
|
||||
Settings settings;
|
||||
settings.hostname(arguments.front());
|
||||
settings.commit();
|
||||
EMSESP::esp8266React.getWiFiSettingsService()->update(
|
||||
[&](WiFiSettings & wifiSettings) {
|
||||
wifiSettings.hostname = arguments.front().c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::SYSTEM,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(wifi), F_(ssid)},
|
||||
flash_string_vector{F_(name_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
Settings settings;
|
||||
settings.wifi_ssid(arguments.front());
|
||||
settings.commit();
|
||||
shell.printfln(F_(wifi_ssid_fmt), settings.wifi_ssid().empty() ? uuid::read_flash_string(F_(unset)).c_str() : settings.wifi_ssid().c_str());
|
||||
shell.println(F("WiFi SSID updated. Please restart"));
|
||||
Network::reconnect();
|
||||
},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> std::vector<std::string> {
|
||||
Settings settings;
|
||||
return std::vector<std::string>{settings.wifi_ssid()};
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(wifi), F_(ssid)},
|
||||
flash_string_vector{F_(name_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
EMSESP::esp8266React.getWiFiSettingsService()->update(
|
||||
[&](WiFiSettings & wifiSettings) {
|
||||
wifiSettings.ssid = arguments.front().c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
shell.println(F("WiFi SSID updated"));
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
|
||||
CommandFlags::ADMIN,
|
||||
@@ -567,11 +552,13 @@ void System::console_commands(Shell & shell, unsigned int context) {
|
||||
[password1](Shell & shell, bool completed, const std::string & password2) {
|
||||
if (completed) {
|
||||
if (password1 == password2) {
|
||||
Settings settings;
|
||||
settings.wifi_password(password2);
|
||||
settings.commit();
|
||||
shell.println(F("WiFi password updated. Please restart"));
|
||||
Network::reconnect();
|
||||
EMSESP::esp8266React.getWiFiSettingsService()->update(
|
||||
[&](WiFiSettings & wifiSettings) {
|
||||
wifiSettings.password = password2.c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
shell.println(F("WiFi password updated"));
|
||||
} else {
|
||||
shell.println(F("Passwords do not match"));
|
||||
}
|
||||
@@ -581,32 +568,45 @@ void System::console_commands(Shell & shell, unsigned int context) {
|
||||
});
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::SYSTEM,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(set)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
EMSESP::esp8266React.getWiFiSettingsService()->read([&](WiFiSettings & wifiSettings) {
|
||||
shell.printfln(F_(hostname_fmt), wifiSettings.hostname.isEmpty() ? uuid::read_flash_string(F_(unset)).c_str() : wifiSettings.hostname.c_str());
|
||||
});
|
||||
|
||||
if (shell.has_flags(CommandFlags::ADMIN)) {
|
||||
shell.printfln("Wifi:");
|
||||
EMSESP::esp8266React.getWiFiSettingsService()->read([&](WiFiSettings & wifiSettings) {
|
||||
shell.print(" ");
|
||||
shell.printfln(F_(wifi_ssid_fmt), wifiSettings.ssid.isEmpty() ? uuid::read_flash_string(F_(unset)).c_str() : wifiSettings.ssid.c_str());
|
||||
shell.print(" ");
|
||||
shell.printfln(F_(wifi_password_fmt), wifiSettings.ssid.isEmpty() ? F_(unset) : F_(asterisks));
|
||||
});
|
||||
|
||||
EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) {
|
||||
shell.printfln(F("Syslog:"));
|
||||
shell.print(" ");
|
||||
shell.printfln(F_(host_fmt), !settings.syslog_host.isEmpty() ? settings.syslog_host.c_str() : uuid::read_flash_string(F_(unset)).c_str());
|
||||
shell.print(" ");
|
||||
shell.printfln(F_(log_level_fmt), uuid::log::format_level_uppercase(static_cast<uuid::log::Level>(settings.syslog_level)));
|
||||
shell.print(" ");
|
||||
shell.printfln(F_(mark_interval_fmt), settings.syslog_mark_interval);
|
||||
shell.println();
|
||||
bool system_heartbeat;
|
||||
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) { system_heartbeat = settings.system_heartbeat; });
|
||||
shell.printfln(F_(system_heartbeat_fmt), system_heartbeat ? F_(enabled) : F_(disabled));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(set)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
Settings settings;
|
||||
shell.printfln(F_(hostname_fmt),
|
||||
settings.hostname().empty() ? uuid::read_flash_string(F_(unset)).c_str() : settings.hostname().c_str());
|
||||
flash_string_vector{F_(show), F_(mqtt)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { Mqtt::show_mqtt(shell); });
|
||||
|
||||
if (shell.has_flags(CommandFlags::ADMIN)) {
|
||||
shell.printfln("Wifi:");
|
||||
shell.print(" ");
|
||||
shell.printfln(F_(wifi_ssid_fmt),
|
||||
settings.wifi_ssid().empty() ? uuid::read_flash_string(F_(unset)).c_str()
|
||||
: settings.wifi_ssid().c_str());
|
||||
shell.print(" ");
|
||||
shell.printfln(F_(wifi_password_fmt), settings.wifi_password().empty() ? F_(unset) : F_(asterisks));
|
||||
shell.printfln(F("Syslog:"));
|
||||
auto host = settings.syslog_host();
|
||||
shell.print(" ");
|
||||
shell.printfln(F_(host_fmt), !host.empty() ? host.c_str() : uuid::read_flash_string(F_(unset)).c_str());
|
||||
shell.print(" ");
|
||||
shell.printfln(F_(log_level_fmt), uuid::log::format_level_uppercase(settings.syslog_level()));
|
||||
shell.print(" ");
|
||||
shell.printfln(F_(mark_interval_fmt), settings.syslog_mark_interval());
|
||||
}
|
||||
});
|
||||
|
||||
// enter the context
|
||||
Console::enter_custom_context(shell, context);
|
||||
|
||||
59
src/system.h
59
src/system.h
@@ -23,15 +23,10 @@
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "settings.h"
|
||||
#include "console.h"
|
||||
#include "mqtt.h"
|
||||
#include "telegram.h"
|
||||
|
||||
#if defined(ESP8266)
|
||||
#include <RTCVars.h>
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#include <uuid/syslog.h>
|
||||
#endif
|
||||
@@ -44,23 +39,15 @@ namespace emsesp {
|
||||
|
||||
class System {
|
||||
public:
|
||||
static uint8_t free_mem();
|
||||
|
||||
void start();
|
||||
void loop();
|
||||
|
||||
static bool safe_mode();
|
||||
static void save_safe_mode(const bool safe_mode);
|
||||
static void safe_mode(const bool safe_mode);
|
||||
|
||||
static void restart(bool safe_mode);
|
||||
static void restart() {
|
||||
restart(false); // default, don't boot into safe mode
|
||||
}
|
||||
|
||||
static void show_mem(const char * text);
|
||||
|
||||
static void console_commands(Shell & shell, unsigned int context);
|
||||
static void restart();
|
||||
static void format(uuid::console::Shell & shell);
|
||||
static uint8_t free_mem();
|
||||
void syslog_init();
|
||||
void set_heartbeat(bool system_heartbeat);
|
||||
static void console_commands(Shell & shell, unsigned int context);
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
@@ -69,11 +56,13 @@ class System {
|
||||
static uuid::syslog::SyslogService syslog_;
|
||||
#endif
|
||||
|
||||
static constexpr uint32_t SYSTEM_CHECK_FREQUENCY = 10000; // check every 10 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 or safe-mode
|
||||
static constexpr uint32_t SYSTEM_CHECK_FREQUENCY = 10000; // check every 10 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)
|
||||
|
||||
// internal LED
|
||||
#ifndef EMSESP_NO_LED
|
||||
#if defined(ESP8266)
|
||||
static constexpr uint8_t LED_GPIO = 2;
|
||||
static constexpr uint8_t LED_ON = LOW;
|
||||
@@ -85,29 +74,33 @@ class System {
|
||||
static constexpr uint8_t LED_GPIO = 5;
|
||||
static constexpr uint8_t LED_ON = LOW;
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
static constexpr uint8_t LED_GPIO = 0;
|
||||
static constexpr uint8_t LED_GPIO = 0; // no LED
|
||||
static constexpr uint8_t LED_ON = 0;
|
||||
#endif
|
||||
|
||||
void led_monitor();
|
||||
void set_led_speed(uint32_t speed);
|
||||
void mqtt_commands(const char * message);
|
||||
void config_syslog();
|
||||
void send_heartbeat();
|
||||
void system_check();
|
||||
|
||||
static void show_system(uuid::console::Shell & shell);
|
||||
static void show_system(uuid::console::Shell & shell);
|
||||
static int8_t wifi_quality();
|
||||
|
||||
void system_check();
|
||||
bool system_healthy_ = false;
|
||||
uint32_t led_flash_speed_ = LED_WARNING_BLINK_FAST; // default boot flashes quickly
|
||||
|
||||
static bool safe_mode_;
|
||||
bool system_healthy_ = false;
|
||||
uint32_t led_flash_speed_ = LED_WARNING_BLINK_FAST; // default boot flashes quickly
|
||||
static uint32_t heap_start_;
|
||||
static int reset_counter_;
|
||||
uint32_t last_heartbeat_ = 0;
|
||||
|
||||
#if defined(ESP8266)
|
||||
static RTCVars state_;
|
||||
#endif
|
||||
// settings
|
||||
uint8_t tx_mode_;
|
||||
bool system_heartbeat_;
|
||||
uint8_t syslog_level_;
|
||||
uint32_t syslog_mark_interval_;
|
||||
String syslog_host_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -41,7 +41,7 @@ const uint8_t ems_crc_table[] = {0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E,
|
||||
uint32_t EMSbus::last_bus_activity_ = 0; // timestamp of last time a valid Rx came in
|
||||
bool EMSbus::bus_connected_ = false; // start assuming the bus hasn't been connected
|
||||
uint8_t EMSbus::ems_mask_ = EMS_MASK_UNSET; // unset so its triggered when booting, the its 0x00=buderus, 0x80=junker/ht3
|
||||
uint8_t EMSbus::ems_bus_id_ = EMSESP_DEFAULT_BUS_ID;
|
||||
uint8_t EMSbus::ems_bus_id_ = EMSESP_DEFAULT_EMS_BUS_ID;
|
||||
uint8_t EMSbus::tx_waiting_ = Telegram::Operation::NONE;
|
||||
bool EMSbus::tx_active_ = false;
|
||||
|
||||
@@ -87,7 +87,7 @@ std::string Telegram::to_string() const {
|
||||
}
|
||||
uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
|
||||
uint8_t length = 0;
|
||||
data[0] = this->src ^ RxService::ems_mask();
|
||||
data[0] = this->src ^ RxService::ems_mask();
|
||||
if (this->operation == Telegram::Operation::TX_READ) {
|
||||
data[1] = this->dest | 0x80;
|
||||
data[4] = this->message_data[0];
|
||||
@@ -98,7 +98,7 @@ std::string Telegram::to_string() const {
|
||||
length = 7;
|
||||
} else {
|
||||
data[2] = this->type_id;
|
||||
length = 5;
|
||||
length = 5;
|
||||
}
|
||||
}
|
||||
if (this->operation == Telegram::Operation::TX_WRITE) {
|
||||
@@ -205,11 +205,6 @@ void RxService::add(uint8_t * data, uint8_t length) {
|
||||
message_length = length - 6 - shift;
|
||||
}
|
||||
|
||||
// if we don't have a type_id or empty data block, exit
|
||||
if ((type_id == 0) || (message_length == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if we're watching and "raw" print out actual telegram as bytes to the console
|
||||
if (EMSESP::watch() == EMSESP::Watch::WATCH_RAW) {
|
||||
uint16_t trace_watch_id = EMSESP::watch_id();
|
||||
@@ -222,6 +217,11 @@ void RxService::add(uint8_t * data, uint8_t length) {
|
||||
LOG_DEBUG(F("[DEBUG] New Rx [#%d] telegram, message length %d"), rx_telegram_id_, message_length);
|
||||
#endif
|
||||
|
||||
// if we don't have a type_id or empty data block, exit
|
||||
if ((type_id == 0) || (message_length == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create the telegram
|
||||
auto telegram = std::make_shared<Telegram>(Telegram::Operation::RX, src, dest, type_id, offset, message_data, message_length);
|
||||
|
||||
@@ -233,7 +233,6 @@ void RxService::add(uint8_t * data, uint8_t length) {
|
||||
rx_telegrams_.emplace_back(rx_telegram_id_++, std::move(telegram)); // add to queue
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Tx CODE starts here...
|
||||
//
|
||||
@@ -253,8 +252,7 @@ void TxService::flush_tx_queue() {
|
||||
// start and initialize Tx
|
||||
void TxService::start() {
|
||||
// grab the bus ID
|
||||
Settings settings;
|
||||
ems_bus_id(settings.ems_bus_id());
|
||||
EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) { ems_bus_id(settings.ems_bus_id); });
|
||||
|
||||
// send first Tx request to bus master (boiler) for its registered devices
|
||||
// this will be added to the queue and sent during the first tx loop()
|
||||
@@ -285,7 +283,7 @@ void TxService::send_poll() {
|
||||
void TxService::send() {
|
||||
// don't process if we don't have a connection to the EMS bus
|
||||
// or we're in read-only mode
|
||||
if (!bus_connected() || EMSESP::ems_read_only()) {
|
||||
if (!bus_connected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -421,7 +419,13 @@ void TxService::send_telegram(const uint8_t * data, const uint8_t length) {
|
||||
|
||||
// builds a Tx telegram and adds to queue
|
||||
// given some details like the destination, type, offset and message block
|
||||
void TxService::add(const uint8_t operation, const uint8_t dest, const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const bool front) {
|
||||
void TxService::add(const uint8_t operation,
|
||||
const uint8_t dest,
|
||||
const uint16_t type_id,
|
||||
const uint8_t offset,
|
||||
uint8_t * message_data,
|
||||
const uint8_t message_length,
|
||||
const bool front) {
|
||||
auto telegram = std::make_shared<Telegram>(operation, ems_bus_id(), dest, type_id, offset, message_data, message_length);
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG(F("[DEBUG] New Tx [#%d] telegram, length %d"), tx_telegram_id_, message_length);
|
||||
|
||||
@@ -129,6 +129,8 @@ class EMSbus {
|
||||
static constexpr uint8_t EMS_MASK_HT3 = 0x80; // EMS bus type Junkers/HT3
|
||||
static constexpr uint8_t EMS_MASK_BUDERUS = 0xFF; // EMS bus type Buderus
|
||||
|
||||
static constexpr uint8_t EMS_TX_ERROR_LIMIT = 10; // % limit of failed Tx read/write attempts before showing a warning
|
||||
|
||||
static bool bus_connected() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if ((uuid::get_uptime() - last_bus_activity_) > EMS_BUS_TIMEOUT) {
|
||||
@@ -270,7 +272,13 @@ class TxService : public EMSbus {
|
||||
void loop();
|
||||
void send();
|
||||
|
||||
void add(const uint8_t operation, const uint8_t dest, const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const bool front = false);
|
||||
void add(const uint8_t operation,
|
||||
const uint8_t dest,
|
||||
const uint16_t type_id,
|
||||
const uint8_t offset,
|
||||
uint8_t * message_data,
|
||||
const uint8_t message_length,
|
||||
const bool front = false);
|
||||
void add(const uint8_t operation, const uint8_t * data, const uint8_t length, const bool front = false);
|
||||
|
||||
void read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0);
|
||||
|
||||
@@ -152,7 +152,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
rx_telegram({0x09, 0x0B, 0x02, 0x00, 0x59, 0x09, 0x0a});
|
||||
|
||||
shell.loop_all();
|
||||
EMSESP::show_values(shell);
|
||||
EMSESP::show_device_values(shell);
|
||||
}
|
||||
|
||||
if (command == "unknown2") {
|
||||
@@ -206,14 +206,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
// B0 0B FF 00 02 62 00 44 02 7A 80 00 80 00 80 00 80 00 80 00 80 00 00 7C 80 00 80 00 80 00 80
|
||||
rx_telegram({0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00,
|
||||
0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80});
|
||||
EMSESP::show_values(shell);
|
||||
EMSESP::show_device_values(shell);
|
||||
|
||||
rx_telegram({0xB0, 0x0B, 0xFF, 0x00, 0x02, 0x62, 0x01, 0x44, 0x03, 0x30, 0x80, 00, 0x80, 00, 0x80, 00,
|
||||
0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x33});
|
||||
EMSESP::show_values(shell);
|
||||
EMSESP::show_device_values(shell);
|
||||
|
||||
rx_telegram({0xB0, 00, 0xFF, 0x18, 02, 0x62, 0x80, 00, 0xB8});
|
||||
EMSESP::show_values(shell);
|
||||
EMSESP::show_device_values(shell);
|
||||
|
||||
EMSESP::send_raw_telegram("B0 00 FF 18 02 62 80 00 B8");
|
||||
|
||||
@@ -221,12 +221,12 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
uart_telegram("30 00 FF 00 02 64 00 00 00 04 00 00 FF 00 00 1E 0B 09 64 00 00 00 00"); // SM100 modulation
|
||||
|
||||
EMSESP::rxservice_.loop();
|
||||
EMSESP::show_values(shell);
|
||||
EMSESP::show_device_values(shell);
|
||||
|
||||
uart_telegram("30 00 FF 0A 02 6A 03"); // SM100 pump off 0
|
||||
|
||||
EMSESP::rxservice_.loop();
|
||||
EMSESP::show_values(shell);
|
||||
EMSESP::show_device_values(shell);
|
||||
}
|
||||
|
||||
if (command == "km") {
|
||||
@@ -293,9 +293,9 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
uart_telegram_withCRC("90 0B FF 00 01 A5 80 00 01 26 15 00 26 2A 05 A0 03 03 03 05 A0 05 A0 00 00 11 01 03 FF FF 00 FE");
|
||||
uart_telegram_withCRC("90 00 FF 19 01 A5 01 04 00 00 00 00 FF 64 2A 00 3C 01 FF 92");
|
||||
|
||||
EMSESP::show_emsbus(shell);
|
||||
EMSESP::show_ems(shell);
|
||||
EMSESP::rxservice_.loop();
|
||||
EMSESP::show_values(shell);
|
||||
EMSESP::show_device_values(shell);
|
||||
}
|
||||
|
||||
if (command == "cr100") {
|
||||
@@ -321,7 +321,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
EMSESP::rxservice_.loop();
|
||||
EMSESP::txservice_.flush_tx_queue();
|
||||
shell.loop_all();
|
||||
EMSESP::show_values(shell);
|
||||
EMSESP::show_device_values(shell);
|
||||
|
||||
shell.invoke_command("thermostat");
|
||||
shell.loop_all();
|
||||
@@ -330,7 +330,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
shell.invoke_command("set mode auto");
|
||||
|
||||
shell.loop_all();
|
||||
EMSESP::show_emsbus(shell);
|
||||
EMSESP::show_ems(shell);
|
||||
shell.loop_all();
|
||||
|
||||
EMSESP::txservice_.send(); // send it to UART
|
||||
@@ -395,7 +395,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
|
||||
if (command == "send") {
|
||||
shell.printfln(F("Sending to Tx..."));
|
||||
EMSESP::show_emsbus(shell);
|
||||
EMSESP::show_ems(shell);
|
||||
EMSESP::txservice_.send(); // send it to UART
|
||||
}
|
||||
|
||||
@@ -430,7 +430,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
// EMS+ Junkers read request
|
||||
EMSESP::send_read_request(0x16F, 0x10);
|
||||
|
||||
EMSESP::show_emsbus(shell);
|
||||
EMSESP::show_ems(shell);
|
||||
|
||||
// process whole Tx queue
|
||||
for (uint8_t i = 0; i < 10; i++) {
|
||||
@@ -464,10 +464,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
// Simulate adding a Poll - should send retry
|
||||
EMSESP::incoming_telegram(poll, 1);
|
||||
|
||||
EMSESP::show_emsbus(shell);
|
||||
EMSESP::show_ems(shell);
|
||||
uint8_t t2[] = {0x21, 0x22};
|
||||
EMSESP::send_write_request(0x91, 0x17, 0x00, t2, sizeof(t2), 0);
|
||||
EMSESP::show_emsbus(shell);
|
||||
EMSESP::show_ems(shell);
|
||||
|
||||
EMSESP::txservice_.flush_tx_queue();
|
||||
}
|
||||
@@ -520,14 +520,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
if (command == "poll2") {
|
||||
shell.printfln(F("Testing Tx Sending last message on queue..."));
|
||||
|
||||
EMSESP::show_emsbus(shell);
|
||||
EMSESP::show_ems(shell);
|
||||
|
||||
uint8_t poll[1] = {0x8B};
|
||||
EMSESP::incoming_telegram(poll, 1);
|
||||
|
||||
EMSESP::rxservice_.loop();
|
||||
|
||||
EMSESP::show_emsbus(shell);
|
||||
EMSESP::show_ems(shell);
|
||||
EMSESP::txservice_.flush_tx_queue();
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ void IRAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) {
|
||||
|
||||
if (EMS_UART.int_st.brk_det) {
|
||||
EMS_UART.int_clr.brk_det = 1; // clear flag
|
||||
EMS_UART.conf0.txd_brk = 0; // disable <brk>
|
||||
EMS_UART.conf0.txd_brk = 0; // disable <brk>
|
||||
if (emsTxBufIdx < emsTxBufLen) { // timer tx_mode is interrupted by <brk>
|
||||
emsTxBufIdx = emsTxBufLen; // stop timer mode
|
||||
drop_next_rx = true; // we have trash in buffer
|
||||
@@ -87,10 +87,10 @@ void IRAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) {
|
||||
|
||||
|
||||
void IRAM_ATTR EMSuart::emsuart_tx_timer_intr_handler() {
|
||||
if(emsTxBufLen == 0) {
|
||||
if (emsTxBufLen == 0) {
|
||||
return;
|
||||
}
|
||||
if(tx_mode_ > 50) {
|
||||
if (tx_mode_ > 50) {
|
||||
for (uint8_t i = 0; i < emsTxBufLen; i++) {
|
||||
EMS_UART.fifo.rw_byte = emsTxBuf[i];
|
||||
}
|
||||
@@ -101,7 +101,7 @@ void IRAM_ATTR EMSuart::emsuart_tx_timer_intr_handler() {
|
||||
timerAlarmWrite(timer, emsTxWait, false);
|
||||
timerAlarmEnable(timer);
|
||||
} else if (emsTxBufIdx + 1 == emsTxBufLen) {
|
||||
EMS_UART.fifo.rw_byte = emsTxBuf[emsTxBufIdx];
|
||||
EMS_UART.fifo.rw_byte = emsTxBuf[emsTxBufIdx];
|
||||
EMS_UART.conf0.txd_brk = 1; // <brk> after send
|
||||
}
|
||||
emsTxBufIdx++;
|
||||
@@ -137,7 +137,7 @@ void EMSuart::start(const uint8_t tx_mode) {
|
||||
uart_isr_register(EMSUART_UART, emsuart_rx_intr_handler, NULL, ESP_INTR_FLAG_IRAM, &uart_handle);
|
||||
xTaskCreate(emsuart_recvTask, "emsuart_recvTask", 2048, NULL, configMAX_PRIORITIES - 1, NULL);
|
||||
|
||||
timer = timerBegin(1, 80, true); // timer prescale to 1 µs, countup
|
||||
timer = timerBegin(1, 80, true); // timer prescale to 1 µs, countup
|
||||
timerAttachInterrupt(timer, &emsuart_tx_timer_intr_handler, true); // Timer with edge interrupt
|
||||
restart();
|
||||
}
|
||||
@@ -173,10 +173,10 @@ void EMSuart::restart() {
|
||||
EMS_UART.idle_conf.tx_idle_num = 2;
|
||||
} else if (tx_mode_ <= 50) {
|
||||
EMS_UART.idle_conf.tx_idle_num = tx_mode_;
|
||||
emsTxWait = EMSUART_TX_BIT_TIME * (tx_mode_ + 10);
|
||||
emsTxWait = EMSUART_TX_BIT_TIME * (tx_mode_ + 10);
|
||||
} else {
|
||||
EMS_UART.idle_conf.tx_idle_num = 2;
|
||||
emsTxWait = EMSUART_TX_BIT_TIME * (tx_mode_ - 50);
|
||||
emsTxWait = EMSUART_TX_BIT_TIME * (tx_mode_ - 50);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,8 @@ uint16_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) {
|
||||
}
|
||||
|
||||
if (tx_mode_ == 5) { // wait before sending
|
||||
vTaskDelay(4 / portTICK_PERIOD_MS);
|
||||
//vTaskDelay(4 / portTICK_PERIOD_MS);
|
||||
delayMicroseconds(4000);
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
EMS_UART.fifo.rw_byte = buf[i];
|
||||
}
|
||||
@@ -233,7 +234,7 @@ uint16_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) {
|
||||
EMS_UART.fifo.rw_byte = buf[i];
|
||||
delayMicroseconds(EMSUART_TX_WAIT_PLUS);
|
||||
}
|
||||
EMS_UART.fifo.rw_byte = buf[len - 1];
|
||||
EMS_UART.fifo.rw_byte = buf[len - 1];
|
||||
EMS_UART.conf0.txd_brk = 1; // <brk> after send, cleard by hardware after send
|
||||
return EMS_TX_STATUS_OK;
|
||||
}
|
||||
@@ -243,7 +244,7 @@ uint16_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) {
|
||||
EMS_UART.fifo.rw_byte = buf[i];
|
||||
delayMicroseconds(EMSUART_TX_WAIT_HT3);
|
||||
}
|
||||
EMS_UART.fifo.rw_byte = buf[len - 1];
|
||||
EMS_UART.fifo.rw_byte = buf[len - 1];
|
||||
EMS_UART.conf0.txd_brk = 1; // <brk> after send, cleard by hardware after send
|
||||
return EMS_TX_STATUS_OK;
|
||||
}
|
||||
@@ -257,11 +258,11 @@ uint16_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) {
|
||||
EMS_UART.fifo.rw_byte = buf[i]; // send each Tx byte
|
||||
uint16_t timeoutcnt = EMSUART_TX_TIMEOUT;
|
||||
while ((EMS_UART.status.rxfifo_cnt == _usrxc) && (--timeoutcnt > 0)) {
|
||||
delayMicroseconds(EMSUART_TX_BUSY_WAIT); // burn CPU cycles...
|
||||
delayMicroseconds(EMSUART_TX_BUSY_WAIT); // burn CPU cycles...
|
||||
}
|
||||
}
|
||||
EMS_UART.fifo.rw_byte = buf[len - 1]; // send each Tx byte
|
||||
EMS_UART.conf0.txd_brk = 1; // <brk> after send, cleard by hardware after send
|
||||
EMS_UART.fifo.rw_byte = buf[len - 1]; // send each Tx byte
|
||||
EMS_UART.conf0.txd_brk = 1; // <brk> after send, cleard by hardware after send
|
||||
return EMS_TX_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,11 +46,11 @@
|
||||
#define EMS_TXMODE_NEW 4 // for michael's testing
|
||||
|
||||
// LEGACY
|
||||
#define EMSUART_TX_BIT_TIME 104 // bit time @9600 baud
|
||||
#define EMSUART_TX_BIT_TIME 104 // bit time @9600 baud
|
||||
#define EMSUART_TX_WAIT_BRK (EMSUART_TX_BIT_TIME * 11) // 1144
|
||||
|
||||
// EMS 1.0
|
||||
#define EMSUART_TX_BUSY_WAIT (EMSUART_TX_BIT_TIME / 8) // 13
|
||||
#define EMSUART_TX_BUSY_WAIT (EMSUART_TX_BIT_TIME / 8) // 13
|
||||
#define EMSUART_TX_TIMEOUT (32 * EMSUART_TX_BIT_TIME / EMSUART_TX_BUSY_WAIT) // 256
|
||||
|
||||
// HT3/Junkers - Time to send one Byte (8 Bits, 1 Start Bit, 1 Stop Bit) plus 7 bit delay. The -8 is for lag compensation.
|
||||
|
||||
@@ -34,7 +34,7 @@ uint8_t emsTxBuf[EMS_MAXBUFFERSIZE];
|
||||
uint8_t emsTxBufIdx;
|
||||
uint8_t emsTxBufLen;
|
||||
uint32_t emsTxWait;
|
||||
bool EMSuart::sending_ = false;
|
||||
bool EMSuart::sending_ = false;
|
||||
|
||||
//
|
||||
// Main interrupt handler
|
||||
@@ -52,7 +52,7 @@ void ICACHE_RAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) {
|
||||
}
|
||||
USIC(EMSUART_UART) = (1 << UIBD); // INT clear the BREAK detect interrupt
|
||||
USIE(EMSUART_UART) &= ~(1 << UIFF); // disable fifo-full irq
|
||||
length = 0;
|
||||
length = 0;
|
||||
while ((USS(EMSUART_UART) >> USRXC) & 0x0FF) { // read fifo into buffer
|
||||
uint8_t rx = USF(EMSUART_UART);
|
||||
if (length < EMS_MAXBUFFERSIZE) {
|
||||
@@ -64,7 +64,7 @@ void ICACHE_RAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) {
|
||||
if (!drop_next_rx) {
|
||||
pEMSRxBuf->length = length;
|
||||
os_memcpy((void *)pEMSRxBuf->buffer, (void *)&uart_buffer, pEMSRxBuf->length); // copy data into transfer buffer, including the BRK 0x00 at the end
|
||||
system_os_post(EMSUART_recvTaskPrio, 0, 0); // call emsuart_recvTask() at next opportunity
|
||||
system_os_post(EMSUART_recvTaskPrio, 0, 0); // call emsuart_recvTask() at next opportunity
|
||||
}
|
||||
drop_next_rx = false;
|
||||
sending_ = false;
|
||||
@@ -98,9 +98,9 @@ void ICACHE_FLASH_ATTR EMSuart::emsuart_flush_fifos() {
|
||||
|
||||
// ISR to Fire when Timer is triggered
|
||||
void ICACHE_RAM_ATTR EMSuart::emsuart_tx_timer_intr_handler() {
|
||||
if ( tx_mode_ > 50) {
|
||||
if (tx_mode_ > 50) {
|
||||
for (uint8_t i = 0; i < emsTxBufLen; i++) {
|
||||
USF(EMSUART_UART) = emsTxBuf[i];
|
||||
USF(EMSUART_UART) = emsTxBuf[i];
|
||||
}
|
||||
USC0(EMSUART_UART) |= (1 << UCBRK); // set <BRK>
|
||||
} else {
|
||||
@@ -150,7 +150,7 @@ void ICACHE_FLASH_ATTR EMSuart::start(uint8_t tx_mode) {
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD);
|
||||
|
||||
// set 9600, 8 bits, no parity check, 1 stop bit
|
||||
USD(EMSUART_UART) = (UART_CLK_FREQ / EMSUART_BAUD);
|
||||
USD(EMSUART_UART) = (UART_CLK_FREQ / EMSUART_BAUD);
|
||||
if (tx_mode_ == 5) {
|
||||
USC0(EMSUART_UART) = 0x2C; // 8N1.5
|
||||
} else {
|
||||
@@ -249,7 +249,7 @@ void ICACHE_FLASH_ATTR EMSuart::tx_brk() {
|
||||
void EMSuart::send_poll(uint8_t data) {
|
||||
// reset tx-brk, just in case it is accidentally set
|
||||
USC0(EMSUART_UART) &= ~(1 << UCBRK);
|
||||
sending_ = true;
|
||||
sending_ = true;
|
||||
|
||||
if (tx_mode_ > 50) { // timer controlled modes
|
||||
emsTxBuf[0] = data;
|
||||
@@ -260,7 +260,7 @@ void EMSuart::send_poll(uint8_t data) {
|
||||
emsTxBufIdx = 0;
|
||||
emsTxBufLen = 1;
|
||||
timer1_write(emsTxWait);
|
||||
}else if (tx_mode_ == 5) {
|
||||
} else if (tx_mode_ == 5) {
|
||||
delayMicroseconds(3000);
|
||||
USF(EMSUART_UART) = data;
|
||||
USC0(EMSUART_UART) |= (1 << UCBRK);
|
||||
@@ -275,7 +275,7 @@ void EMSuart::send_poll(uint8_t data) {
|
||||
USF(EMSUART_UART) = data;
|
||||
delayMicroseconds(EMSUART_TX_WAIT_PLUS);
|
||||
tx_brk(); // send <BRK>
|
||||
} else {
|
||||
} else {
|
||||
// tx_mode 1
|
||||
// EMS1.0, same logic as in transmit
|
||||
ETS_UART_INTR_DISABLE();
|
||||
@@ -306,9 +306,9 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
|
||||
}
|
||||
// reset tx-brk, just in case it is accidentally set
|
||||
USC0(EMSUART_UART) &= ~(1 << UCBRK);
|
||||
sending_ = true;
|
||||
sending_ = true;
|
||||
|
||||
// all at once after a inititial timer delay
|
||||
// all at once after a initial timer delay
|
||||
if (tx_mode_ > 50) {
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
emsTxBuf[i] = buf[i];
|
||||
@@ -327,7 +327,7 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
|
||||
timer1_write(emsTxWait);
|
||||
return EMS_TX_STATUS_OK;
|
||||
}
|
||||
// fixed dealy before sending
|
||||
// fixed delay before sending
|
||||
if (tx_mode_ == 5) {
|
||||
delayMicroseconds(3000);
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
@@ -428,7 +428,7 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
|
||||
USC0(EMSUART_UART) &= ~(1 << UCBRK); // clear <BRK>
|
||||
}
|
||||
|
||||
ETS_UART_INTR_ENABLE(); // open up the FIFO again to start receiving
|
||||
ETS_UART_INTR_ENABLE(); // open up the FIFO again to start receiving
|
||||
return EMS_TX_STATUS_OK; // send the Tx ok status back
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ class EMSuart {
|
||||
static void ICACHE_FLASH_ATTR send_poll(uint8_t data);
|
||||
static uint16_t ICACHE_FLASH_ATTR transmit(uint8_t * buf, uint8_t len);
|
||||
static bool sending() {
|
||||
return sending_;
|
||||
return sending_;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "2.0.0a32"
|
||||
#define EMSESP_APP_VERSION "2.0.0b5"
|
||||
|
||||
Reference in New Issue
Block a user