Rework settings (#2282)
* wip based on early draft. todo benchmarking
* fixup eraser, assume keys are unique
* fix cursor copy, test removal at random
* small benchmark via permutations. todo lambdas and novirtual
* fix empty condition / reset
* overwrite optimizations, fix move offsets overflows
* ...erase using 0xff instead of 0
* test the theory with code, different length kv were bugged
* try to check for out-of-bounds writes / reads
* style
* trying to fix mover again
* clarify length, defend against reading len on edge
* fix uncommited rewind change
* prove space assumptions
* more concise traces, fix move condition (agrh!!!)
* slightly more internal knowledge (estimates API?)
* make sure cursor is only valid within the range
* ensure 0 does not blow things
* go back up
* cursor comments
* comments
* rewrite writes through cursor
* in del too
* estimate kv storage requirements, return available size
* move raw erase / move into a method, allow ::set to avoid scanning storage twice
* refactor naming, use in code
* amend storage slicing test
* fix crash handler offsets, cleanup configuration
* start -> begin
* eeprom readiness
* dependencies
* unused
* SPI_FLASH constants for older Core
* vtables -> templates
* less include dependencies
* gcov help, move estimate outside of the class
* writer position can never match, use begin + offset
* tweak save_crash to trigger only once in a serious crash
* doh, header function should be inline
* foreach api, tweak structs for public api
* use test helper class
* when not using foreach, move cursor reset closer to the loop using read_kv
* coverage comments, fix typo in tests decltype
* ensure set() does not break with offset
* make codacy happy again 3 years ago sys: clean-up system-specific functions
- shrink utils source file, move heartbeat and boot management into system
- improvise with 'heartbeat' functionality. include scheduler implementation that will
manage the per-module heartbeat callbacks with individual 'mode' and
'interval' settings. current ones are mqtt (including relays, lights, thermostat), debug and
influxdb. preserve heartbeat NONE, ONCE and REPEAT, REPEAT_STATUS is effectively a hbReport & status bit.
- mqtt heartbeat is managed through mqttHeartbeat() callbacks
- tweak mqtt callbacks to use lists instead of the vector, slighly reducing the size of the .bin
- update WebUI, include report setting and update hbMode values
- make sure general.h settings include new heartbeat,
move constant definitions outside of the header
- correctly include dependencies through the .cpp, avoid leaking
internal details.
- as a side-effect, base headers are no longer included recursively
3 years ago sys: clean-up system-specific functions
- shrink utils source file, move heartbeat and boot management into system
- improvise with 'heartbeat' functionality. include scheduler implementation that will
manage the per-module heartbeat callbacks with individual 'mode' and
'interval' settings. current ones are mqtt (including relays, lights, thermostat), debug and
influxdb. preserve heartbeat NONE, ONCE and REPEAT, REPEAT_STATUS is effectively a hbReport & status bit.
- mqtt heartbeat is managed through mqttHeartbeat() callbacks
- tweak mqtt callbacks to use lists instead of the vector, slighly reducing the size of the .bin
- update WebUI, include report setting and update hbMode values
- make sure general.h settings include new heartbeat,
move constant definitions outside of the header
- correctly include dependencies through the .cpp, avoid leaking
internal details.
- as a side-effect, base headers are no longer included recursively
3 years ago Terminal: change command-line parser (#2247)
Change the underlying command line handling:
- switch to a custom parser, inspired by redis / sds
- update terminalRegisterCommand signature, pass only bare minimum
- clean-up `help` & `commands`. update settings `set`, `get` and `del`
- allow our custom test suite to run command-line tests
- clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`)
- send parsing errors to the debug log
As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT`
- MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API.
- Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS.
Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :)
Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case. 4 years ago sys: clean-up system-specific functions
- shrink utils source file, move heartbeat and boot management into system
- improvise with 'heartbeat' functionality. include scheduler implementation that will
manage the per-module heartbeat callbacks with individual 'mode' and
'interval' settings. current ones are mqtt (including relays, lights, thermostat), debug and
influxdb. preserve heartbeat NONE, ONCE and REPEAT, REPEAT_STATUS is effectively a hbReport & status bit.
- mqtt heartbeat is managed through mqttHeartbeat() callbacks
- tweak mqtt callbacks to use lists instead of the vector, slighly reducing the size of the .bin
- update WebUI, include report setting and update hbMode values
- make sure general.h settings include new heartbeat,
move constant definitions outside of the header
- correctly include dependencies through the .cpp, avoid leaking
internal details.
- as a side-effect, base headers are no longer included recursively
3 years ago sys: clean-up system-specific functions
- shrink utils source file, move heartbeat and boot management into system
- improvise with 'heartbeat' functionality. include scheduler implementation that will
manage the per-module heartbeat callbacks with individual 'mode' and
'interval' settings. current ones are mqtt (including relays, lights, thermostat), debug and
influxdb. preserve heartbeat NONE, ONCE and REPEAT, REPEAT_STATUS is effectively a hbReport & status bit.
- mqtt heartbeat is managed through mqttHeartbeat() callbacks
- tweak mqtt callbacks to use lists instead of the vector, slighly reducing the size of the .bin
- update WebUI, include report setting and update hbMode values
- make sure general.h settings include new heartbeat,
move constant definitions outside of the header
- correctly include dependencies through the .cpp, avoid leaking
internal details.
- as a side-effect, base headers are no longer included recursively
3 years ago sys: clean-up system-specific functions
- shrink utils source file, move heartbeat and boot management into system
- improvise with 'heartbeat' functionality. include scheduler implementation that will
manage the per-module heartbeat callbacks with individual 'mode' and
'interval' settings. current ones are mqtt (including relays, lights, thermostat), debug and
influxdb. preserve heartbeat NONE, ONCE and REPEAT, REPEAT_STATUS is effectively a hbReport & status bit.
- mqtt heartbeat is managed through mqttHeartbeat() callbacks
- tweak mqtt callbacks to use lists instead of the vector, slighly reducing the size of the .bin
- update WebUI, include report setting and update hbMode values
- make sure general.h settings include new heartbeat,
move constant definitions outside of the header
- correctly include dependencies through the .cpp, avoid leaking
internal details.
- as a side-effect, base headers are no longer included recursively
3 years ago sys: clean-up system-specific functions
- shrink utils source file, move heartbeat and boot management into system
- improvise with 'heartbeat' functionality. include scheduler implementation that will
manage the per-module heartbeat callbacks with individual 'mode' and
'interval' settings. current ones are mqtt (including relays, lights, thermostat), debug and
influxdb. preserve heartbeat NONE, ONCE and REPEAT, REPEAT_STATUS is effectively a hbReport & status bit.
- mqtt heartbeat is managed through mqttHeartbeat() callbacks
- tweak mqtt callbacks to use lists instead of the vector, slighly reducing the size of the .bin
- update WebUI, include report setting and update hbMode values
- make sure general.h settings include new heartbeat,
move constant definitions outside of the header
- correctly include dependencies through the .cpp, avoid leaking
internal details.
- as a side-effect, base headers are no longer included recursively
3 years ago sys: clean-up system-specific functions
- shrink utils source file, move heartbeat and boot management into system
- improvise with 'heartbeat' functionality. include scheduler implementation that will
manage the per-module heartbeat callbacks with individual 'mode' and
'interval' settings. current ones are mqtt (including relays, lights, thermostat), debug and
influxdb. preserve heartbeat NONE, ONCE and REPEAT, REPEAT_STATUS is effectively a hbReport & status bit.
- mqtt heartbeat is managed through mqttHeartbeat() callbacks
- tweak mqtt callbacks to use lists instead of the vector, slighly reducing the size of the .bin
- update WebUI, include report setting and update hbMode values
- make sure general.h settings include new heartbeat,
move constant definitions outside of the header
- correctly include dependencies through the .cpp, avoid leaking
internal details.
- as a side-effect, base headers are no longer included recursively
3 years ago |
|
- /*
-
- DEBUG MODULE
-
- Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
-
- */
-
- #include "espurna.h"
-
- #if DEBUG_SUPPORT
-
- #include "settings.h"
- #include "telnet.h"
- #include "ntp.h"
-
- #include <limits>
- #include <type_traits>
- #include <vector>
-
- #if WEB_SUPPORT
- #include "web.h"
- #include "ws.h"
- #endif
-
- #if DEBUG_WEB_SUPPORT
- #include <ArduinoJson.h>
- #endif
-
- #if DEBUG_UDP_SUPPORT
- #include <WiFiUdp.h>
- #endif
-
- namespace espurna {
- namespace debug {
- namespace settings {
- namespace options {
- namespace {
-
- using espurna::settings::options::Enumeration;
-
- PROGMEM_STRING(Disabled, "off");
- PROGMEM_STRING(Enabled, "on");
- PROGMEM_STRING(SkipBoot, "skip-boot");
-
- static constexpr Enumeration<DebugLogMode> DebugLogModeOptions[] PROGMEM {
- {DebugLogMode::Disabled, Disabled},
- {DebugLogMode::Enabled, Enabled},
- {DebugLogMode::SkipBoot, SkipBoot},
- };
-
- } // namespace
- } // namespace options
-
- namespace keys {
- namespace {
-
- PROGMEM_STRING(SdkDebug, "dbgSDK");
- PROGMEM_STRING(Mode, "dbgLogMode");
- PROGMEM_STRING(Buffer, "dbgLogBuf");
- PROGMEM_STRING(BufferSize, "dbgLogBufSize");
-
- PROGMEM_STRING(HeartbeatMode, "dbgHbMode");
- PROGMEM_STRING(HeartbeatInterval, "dbgHbIntvl");
-
- } // namespace
- } // namespace keys
- } // namespace settings
- } // namespace debug
-
- namespace settings {
- namespace internal {
- namespace {
-
- using espurna::debug::settings::options::DebugLogModeOptions;
-
- } // namespace
-
- String serialize(::DebugLogMode value) {
- return serialize(DebugLogModeOptions, value);
- }
-
- template<>
- DebugLogMode convert(const String& value) {
- return convert(DebugLogModeOptions, value, DebugLogMode::Enabled);
- }
-
- } // namespace internal
- } // namespace settings
-
- namespace debug {
- namespace {
-
- struct Timestamp {
- Timestamp() = default;
-
- constexpr Timestamp(bool value) :
- _value(value)
- {}
-
- constexpr explicit operator bool() const {
- return _value;
- }
-
- private:
- bool _value { false };
- };
-
- } // namespace
-
- namespace build {
- namespace {
-
- constexpr Timestamp AddTimestamp { 1 == DEBUG_ADD_TIMESTAMP };
-
- constexpr bool coreDebug() {
- #if defined(DEBUG_ESP_PORT) && !defined(NDEBUG)
- return true;
- #else
- return false;
- #endif
- }
-
- constexpr bool sdkDebug() {
- return false;
- }
-
- constexpr DebugLogMode mode() {
- return DEBUG_LOG_MODE;
- }
-
- constexpr bool buffer() {
- return 1 == DEBUG_LOG_BUFFER_ENABLED;
- }
-
- constexpr size_t bufferSize() {
- return DEBUG_LOG_BUFFER_SIZE;
- }
-
- } // namespace
- } // namespace build
-
- namespace settings {
- namespace {
-
- [[gnu::unused]]
- bool sdkDebug() {
- return getSetting(keys::SdkDebug, build::sdkDebug());
- }
-
- DebugLogMode mode() {
- return getSetting(keys::Mode, build::mode());
- }
-
- bool buffer() {
- return getSetting(keys::Buffer, build::buffer());
- }
-
- size_t bufferSize() {
- return getSetting(keys::BufferSize, build::bufferSize());
- }
-
- espurna::heartbeat::Mode heartbeatMode() {
- return getSetting(keys::HeartbeatMode, espurna::heartbeat::currentMode());
- }
-
- espurna::duration::Seconds heartbeatInterval() {
- return getSetting(keys::HeartbeatInterval, espurna::heartbeat::currentInterval());
- }
-
- } // namespace
- } // namespace settings
-
- namespace internal {
- namespace {
-
- bool enabled { false };
-
- } // namespace
- } // namespace internal
-
- namespace {
-
- bool enabled() {
- return internal::enabled;
- }
-
- void disable() {
- internal::enabled = false;
- }
-
- void enable() {
- internal::enabled = true;
- }
-
- void delayedEnable() {
- disable();
- ::espurnaRegisterOnce(enable);
- }
-
- void send(const char* message, size_t len, Timestamp);
- void send(const char* message, size_t len) {
- send(message, len, build::AddTimestamp);
- }
-
- void formatAndSend(const char* format, va_list args) {
- constexpr size_t SmallStringBufferSize { 128 };
- char temp[SmallStringBufferSize];
-
- int len = vsnprintf_P(temp, sizeof(temp), format, args);
- if (len <= 0) {
- return;
- }
-
- // strlen(...) + '\0' already in temp buffer, avoid (explicit) dynamic memory when possible
- // (TODO: printf might still do it anyway internally?)
- if (static_cast<size_t>(len) < sizeof(temp)) {
- send(temp, len);
- return;
- }
-
- const size_t BufferSize { static_cast<size_t>(len) + 1 };
- auto* buffer = new (std::nothrow) char[BufferSize];
- if (!buffer) {
- return;
- }
-
- vsnprintf_P(buffer, BufferSize, format, args);
- send(buffer, len);
- delete[] buffer;
- }
-
- namespace buffer {
- namespace internal {
-
- bool enabled { false };
- std::vector<char> storage;
-
- } // namespace internal
-
- size_t size() {
- return internal::storage.size();
- }
-
- size_t capacity() {
- return internal::storage.capacity();
- }
-
- void reserve(size_t size) {
- internal::storage.reserve(size);
- }
-
- bool enabled() {
- return internal::enabled;
- }
-
- void disable() {
- internal::enabled = false;
- }
-
- void enable() {
- internal::enabled = true;
- }
-
- void enable(size_t reserved) {
- enable();
- reserve(reserved);
- }
-
- template <typename T>
- struct Handle {
- Handle() = delete;
- explicit Handle(T& lock) :
- _lock(lock)
- {
- _lock.lock();
- }
-
- ~Handle() {
- _lock.unlock();
- }
-
- explicit operator bool() const {
- return _lock;
- }
-
- private:
- T& _lock;
- };
-
- struct Lock {
- Lock() = default;
-
- explicit operator bool() const {
- return _value;
- }
-
- Handle<Lock> handle() {
- return Handle<Lock>(*this);
- }
-
- bool lock() {
- if (!_value) {
- _value = true;
- return true;
- }
-
- return false;
- }
-
- bool unlock() {
- if (_value) {
- _value = false;
- return true;
- }
-
- return false;
- }
-
- private:
- bool _value { false };
- };
-
- struct DebugLock {
- DebugLock() {
- if (debug::enabled()) {
- _changed = true;
- debug::disable();
- }
- }
-
- ~DebugLock() {
- if (_changed) {
- debug::enable();
- }
- }
-
- private:
- bool _changed { false };
- };
-
- // Buffer data until we encounter line break, then flush via debug method
- // (which is supposed to 1-to-1 copy the data, without adding the timestamp)
- // TODO: abstract as `PrintLine`, so this becomes generic line buffering output for terminal as well?
-
- namespace internal {
-
- std::vector<char> line;
-
- } // namespace internal
-
- void sendBytes(const uint8_t* bytes, size_t size) {
- static Lock lock;
- if (lock) {
- return;
- }
-
- if (!size || ((size > 0) && bytes[size - 1] == '\0')) {
- return;
- }
-
- auto handle = lock.handle();
- if (internal::line.capacity() < (size + 2)) {
- internal::line.reserve(internal::line.size() + size + 2);
- }
-
- internal::line.insert(internal::line.end(),
- reinterpret_cast<const char*>(bytes),
- reinterpret_cast<const char*>(bytes) + size);
-
- if (internal::line.end() != std::find(internal::line.begin(), internal::line.end(), '\n')) {
- // TODO: ws and telnet still assume this is a c-string and will try to strlen this pointer
- auto len = internal::line.size();
- internal::line.push_back('\0');
-
- DebugLock debugLock;
- debug::send(internal::line.data(), len, Timestamp(false));
-
- internal::line.clear();
- }
- }
-
- // Longer recording of all log data. Stops when storage is filled, requires manual flushing.
-
- void add(const char (&prefix)[10], const char* data, size_t len) {
- if (len > std::numeric_limits<uint16_t>::max()) {
- return;
- }
-
- size_t total { len };
- bool withPrefix { prefix[0] != '\0' };
- if (withPrefix) {
- total += sizeof(prefix) - 1;
- }
-
- if ((internal::storage.capacity() - internal::storage.size()) <= (total + 3)) {
- internal::enabled = false;
- return;
- }
-
- internal::storage.push_back(total >> 8);
- internal::storage.push_back(total & 0xff);
-
- if (withPrefix) {
- internal::storage.insert(internal::storage.end(), prefix, prefix + sizeof(prefix));
- }
- internal::storage.insert(internal::storage.end(), data, data + len);
- }
-
- void dump(Print& out) {
- size_t index = 0;
- do {
- if (index >= internal::storage.size()) {
- break;
- }
-
- size_t len = internal::storage[index] << 8;
- len = len | internal::storage[index + 1];
- index += 2;
-
- auto value = internal::storage[index + len];
- internal::storage[index + len] = '\0';
- out.print(internal::storage.data() + index);
- internal::storage[index + len] = value;
-
- index += len;
- } while (true);
-
- internal::storage.clear();
- internal::storage.shrink_to_fit();
- }
-
- } // namespace buffer
-
- #if DEBUG_SERIAL_SUPPORT
- namespace serial {
-
- using Output = void(*)(const char (&)[10], const char*, size_t);
-
- void null_output(const char (&)[10], const char*, size_t) {
- }
-
- namespace internal {
-
- Print* port { nullptr };
- Output output { null_output };
-
- } // namespace
-
- void output(const char (&prefix)[10], const char* message, size_t len) {
- internal::output(prefix, message, len);
- }
-
- void port_output(const char (&prefix)[10], const char* message, size_t len) {
- if (prefix[0] != '\0') {
- internal::port->write(&prefix[0], sizeof(prefix) - 1);
- }
- internal::port->write(message, len);
- }
-
- void setup() {
- // HardwareSerial::begin() will automatically enable this when
- // `#if defined(DEBUG_ESP_PORT) && !defined(NDEBUG)`
- // Do not interfere when that is the case
- const auto port = uartPort(DEBUG_SERIAL_PORT - 1);
- if (!port || !port->tx) {
- return;
- }
-
- // TODO: notice that SDK accepts anything as putc / printf,
- // but we don't really have a good reason to wrire both
- // this debug output and the one from SDK
- // (and most of the time this is need to grab boot info from a
- // physically connected device)
- if (!build::coreDebug() && settings::sdkDebug()) {
- switch (port->type) {
- case driver::uart::Type::Uart0:
- uart_set_debug(0);
- break;
- case driver::uart::Type::Uart1:
- uart_set_debug(1);
- break;
- default:
- break;
- }
- }
-
- internal::port = port->stream;
- internal::output = port_output;
- }
-
- } // namespace serial
- #endif
-
- #if DEBUG_UDP_SUPPORT
- namespace syslog {
- namespace build {
-
- IPAddress ip() {
- return DEBUG_UDP_IP;
- }
-
- constexpr uint16_t port() {
- return DEBUG_UDP_PORT;
- };
-
- constexpr bool enabled() {
- return port() == 514;
- }
-
- } // namespace build
-
- namespace internal {
-
- size_t len { 0 };
- char header[128] = {0};
- WiFiUDP udp;
-
- } // namespace
-
- // We use the syslog header as defined in RFC5424 (The Syslog Protocol), ref:
- // - https://tools.ietf.org/html/rfc5424
- // - https://github.com/xoseperez/espurna/issues/2312/
-
- void configure() {
- snprintf_P(
- internal::header, sizeof(internal::header),
- PSTR("<%u>1 - %.31s ESPurna - - - "), DEBUG_UDP_FAC_PRI,
- systemHostname().c_str());
- }
-
- bool output(const char* message, size_t len) {
- if (build::enabled() && wifiConnected()) {
- internal::udp.beginPacket(build::ip(), build::port());
- internal::udp.write(internal::header, internal::len);
- internal::udp.write(message, len);
-
- return internal::udp.endPacket() > 0;
- }
-
- return false;
- }
-
- } // namespace syslog
- #endif
-
- void send(const char* message, size_t len, Timestamp timestamp) {
- if (!message || !len) {
- return;
- }
-
- char prefix[10] = {0};
- static bool continue_timestamp = true;
- if (timestamp && continue_timestamp) {
- snprintf(prefix, sizeof(prefix), "[%06lu] ", millis() % 1000000);
- }
-
- continue_timestamp = static_cast<bool>(timestamp)
- || (message[len - 1] == '\r')
- || (message[len - 1] == '\n');
-
- bool pause { false };
-
- #if DEBUG_SERIAL_SUPPORT
- serial::output(prefix, message, len);
- #endif
-
- #if DEBUG_UDP_SUPPORT
- pause = syslog::output(message, len);
- #endif
-
- #if DEBUG_TELNET_SUPPORT
- pause = telnetDebugSend(prefix, message) || pause;
- #endif
-
- #if DEBUG_WEB_SUPPORT
- pause = wsDebugSend(prefix, message) || pause;
- #endif
-
- #if DEBUG_LOG_BUFFER_SUPPORT
- buffer::add(prefix, message, len);
- #endif
-
- if (pause) {
- optimistic_yield(1000);
- }
- }
-
- // -----------------------------------------------------------------------------
-
- #if DEBUG_WEB_SUPPORT
- namespace web {
-
- void onVisible(JsonObject& root) {
- wsPayloadModule(root, PSTR("dbg"));
- }
-
- } // namespace web
- #endif
-
- // -----------------------------------------------------------------------------
-
- bool status(espurna::heartbeat::Mask mask) {
- if (mask & espurna::heartbeat::Report::Uptime) {
- debugSend(PSTR("[MAIN] Uptime: %s\n"), prettyDuration(systemUptime()).c_str());
- }
-
- if (mask & espurna::heartbeat::Report::Freeheap) {
- const auto stats = systemHeapStats();
- debugSend(PSTR("[MAIN] Heap: initial %5lu available %5lu contiguous %5lu\n"),
- systemInitialFreeHeap(), stats.available, stats.usable);
- }
-
- if ((mask & espurna::heartbeat::Report::Vcc) && (ADC_MODE_VALUE == ADC_VCC)) {
- debugSend(PSTR("[MAIN] VCC: %lu mV\n"), ESP.getVcc());
- }
-
- #if NTP_SUPPORT
- if ((mask & espurna::heartbeat::Report::Datetime) && ntpSynced()) {
- debugSend(PSTR("[MAIN] Datetime: %s\n"), ntpDateTime().c_str());
- }
- #endif
-
- return true;
- }
-
- void configure() {
- #if DEBUG_LOG_BUFFER_SUPPORT
- if (settings::buffer()) {
- debug::buffer::enable(settings::bufferSize());
- }
- #endif
-
- ::systemHeartbeat(status,
- settings::heartbeatMode(),
- settings::heartbeatInterval());
- }
-
- void onBoot() {
- static_assert(
- std::is_same<int, std::underlying_type<DebugLogMode>::type>::value,
- "should be able to match DebugLogMode with int"
- );
-
- switch (settings::mode()) {
- case DebugLogMode::SkipBoot:
- debug::delayedEnable();
- break;
- case DebugLogMode::Disabled:
- debug::disable();
- break;
- case DebugLogMode::Enabled:
- debug::enable();
- break;
- }
-
- #if DEBUG_SERIAL_SUPPORT
- espurna::debug::serial::setup();
- #endif
-
- configure();
- }
-
- #if TERMINAL_SUPPORT
- namespace terminal {
-
- PROGMEM_STRING(DebugBuffer, "DEBUG.BUFFER");
-
- void debug_buffer(::terminal::CommandContext&& ctx) {
- debug::buffer::disable();
- if (!debug::buffer::size()) {
- terminalError(ctx, F("buffer is empty\n"));
- return;
- }
-
- ctx.output.printf_P(PSTR("buffer size: %u / %u bytes\n"),
- debug::buffer::size(), debug::buffer::capacity());
- debug::buffer::dump(ctx.output);
- terminalOK(ctx);
- }
-
- static constexpr ::terminal::Command commands[] PROGMEM {
- {DebugBuffer, debug_buffer},
- };
-
- void setup() {
- espurna::terminal::add(commands);
- }
-
- } // namespace terminal
- #endif
-
- } // namespace
- } // namespace debug
- } // namespace espurna
-
- void debugSendBytes(const uint8_t* bytes, size_t size) {
- espurna::debug::buffer::sendBytes(bytes, size);
- }
-
- #if DEBUG_LOG_BUFFER_SUPPORT
- bool debugLogBuffer() {
- return espurna::debug::buffer::enabled();
- }
- #endif
-
- void debugSend(const char* format, ...) {
- if (espurna::debug::enabled()) {
- va_list args;
- va_start(args, format);
- espurna::debug::formatAndSend(format, args);
- va_end(args);
- }
- }
-
- void debugConfigureBoot() {
- espurna::debug::onBoot();
- }
-
- #if WEB_SUPPORT
- void debugWebSetup() {
- wsRegister()
- .onVisible(espurna::debug::web::onVisible);
- }
- #endif
-
- void debugShowBanner() {
- #if DEBUG_SERIAL_SUPPORT
- if (espurna::debug::buffer::enabled()) {
- return;
- }
-
- const auto app = buildApp();
- DEBUG_MSG_P(PSTR("[MAIN] %s %s built %s\n"),
- app.name.c_str(), app.version.c_str(),
- app.build_time.c_str());
-
- DEBUG_MSG_P(PSTR("[MAIN] %s\n"), app.author.c_str());
- DEBUG_MSG_P(PSTR("[MAIN] %s\n"), app.website.c_str());
-
- DEBUG_MSG_P(PSTR("[MAIN] CPU chip ID: %s frequency: %hhuMHz\n"),
- systemChipId().c_str(), system_get_cpu_freq());
-
- const auto device = systemDevice();
- DEBUG_MSG_P(PSTR("[MAIN] Device: %s\n"),
- device.c_str());
- #endif
- }
-
- void debugSetup() {
- #if DEBUG_UDP_SUPPORT
- if (espurna::debug::syslog::build::enabled()) {
- espurna::debug::syslog::configure();
- espurnaRegisterReload(espurna::debug::syslog::configure);
- }
- #endif
- #if DEBUG_LOG_BUFFER_SUPPORT
- #if TERMINAL_SUPPORT
- espurna::debug::terminal::setup();
- #endif
- #endif
- }
-
- #endif // DEBUG_SUPPORT
|