mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
Merge pull request #840 from proddy/dev
added version upgrade/downgrade #832
This commit is contained in:
19
Makefile
19
Makefile
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# GNUMakefile for EMS-ESP
|
||||
# (c) 2020 Paul Derbyshire
|
||||
#
|
||||
|
||||
NUMJOBS=${NUMJOBS:-" -j4 "}
|
||||
MAKEFLAGS+="j "
|
||||
#----------------------------------------------------------------------
|
||||
@@ -17,23 +17,28 @@ MAKEFLAGS+="j "
|
||||
#TARGET := $(notdir $(CURDIR))
|
||||
TARGET := emsesp
|
||||
BUILD := build
|
||||
SOURCES := src src/* lib_standalone lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src src/devices lib/ArduinoJson/src lib/PButton
|
||||
INCLUDES := src lib_standalone lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src lib/* src/devices
|
||||
SOURCES := src src/* lib_standalone lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src src/devices lib/ArduinoJson/src lib/PButton lib/semver
|
||||
INCLUDES := src lib_standalone lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src lib/semver lib/* src/devices
|
||||
LIBRARIES :=
|
||||
|
||||
CPPCHECK = cppcheck
|
||||
# CHECKFLAGS = -q --force --std=c++17
|
||||
CHECKFLAGS = -q --force --std=c++11
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# Languages Standard
|
||||
#----------------------------------------------------------------------
|
||||
# C_STANDARD := -std=c17
|
||||
# CXX_STANDARD := -std=c++17
|
||||
C_STANDARD := -std=c11
|
||||
CXX_STANDARD := -std=c++11
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# Defined Symbols
|
||||
#----------------------------------------------------------------------
|
||||
DEFINES += -DFACTORY_WIFI_HOSTNAME=\"ems-esp\" -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0 -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_USE_SERIAL -DEMSESP_DEFAULT_BOARD_PROFILE=\"LOLIN\"
|
||||
DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0 -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_USE_SERIAL
|
||||
|
||||
DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.5.0b11\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# Sources & Files
|
||||
@@ -66,7 +71,7 @@ CXX := /usr/bin/g++
|
||||
# CXXFLAGS C++ Compiler Flags
|
||||
# LDFLAGS Linker Flags
|
||||
#----------------------------------------------------------------------
|
||||
CPPFLAGS += $(DEFINES) $(INCLUDE)
|
||||
CPPFLAGS += $(DEFINES) $(DEFAULTS) $(INCLUDE)
|
||||
CPPFLAGS += -ggdb
|
||||
CPPFLAGS += -g3
|
||||
CPPFLAGS += -Os
|
||||
@@ -114,6 +119,8 @@ COMPILE.cpp = $(CXX) $(CXX_STANDARD) $(CXXFLAGS) $(DEPFLAGS) -c $< -o $@
|
||||
# Targets
|
||||
#----------------------------------------------------------------------
|
||||
.PHONY: all
|
||||
.SILENT: $(OUTPUT)
|
||||
|
||||
all: $(OUTPUT)
|
||||
|
||||
$(OUTPUT): $(OBJS)
|
||||
@@ -147,4 +154,4 @@ help:
|
||||
@echo available targets: all run clean
|
||||
@echo $(OUTPUT)
|
||||
|
||||
-include $(DEPS)
|
||||
-include $(DEPS)
|
||||
@@ -41,7 +41,6 @@ class FSPersistence {
|
||||
Serial.println();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
_statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
|
||||
settingsFile.close();
|
||||
return;
|
||||
@@ -96,6 +95,7 @@ class FSPersistence {
|
||||
// debug added by Proddy
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_USE_SERIAL)
|
||||
Serial.println();
|
||||
Serial.printf("Writing to file: %s: ", _filePath);
|
||||
serializeJson(jsonDocument, Serial);
|
||||
Serial.println();
|
||||
|
||||
22
lib/semver/LICENSE
Normal file
22
lib/semver/LICENSE
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Marko Živanović
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
7
lib/semver/README.md
Normal file
7
lib/semver/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# About
|
||||
|
||||
This project is MIT-licensed, C++14 implementation of [semantic versioning](http://semver.org) parser and comparator with support for modifying parsed version strings. Semantic versioning 2.0.0 specification is supported out-of-the-box and the code should be flexible-enough to support future revisions or other similar versioning schemes.
|
||||
|
||||
Copyright (c) 2015 Marko Zivanovic
|
||||
|
||||
Based on https://github.com/zmarko/semver
|
||||
121
lib/semver/Semver200_comparator.cpp
Normal file
121
lib/semver/Semver200_comparator.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Marko Zivanovic
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include "semver200.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace version {
|
||||
|
||||
namespace {
|
||||
|
||||
// Compare normal version identifiers.
|
||||
int compare_normal(const Version_data & l, const Version_data & r) {
|
||||
if (l.major > r.major)
|
||||
return 1;
|
||||
if (l.major < r.major)
|
||||
return -1;
|
||||
if (l.minor > r.minor)
|
||||
return 1;
|
||||
if (l.minor < r.minor)
|
||||
return -1;
|
||||
if (l.patch > r.patch)
|
||||
return 1;
|
||||
if (l.patch < r.patch)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Compare alphanumeric prerelease identifiers.
|
||||
inline int cmp_alnum_prerel_ids(const string & l, const string & r) {
|
||||
auto cmp = l.compare(r);
|
||||
if (cmp == 0) {
|
||||
return cmp;
|
||||
} else {
|
||||
return cmp > 0 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Compare numeric prerelease identifiers.
|
||||
inline int cmp_num_prerel_ids(const string & l, const string & r) {
|
||||
long long li = stoll(l);
|
||||
long long ri = stoll(r);
|
||||
if (li == ri)
|
||||
return 0;
|
||||
return li > ri ? 1 : -1;
|
||||
}
|
||||
|
||||
using Prerel_type_pair = pair<Id_type, Id_type>;
|
||||
using Prerel_id_comparator = function<int(const string &, const string &)>;
|
||||
const map<Prerel_type_pair, Prerel_id_comparator> comparators = {{{Id_type::alnum, Id_type::alnum}, cmp_alnum_prerel_ids},
|
||||
{{Id_type::alnum, Id_type::num}, [](const string &, const string &) { return 1; }},
|
||||
{{Id_type::num, Id_type::alnum}, [](const string &, const string &) { return -1; }},
|
||||
{{Id_type::num, Id_type::num}, cmp_num_prerel_ids}};
|
||||
|
||||
// Compare prerelease identifiers based on their types.
|
||||
inline int compare_prerel_identifiers(const Prerelease_identifier & l, const Prerelease_identifier & r) {
|
||||
auto cmp = comparators.at({l.second, r.second});
|
||||
return cmp(l.first, r.first);
|
||||
}
|
||||
|
||||
inline int cmp_rel_prerel(const Prerelease_identifiers & l, const Prerelease_identifiers & r) {
|
||||
if (l.empty() && !r.empty())
|
||||
return 1;
|
||||
if (r.empty() && !l.empty())
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Semver200_comparator::compare(const Version_data & l, const Version_data & r) const {
|
||||
// Compare normal version components.
|
||||
int cmp = compare_normal(l, r);
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
|
||||
// Compare if one version is release and the other prerelease - release is always higher.
|
||||
cmp = cmp_rel_prerel(l.prerelease_ids, r.prerelease_ids);
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
|
||||
// Compare prerelease by looking at each identifier: numeric ones are compared as numbers,
|
||||
// alphanum as ASCII strings.
|
||||
auto shorter = min(l.prerelease_ids.size(), r.prerelease_ids.size());
|
||||
for (size_t i = 0; i < shorter; i++) {
|
||||
cmp = compare_prerel_identifiers(l.prerelease_ids[i], r.prerelease_ids[i]);
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
}
|
||||
|
||||
// Prerelease identifiers are the same, to the length of the shorter version string;
|
||||
// if they are the same length, then versions are equal, otherwise, longer one wins.
|
||||
if (l.prerelease_ids.size() == r.prerelease_ids.size())
|
||||
return 0;
|
||||
return l.prerelease_ids.size() > r.prerelease_ids.size() ? 1 : -1;
|
||||
}
|
||||
|
||||
} // namespace version
|
||||
194
lib/semver/Semver200_parser.cpp
Normal file
194
lib/semver/Semver200_parser.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Marko Zivanovic
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include "semver200.h"
|
||||
|
||||
#include "../../src/emsesp_stub.hpp" // for logging
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// disable symbol name too long warning
|
||||
#pragma warning(disable : 4503)
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace version {
|
||||
|
||||
namespace {
|
||||
enum class Parser_state { major, minor, patch, prerelease, build };
|
||||
|
||||
using Validator = function<void(const string &, const char)>;
|
||||
using State_transition_hook = function<void(string &)>;
|
||||
/// State transition is described by a character that triggers it, a state to transition to and
|
||||
/// optional hook to be invoked on transition.
|
||||
using Transition = tuple<const char, Parser_state, State_transition_hook>;
|
||||
using Transitions = vector<Transition>;
|
||||
using State = tuple<Transitions, string &, Validator>;
|
||||
using State_machine = std::map<Parser_state, State>;
|
||||
|
||||
// Ranges of characters allowed in prerelease and build identifiers.
|
||||
const vector<pair<char, char>> allowed_prerel_id_chars = {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}, {'-', '-'}};
|
||||
|
||||
inline Transition mkx(const char c, Parser_state p, State_transition_hook pth) {
|
||||
return make_tuple(c, p, pth);
|
||||
}
|
||||
|
||||
inline void Parse_error(const std::string & s) {
|
||||
emsesp::EMSESP::logger().err("parse error: %s", s.c_str());
|
||||
}
|
||||
|
||||
/// Advance parser state machine by a single step.
|
||||
/**
|
||||
Perform single step of parser state machine: if character matches one from transition tables -
|
||||
trigger transition to next state; otherwise, validate if current token is in legal state
|
||||
(throw Parse_error if not) and then add character to current token; State transition includes
|
||||
preparing various vars for next state and invoking state transition hook (if specified) which is
|
||||
where whole tokens are validated.
|
||||
*/
|
||||
inline void process_char(const char c, Parser_state & cstate, Parser_state & pstate, const Transitions & transitions, string & target, Validator validate) {
|
||||
for (const auto & transition : transitions) {
|
||||
if (c == get<0>(transition)) {
|
||||
if (get<2>(transition))
|
||||
get<2>(transition)(target);
|
||||
pstate = cstate;
|
||||
cstate = get<1>(transition);
|
||||
return;
|
||||
}
|
||||
}
|
||||
validate(target, c);
|
||||
target.push_back(c);
|
||||
}
|
||||
|
||||
/// Validate normal (major, minor, patch) version components.
|
||||
inline void normal_version_validator(const string & tgt, const char c) {
|
||||
if (c < '0' || c > '9')
|
||||
Parse_error("invalid character encountered: " + string(1, c));
|
||||
if (tgt.compare(0, 1, "0") == 0)
|
||||
Parse_error("leading 0 not allowed");
|
||||
}
|
||||
|
||||
/// Validate that prerelease and build version identifiers are comprised of allowed chars only.
|
||||
inline void prerelease_version_validator(const string &, const char c) {
|
||||
bool res = false;
|
||||
for (const auto & r : allowed_prerel_id_chars) {
|
||||
res |= (c >= r.first && c <= r.second);
|
||||
}
|
||||
if (!res)
|
||||
Parse_error("invalid character encountered: " + string(1, c));
|
||||
}
|
||||
|
||||
inline bool is_identifier_numeric(const string & id) {
|
||||
return id.find_first_not_of("0123456789") == string::npos;
|
||||
}
|
||||
|
||||
inline bool check_for_leading_0(const string & str) {
|
||||
return str.length() > 1 && str[0] == '0';
|
||||
}
|
||||
|
||||
/// Validate every individual prerelease identifier, determine it's type and add it to collection.
|
||||
void prerelease_hook_impl(string & id, Prerelease_identifiers & prerelease) {
|
||||
if (id.empty())
|
||||
Parse_error("version identifier cannot be empty");
|
||||
Id_type t = Id_type::alnum;
|
||||
if (is_identifier_numeric(id)) {
|
||||
t = Id_type::num;
|
||||
if (check_for_leading_0(id)) {
|
||||
Parse_error("numeric identifiers cannot have leading 0");
|
||||
}
|
||||
}
|
||||
prerelease.push_back(Prerelease_identifier(id, t));
|
||||
id.clear();
|
||||
}
|
||||
|
||||
/// Validate every individual build identifier and add it to collection.
|
||||
void build_hook_impl(string & id, Parser_state & pstate, Build_identifiers & build, std::string & prerelease_id, Prerelease_identifiers & prerelease) {
|
||||
// process last token left from parsing prerelease data
|
||||
if (pstate == Parser_state::prerelease)
|
||||
prerelease_hook_impl(prerelease_id, prerelease);
|
||||
if (id.empty())
|
||||
Parse_error("version identifier cannot be empty");
|
||||
build.push_back(id);
|
||||
id.clear();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/// Parse semver 2.0.0-compatible string to Version_data structure.
|
||||
/**
|
||||
Version text parser is implemented as a state machine. In each step one successive character from version
|
||||
string is consumed and is either added to current token or triggers state transition. Hooks can be
|
||||
injected into state transitions for validation/customization purposes.
|
||||
*/
|
||||
Version_data Semver200_parser::parse(const string & s) const {
|
||||
string major;
|
||||
string minor;
|
||||
string patch;
|
||||
string prerelease_id;
|
||||
string build_id;
|
||||
Prerelease_identifiers prerelease;
|
||||
Build_identifiers build;
|
||||
Parser_state cstate{Parser_state::major};
|
||||
Parser_state pstate;
|
||||
|
||||
auto prerelease_hook = [&](string & id) { prerelease_hook_impl(id, prerelease); };
|
||||
|
||||
auto build_hook = [&](string & id) { build_hook_impl(id, pstate, build, prerelease_id, prerelease); };
|
||||
|
||||
// State transition tables
|
||||
auto major_trans = {mkx('.', Parser_state::minor, {})};
|
||||
auto minor_trans = {mkx('.', Parser_state::patch, {})};
|
||||
auto patch_trans = {mkx('-', Parser_state::prerelease, {}), mkx('+', Parser_state::build, {})};
|
||||
auto prerelease_trans = {// When identifier separator (.) is found, stay in the same state but invoke hook
|
||||
// in order to process each individual identifier separately.
|
||||
mkx('.', Parser_state::prerelease, prerelease_hook),
|
||||
mkx('+', Parser_state::build, {})};
|
||||
auto build_trans = {// Same stay-in-the-same-state-but-invoke-hook trick from above.
|
||||
mkx('.', Parser_state::build, build_hook)};
|
||||
|
||||
State_machine state_machine = {{Parser_state::major, State{major_trans, major, normal_version_validator}},
|
||||
{Parser_state::minor, State{minor_trans, minor, normal_version_validator}},
|
||||
{Parser_state::patch, State{patch_trans, patch, normal_version_validator}},
|
||||
{Parser_state::prerelease, State{prerelease_trans, prerelease_id, prerelease_version_validator}},
|
||||
{Parser_state::build, State{build_trans, build_id, prerelease_version_validator}}};
|
||||
|
||||
// Main loop.
|
||||
for (const auto & c : s) {
|
||||
auto state = state_machine.at(cstate);
|
||||
process_char(c, cstate, pstate, get<0>(state), get<1>(state), get<2>(state));
|
||||
}
|
||||
|
||||
// Trigger appropriate hooks in order to process last token, because no state transition was
|
||||
// triggered for it.
|
||||
if (cstate == Parser_state::prerelease) {
|
||||
prerelease_hook(prerelease_id);
|
||||
} else if (cstate == Parser_state::build) {
|
||||
build_hook(build_id);
|
||||
}
|
||||
|
||||
return Version_data{stoi(major), stoi(minor), stoi(patch), prerelease, build};
|
||||
}
|
||||
|
||||
} // namespace version
|
||||
53
lib/semver/semver200.h
Normal file
53
lib/semver/semver200.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Marko Zivanovic
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "version.h"
|
||||
|
||||
namespace version {
|
||||
|
||||
/// Parse string into Version_data structure according to semantic versioning 2.0.0 rules.
|
||||
struct Semver200_parser {
|
||||
Version_data parse(const std::string &) const;
|
||||
};
|
||||
|
||||
/// Compare Version_data to another using semantic versioning 2.0.0 rules.
|
||||
struct Semver200_comparator {
|
||||
int compare(const Version_data &, const Version_data &) const;
|
||||
};
|
||||
|
||||
/// Concrete version class that binds all semver 2.0.0 functionality together.
|
||||
class Semver200_version : public Basic_version<Semver200_parser, Semver200_comparator> {
|
||||
public:
|
||||
Semver200_version()
|
||||
: Basic_version{Semver200_parser(), Semver200_comparator()} {
|
||||
}
|
||||
|
||||
Semver200_version(const std::string & v)
|
||||
: Basic_version{v, Semver200_parser(), Semver200_comparator()} {
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace version
|
||||
149
lib/semver/version.h
Normal file
149
lib/semver/version.h
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Marko Zivanovic
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace version {
|
||||
|
||||
/// Type of prerelease identifier: alphanumeric or numeric.
|
||||
/**
|
||||
Type of identifier affects comparison: alphanumeric identifiers are compared as ASCII strings, while
|
||||
numeric identifiers are compared as numbers.
|
||||
*/
|
||||
enum class Id_type {
|
||||
alnum, ///< Identifier is alphanumerical
|
||||
num ///< Identifier is numeric
|
||||
};
|
||||
|
||||
/// Container for prerelease identifier value and it's type.
|
||||
/**
|
||||
Prerelease version string consist of an optional series of dot-separated identifiers.
|
||||
These identifiers can be either numerical or alphanumerical.
|
||||
This structure describes one such identifier.
|
||||
*/
|
||||
using Prerelease_identifier = std::pair<std::string, Id_type>;
|
||||
|
||||
/// Container for all prerelease identifiers for a given version string.
|
||||
using Prerelease_identifiers = std::vector<Prerelease_identifier>;
|
||||
|
||||
/// Build identifier is arbitrary string with no special meaning with regards to version precedence.
|
||||
using Build_identifier = std::string;
|
||||
|
||||
/// Container for all build identifiers of a given version string.
|
||||
using Build_identifiers = std::vector<Build_identifier>;
|
||||
|
||||
/// Description of version broken into parts, as per semantic versioning specification.
|
||||
struct Version_data {
|
||||
Version_data(const int M, const int m, const int p, const Prerelease_identifiers & pr, const Build_identifiers & b)
|
||||
: major{M}
|
||||
, minor{m}
|
||||
, patch{p}
|
||||
, prerelease_ids{pr}
|
||||
, build_ids{b} {
|
||||
}
|
||||
|
||||
int major; ///< Major version, change only on incompatible API modifications.
|
||||
int minor; ///< Minor version, change on backwards-compatible API modifications.
|
||||
int patch; ///< Patch version, change only on bugfixes.
|
||||
|
||||
/// Optional series of prerelease identifiers.
|
||||
Prerelease_identifiers prerelease_ids;
|
||||
|
||||
/// Optional series of build identifiers.
|
||||
Build_identifiers build_ids;
|
||||
};
|
||||
|
||||
// Forward declaration required for operators' template declarations.
|
||||
template <typename Parser, typename Comparator>
|
||||
class Basic_version;
|
||||
|
||||
/// Test if left-hand version operand is of lower precedence than the right-hand version.
|
||||
template <typename Parser, typename Comparator>
|
||||
bool operator<(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
||||
|
||||
/// Test if left-hand version operand if of equal precedence as the right-hand version.
|
||||
template <typename Parser, typename Comparator>
|
||||
bool operator==(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
||||
|
||||
/// Test if left-hand version and right-hand version are of different precedence.
|
||||
template <typename Parser, typename Comparator>
|
||||
bool operator!=(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
||||
|
||||
/// Test if left-hand version operand is of higher precedence than the right-hand version.
|
||||
template <typename Parser, typename Comparator>
|
||||
bool operator>(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
||||
|
||||
/// Test if left-hand version operand is of higher or equal precedence as the right-hand version.
|
||||
template <typename Parser, typename Comparator>
|
||||
bool operator>=(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
||||
|
||||
/// Test if left-hand version operand is of lower or equal precedence as the right-hand version.
|
||||
template <typename Parser, typename Comparator>
|
||||
bool operator<=(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
||||
|
||||
/// Base class for various version parsing, precedence ordering and data manipulation schemes.
|
||||
/**
|
||||
Basic_version class describes general version object without prescribing parsing,
|
||||
validation, comparison and modification rules. These rules are implemented by supplied Parser, Comparator
|
||||
and Modifier objects.
|
||||
*/
|
||||
template <typename Parser, typename Comparator>
|
||||
class Basic_version {
|
||||
public:
|
||||
/// Construct Basic_version object using Parser object to parse default ("0.0.0") version string, Comparator for comparison and Modifier for modification.
|
||||
Basic_version(Parser, Comparator);
|
||||
|
||||
/// Construct Basic_version object using Parser to parse supplied version string, Comparator for comparison and Modifier for modification.
|
||||
Basic_version(const std::string &, Parser, Comparator);
|
||||
|
||||
/// Construct Basic_version object using supplied Version_data, Parser, Comparator and Modifier objects.
|
||||
Basic_version(const Version_data &, Parser, Comparator);
|
||||
|
||||
/// Construct Basic_version by copying data from another one.
|
||||
Basic_version(const Basic_version &);
|
||||
|
||||
/// Copy version data from another Basic_version to this one.
|
||||
Basic_version & operator=(const Basic_version &);
|
||||
|
||||
int major() const; ///< Get major version.
|
||||
int minor() const; ///< Get minor version.
|
||||
int patch() const; ///< Get patch version.
|
||||
const std::string prerelease() const; ///< Get prerelease version string.
|
||||
const std::string build() const; ///< Get build version string.
|
||||
|
||||
friend bool operator< <>(const Basic_version &, const Basic_version &);
|
||||
friend bool operator==<>(const Basic_version &, const Basic_version &);
|
||||
|
||||
private:
|
||||
Parser parser_;
|
||||
Comparator comparator_;
|
||||
Version_data ver_;
|
||||
};
|
||||
|
||||
} // namespace version
|
||||
|
||||
#include "version.inl"
|
||||
132
lib/semver/version.inl
Normal file
132
lib/semver/version.inl
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Marko Zivanovic
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "version.h"
|
||||
|
||||
namespace version {
|
||||
|
||||
namespace {
|
||||
|
||||
/// Utility function to splice all vector elements to output stream, using designated separator
|
||||
/// between elements and function object for getting values from vector elements.
|
||||
template <typename T, typename F>
|
||||
std::string & splice(std::string & ss, const std::vector<T> & vec, const std::string & sep, F read) {
|
||||
if (!vec.empty()) {
|
||||
for (auto it = vec.cbegin(); it < vec.cend() - 1; ++it) {
|
||||
ss += read(*it) + sep;
|
||||
}
|
||||
ss += read(*vec.crbegin());
|
||||
}
|
||||
return ss;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
template <typename Parser, typename Comparator>
|
||||
Basic_version<Parser, Comparator>::Basic_version(Parser p, Comparator c)
|
||||
: parser_(p)
|
||||
, comparator_(c)
|
||||
, ver_(parser_.parse("0.0.0")) {
|
||||
}
|
||||
|
||||
template <typename Parser, typename Comparator>
|
||||
Basic_version<Parser, Comparator>::Basic_version(const std::string & v, Parser p, Comparator c)
|
||||
: parser_(p)
|
||||
, comparator_(c)
|
||||
, ver_(parser_.parse(v)) {
|
||||
}
|
||||
|
||||
template <typename Parser, typename Comparator>
|
||||
Basic_version<Parser, Comparator>::Basic_version(const Version_data & v, Parser p, Comparator c)
|
||||
: parser_(p)
|
||||
, comparator_(c)
|
||||
, ver_(v) {
|
||||
}
|
||||
|
||||
template <typename Parser, typename Comparator>
|
||||
Basic_version<Parser, Comparator>::Basic_version(const Basic_version<Parser, Comparator> &) = default;
|
||||
|
||||
template <typename Parser, typename Comparator>
|
||||
Basic_version<Parser, Comparator> & Basic_version<Parser, Comparator>::operator=(const Basic_version<Parser, Comparator> &) = default;
|
||||
|
||||
template <typename Parser, typename Comparator>
|
||||
int Basic_version<Parser, Comparator>::major() const {
|
||||
return ver_.major;
|
||||
}
|
||||
|
||||
template <typename Parser, typename Comparator>
|
||||
int Basic_version<Parser, Comparator>::minor() const {
|
||||
return ver_.minor;
|
||||
}
|
||||
|
||||
template <typename Parser, typename Comparator>
|
||||
int Basic_version<Parser, Comparator>::patch() const {
|
||||
return ver_.patch;
|
||||
}
|
||||
|
||||
template <typename Parser, typename Comparator>
|
||||
const std::string Basic_version<Parser, Comparator>::prerelease() const {
|
||||
std::string ss;
|
||||
return splice(ss, ver_.prerelease_ids, ".", [](const Prerelease_identifier & id) { return id.first; });
|
||||
}
|
||||
|
||||
template <typename Parser, typename Comparator>
|
||||
const std::string Basic_version<Parser, Comparator>::build() const {
|
||||
std::string ss;
|
||||
return splice(ss, ver_.build_ids, ".", [](const std::string & id) { return id; });
|
||||
}
|
||||
|
||||
template <typename Parser, typename Comparator>
|
||||
bool operator<(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
|
||||
return l.comparator_.compare(l.ver_, r.ver_) == -1;
|
||||
}
|
||||
|
||||
template <typename Parser, typename Comparator>
|
||||
bool operator==(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
|
||||
return l.comparator_.compare(l.ver_, r.ver_) == 0;
|
||||
}
|
||||
|
||||
template <typename Parser, typename Comparator>
|
||||
inline bool operator!=(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
|
||||
return !(l == r);
|
||||
}
|
||||
|
||||
template <typename Parser, typename Comparator>
|
||||
inline bool operator>(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
|
||||
return r < l;
|
||||
}
|
||||
|
||||
template <typename Parser, typename Comparator>
|
||||
inline bool operator>=(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
|
||||
return !(l < r);
|
||||
}
|
||||
|
||||
template <typename Parser, typename Comparator>
|
||||
inline bool operator<=(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
|
||||
return !(l > r);
|
||||
}
|
||||
|
||||
} // namespace version
|
||||
@@ -26,43 +26,9 @@
|
||||
|
||||
class DummySettings {
|
||||
public:
|
||||
std::string version{"poerp"};
|
||||
String locale = "en";
|
||||
uint8_t tx_mode = 1;
|
||||
uint8_t ems_bus_id = 0x0B;
|
||||
bool syslog_enabled = false;
|
||||
int8_t syslog_level = 3; // uuid::log::Level
|
||||
uint32_t syslog_mark_interval = 0;
|
||||
String syslog_host = "192.168.1.4";
|
||||
uint16_t syslog_port = 514;
|
||||
bool shower_timer = true;
|
||||
bool shower_alert = false;
|
||||
uint8_t shower_alert_coldshot = 10;
|
||||
uint8_t shower_alert_trigger = 7;
|
||||
bool hide_led = false;
|
||||
bool notoken_api = false;
|
||||
bool readonly_mode = false;
|
||||
uint8_t bool_format = 1; // using "on" and "off"
|
||||
uint8_t bool_dashboard = 1;
|
||||
uint8_t enum_format = 1;
|
||||
bool nosleep = false;
|
||||
bool fahrenheit = false;
|
||||
bool bandwidth20 = false;
|
||||
bool telnet_enabled = false;
|
||||
String board_profile = "CUSTOM";
|
||||
bool trace_raw = false;
|
||||
bool analog_enabled = true; // analog is enabled
|
||||
int8_t weblog_level = 1;
|
||||
uint8_t weblog_buffer = 50;
|
||||
bool weblog_compact = true;
|
||||
uint8_t rx_gpio = 0;
|
||||
uint8_t tx_gpio = 0;
|
||||
uint8_t dallas_gpio = 16; // to ensure its enabled
|
||||
bool dallas_parasite = false;
|
||||
uint8_t led_gpio = 0;
|
||||
bool low_clock = false;
|
||||
uint8_t pbutton_gpio = false;
|
||||
uint8_t solar_maxflow = 30;
|
||||
// SYSTEM
|
||||
bool bandwidth20 = false;
|
||||
bool nosleep = false;
|
||||
|
||||
// MQTT
|
||||
uint16_t publish_time = 10;
|
||||
@@ -107,13 +73,6 @@ class DummySettings {
|
||||
bool enableCORS = false;
|
||||
String CORSOrigin = "*";
|
||||
|
||||
uint8_t phy_type = 0;
|
||||
uint8_t eth_power = 0; // 0 means -1
|
||||
uint8_t eth_phy_addr = 0;
|
||||
uint8_t eth_clock_mode = 0;
|
||||
|
||||
#define FACTORY_MQTT_MAX_TOPIC_LENGTH 128
|
||||
|
||||
static void read(DummySettings & settings, JsonObject & root){};
|
||||
static void read(DummySettings & settings){};
|
||||
|
||||
@@ -188,4 +147,4 @@ class JsonUtils {
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -260,7 +260,7 @@ const UPLOAD_FILE_ENDPOINT = REST_ENDPOINT_ROOT + 'uploadFile'
|
||||
const SIGN_IN_ENDPOINT = REST_ENDPOINT_ROOT + 'signIn'
|
||||
const GENERATE_TOKEN_ENDPOINT = REST_ENDPOINT_ROOT + 'generateToken'
|
||||
const system_status = {
|
||||
emsesp_version: '3.5.0demo',
|
||||
emsesp_version: '3.5.0-demo',
|
||||
esp_platform: 'ESP32',
|
||||
max_alloc_heap: 89,
|
||||
psram_size: 0,
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
|
||||
[common]
|
||||
; custom build flags
|
||||
; options are: EMSESP_DEBUG, EMSESP_UART_DEBUG, EMSESP_DEBUG_SENSOR, EMSESP_WIFI_TWEAK, EMSESP_DEFAULT_BOARD_PROFILE
|
||||
; options are: EMSESP_DEBUG, EMSESP_UART_DEBUG, EMSESP_DEBUG_SENSOR, EMSESP_WIFI_TWEAK, EMSESP_DEFAULT_BOARD_PROFILE EMSESP_DEBUG_LIMITED
|
||||
; my_build_flags = -DEMSESP_DEBUG -DEMSESP_DEFAULT_BOARD_PROFILE=\"NODEMCU\"
|
||||
; my_build_flags = -DEMSESP_DEBUG_SENSOR
|
||||
; my_build_flags = -DEMSESP_DEBUG -DEMSESP_USE_SERIAL
|
||||
; my_build_flags = -DEMSESP_DEBUG -DCORE_DEBUG_LEVEL=5 ; 5=verbose, 4=debug, 3=info
|
||||
my_build_flags = -DEMSESP_DEBUG -DEMSESP_DEBUG_LIMITED -DEMSESP_USE_SERIAL
|
||||
|
||||
[env:esp32_4M]
|
||||
; if using OTA enter your details below
|
||||
|
||||
@@ -11,13 +11,15 @@ extra_configs =
|
||||
|
||||
[common]
|
||||
core_build_flags =
|
||||
-Wall
|
||||
; -Wall -Wextra -Werror -Wswitch-enum -Wno-unused-parameter
|
||||
-D CORE_DEBUG_LEVEL=0
|
||||
-D NDEBUG
|
||||
-D ARDUINO_ARCH_ESP32=1
|
||||
-D ESP32=1
|
||||
; -Os
|
||||
|
||||
core_unbuild_flags =
|
||||
; core_unbuild_flags = -std=gnu++11
|
||||
core_unbuild_flags =
|
||||
|
||||
; my_build_flags is set in pio_local.ini
|
||||
my_build_flags =
|
||||
@@ -42,7 +44,6 @@ monitor_raw = yes
|
||||
upload_speed = 921600
|
||||
build_type = release
|
||||
lib_ldf_mode = chain+
|
||||
debug_build_flags = -Os # optimize for size
|
||||
|
||||
check_tool = cppcheck, clangtidy
|
||||
check_severity = high, medium
|
||||
@@ -56,7 +57,8 @@ check_flags =
|
||||
extra_scripts = scripts/rename_fw.py
|
||||
board = esp32dev
|
||||
platform = espressif32
|
||||
board_build.partitions = esp32_partition_4M.csv
|
||||
board_build.partitions = esp32_partition_4M.csv
|
||||
board_build.filesystem = littlefs
|
||||
build_flags = ${common.build_flags}
|
||||
build_unflags = ${common.unbuild_flags}
|
||||
|
||||
|
||||
104
scripts/app-tls-size.py
Normal file
104
scripts/app-tls-size.py
Normal file
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python3
|
||||
# app-tls-size - Calculate size of Thread-Local Storage
|
||||
# Copyright 2022 Simon Arlott
|
||||
|
||||
# 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
# PlatformIO usage:
|
||||
#
|
||||
# [env:...]
|
||||
# extra_scripts = post:app-tls-size.py
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
RE_ELF_SECTION = re.compile(r"^\s*(?P<type>\w+)\s+(?P<offset>\w+)\s+(?P<virtaddr>\w+)\s+(?P<physaddr>\w+)\s+(?P<filesiz>\w+)\s+(?P<memsiz>\w+)\s+(?P<ndx>\w+)\s+")
|
||||
Symbol = collections.namedtuple("Symbol", ["value", "size", "line"])
|
||||
RE_ELF_SYMBOL = re.compile(r"^(?P<before_value>\s*(?P<num>\w+):\s+)(?P<value>\w+)(?P<after_value>\s+(?P<size>\w+)\s+(?P<type>\w+)\s+(?P<bind>\w+)\s+(?P<visibility>\w+)\s+(?P<ndx>\w+)\s+(?P<name>\w+))")
|
||||
|
||||
def print_tls_size(fw_elf):
|
||||
tls_offset = None
|
||||
width = 8
|
||||
|
||||
lines = subprocess.run(["readelf", "-W", "--program-headers", fw_elf],
|
||||
check=True, universal_newlines=True, stdout=subprocess.PIPE
|
||||
).stdout.strip().split("\n")
|
||||
|
||||
for line in lines:
|
||||
match = RE_ELF_SECTION.match(line)
|
||||
if match:
|
||||
if tls_offset is None and match["type"] == "TLS":
|
||||
tls_offset = int(match["virtaddr"], 16)
|
||||
|
||||
header = True
|
||||
lines = subprocess.run(["readelf", "-W", "--syms", "--dyn-syms", fw_elf],
|
||||
check=True, universal_newlines=True, stdout=subprocess.PIPE
|
||||
).stdout.strip().split("\n")
|
||||
syms = set()
|
||||
|
||||
for line in lines:
|
||||
match = RE_ELF_SYMBOL.match(line)
|
||||
if match:
|
||||
header = False
|
||||
|
||||
if match["type"] == "TLS":
|
||||
syms.add(Symbol(int(match["value"], 16), int(match["size"]), line))
|
||||
width = len(match['value'])
|
||||
elif tls_offset is not None and (match["type"] == "NOTYPE" and match["bind"] == "GLOBAL"
|
||||
and match["visibility"] == "DEFAULT"
|
||||
and match["name"] in set(["_thread_local_start", "_thread_local_end"])
|
||||
):
|
||||
value = int(match["value"], 16) - tls_offset
|
||||
line = ("{1}{2:0{0}x}{3}").format(len(match['value']),
|
||||
match["before_value"], value, match["after_value"])
|
||||
syms.add(Symbol(value, int(match["size"]), line))
|
||||
|
||||
elif header:
|
||||
print(line)
|
||||
|
||||
if syms:
|
||||
syms = list(syms)
|
||||
syms.sort()
|
||||
size = (syms[-1].value + syms[-1].size) - syms[0].value
|
||||
else:
|
||||
size = 0
|
||||
|
||||
value = syms[0].value
|
||||
for sym in syms:
|
||||
if sym.value > value:
|
||||
print("\t{1:0{0}x} {2:5d} TLS UNKNOWN".format(width, value, sym.value - value))
|
||||
print(sym.line)
|
||||
value = sym.value + sym.size
|
||||
|
||||
print()
|
||||
print(f"Total Thread-Local Storage size: {size} bytes")
|
||||
|
||||
def after_fw_elf(source, target, env):
|
||||
fw_elf = str(target[0])
|
||||
print_tls_size(fw_elf)
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Calculate size of Thread-Local Storage")
|
||||
parser.add_argument("fw_elf", metavar="ELF", type=str, help="Firmware ELF filename")
|
||||
|
||||
args = parser.parse_args()
|
||||
print_tls_size(**vars(args))
|
||||
elif __name__ == "SCons.Script":
|
||||
Import("env")
|
||||
|
||||
env.AddPostAction("${BUILD_DIR}/${PROGNAME}.elf", after_fw_elf)
|
||||
|
||||
@@ -59,12 +59,12 @@ void EMSESPShell::stopped() {
|
||||
// this is one of the first functions called when the shell is started
|
||||
void EMSESPShell::display_banner() {
|
||||
println();
|
||||
printfln("┌──────────────────────────────────────┐");
|
||||
printfln("│ %sEMS-ESP version %-10s%s │", COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_BOLD_OFF);
|
||||
printfln("│ %s%shttps://github.com/emsesp/EMS-ESP32%s │", COLOR_BRIGHT_GREEN, COLOR_UNDERLINE, COLOR_RESET);
|
||||
printfln("│ │");
|
||||
printfln("│ type %shelp%s to show available commands │", COLOR_UNDERLINE, COLOR_RESET);
|
||||
printfln("└──────────────────────────────────────┘");
|
||||
printfln("┌────────────────────────────────────────────┐");
|
||||
printfln("│ %sEMS-ESP version %-10s%s │", COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_BOLD_OFF);
|
||||
printfln("│ %s%shttps://github.com/emsesp/EMS-ESP32%s │", COLOR_BRIGHT_GREEN, COLOR_UNDERLINE, COLOR_RESET);
|
||||
printfln("│ │");
|
||||
printfln("│ type %shelp%s to show available commands │", COLOR_UNDERLINE, COLOR_RESET);
|
||||
printfln("└────────────────────────────────────────────┘");
|
||||
println();
|
||||
|
||||
// set console name
|
||||
|
||||
@@ -21,14 +21,14 @@
|
||||
|
||||
// GENERAL SETTINGS
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#define EMSESP_DEFAULT_LOCALE EMSESP_LOCALE_EN // English
|
||||
#else
|
||||
// this is for debugging different languages in standalone version
|
||||
// #define EMSESP_DEFAULT_LOCALE EMSESP_LOCALE_DE // German
|
||||
#ifndef EMSESP_DEFAULT_LOCALE
|
||||
#define EMSESP_DEFAULT_LOCALE EMSESP_LOCALE_EN // English
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_VERSION
|
||||
#define EMSESP_DEFAULT_VERSION ""
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_TX_MODE
|
||||
#define EMSESP_DEFAULT_TX_MODE 1 // EMS1.0
|
||||
#endif
|
||||
@@ -228,5 +228,14 @@ enum {
|
||||
|
||||
};
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
#define EMSESP_PLATFORM "ESP32-C3";
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#define EMSESP_PLATFORM "ESP32-S2";
|
||||
#elif CONFIG_IDF_TARGET_ESP32 || EMSESP_STANDALONE
|
||||
#define EMSESP_PLATFORM "ESP32";
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1297,6 +1297,21 @@ void EMSESP::start() {
|
||||
}
|
||||
#endif
|
||||
|
||||
// do a quick scan of the filesystem to see if we have a /config folder
|
||||
// so we know if this is a new install or not
|
||||
#ifndef EMSESP_STANDALONE
|
||||
File root = LittleFS.open("/config");
|
||||
bool factory_settings = !root;
|
||||
if (!root) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
Serial.println("No config found, assuming factory settings");
|
||||
#endif
|
||||
}
|
||||
root.close();
|
||||
#else
|
||||
bool factory_settings = false;
|
||||
#endif
|
||||
|
||||
esp8266React.begin(); // loads core system services settings (network, mqtt, ap, ntp etc)
|
||||
webLogService.begin(); // start web log service. now we can start capturing logs to the web log
|
||||
|
||||
@@ -1315,12 +1330,13 @@ void EMSESP::start() {
|
||||
webSettingsService.begin(); // load EMS-ESP Application settings...
|
||||
|
||||
// do any system upgrades
|
||||
if (system_.check_upgrade()) {
|
||||
if (system_.check_upgrade(factory_settings)) {
|
||||
LOG_WARNING("System needs a restart to apply new settings. Please wait.");
|
||||
system_.system_restart();
|
||||
};
|
||||
|
||||
system_.reload_settings(); // ... and store some of the settings locally
|
||||
system_.reload_settings(); // ... and store some of the settings locally
|
||||
|
||||
webCustomizationService.begin(); // load the customizations
|
||||
|
||||
// start telnet service if it's enabled
|
||||
|
||||
11
src/mqtt.cpp
11
src/mqtt.cpp
@@ -192,6 +192,7 @@ void Mqtt::loop() {
|
||||
// print MQTT log and other stuff to console
|
||||
void Mqtt::show_mqtt(uuid::console::Shell & shell) {
|
||||
shell.printfln("MQTT is %s", connected() ? F_(connected) : F_(disconnected));
|
||||
shell.printfln("MQTT Entity ID format is %d", entity_format_);
|
||||
|
||||
shell.printfln("MQTT publish errors: %lu", mqtt_publish_fails_);
|
||||
shell.println();
|
||||
@@ -669,12 +670,12 @@ std::shared_ptr<const MqttMessage> Mqtt::queue_message(const uint8_t operation,
|
||||
#if defined(EMSESP_DEBUG)
|
||||
if (operation == Operation::PUBLISH) {
|
||||
if (message->payload.empty()) {
|
||||
LOG_INFO("[DEBUG] Adding to queue: (Publish) topic='%s' empty payload", message->topic.c_str());
|
||||
LOG_INFO("[DEBUG] Adding to queue: (publish) topic='%s' empty payload", message->topic.c_str());
|
||||
} else {
|
||||
LOG_INFO("[DEBUG] Adding to queue: (Publish) topic='%s' payload=%s", message->topic.c_str(), message->payload.c_str());
|
||||
LOG_INFO("[DEBUG] Adding to queue: (publish) topic='%s' payload=%s", message->topic.c_str(), message->payload.c_str());
|
||||
}
|
||||
} else {
|
||||
LOG_INFO("[DEBUG] Adding to queue: (Subscribe) topic='%s'", message->topic.c_str());
|
||||
LOG_INFO("[DEBUG] Adding to queue: (subscribe) topic='%s'", message->topic.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -986,7 +987,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
}
|
||||
|
||||
// build unique identifier also used as object_id and becomes the Entity ID in HA
|
||||
char uniq_id[70];
|
||||
char uniq_id[80];
|
||||
if (Mqtt::entity_format() == 2) {
|
||||
// prefix base name to each uniq_id and use the shortname
|
||||
snprintf(uniq_id, sizeof(uniq_id), "%s_%s_%s", mqtt_basename_.c_str(), device_name, entity_with_tag);
|
||||
@@ -997,7 +998,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
// entity_format is 0
|
||||
// old v3.4 style
|
||||
// take en_name and replace all spaces and lowercase it
|
||||
char uniq_s[40];
|
||||
char uniq_s[60];
|
||||
strlcpy(uniq_s, en_name, sizeof(uniq_s));
|
||||
Helpers::replace_char(uniq_s, ' ', '_');
|
||||
if (EMSdevice::tag_to_string(tag).empty()) {
|
||||
|
||||
110
src/system.cpp
110
src/system.cpp
@@ -19,6 +19,8 @@
|
||||
#include "system.h"
|
||||
#include "emsesp.h" // for send_raw_telegram() command
|
||||
|
||||
#include <semver200.h>
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#include "test/test.h"
|
||||
#endif
|
||||
@@ -218,7 +220,7 @@ void System::wifi_reconnect() {
|
||||
void System::format(uuid::console::Shell & shell) {
|
||||
auto msg = ("Formatting file system. This will reset all settings to their defaults");
|
||||
shell.logger().warning(msg);
|
||||
shell.flush();
|
||||
// shell.flush();
|
||||
|
||||
EMSuart::stop();
|
||||
|
||||
@@ -1013,45 +1015,74 @@ bool System::check_restore() {
|
||||
}
|
||||
|
||||
// handle upgrades from previous versions
|
||||
// this function will not be called on a clean install, with no settings files yet created
|
||||
// returns true if we need a reboot
|
||||
bool System::check_upgrade() {
|
||||
std::string old_version;
|
||||
bool System::check_upgrade(bool factory_settings) {
|
||||
bool missing_version = true;
|
||||
std::string settingsVersion{EMSESP_APP_VERSION}; // default setting version
|
||||
|
||||
// TODO fix
|
||||
if (!factory_settings) {
|
||||
// fetch current version from settings file
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) { settingsVersion = settings.version.c_str(); });
|
||||
|
||||
if (version_ != EMSESP_APP_VERSION) {
|
||||
if (version_.empty()) {
|
||||
LOG_DEBUG("No version, presuming fresh install. Setting to %s", EMSESP_APP_VERSION);
|
||||
old_version = EMSESP_APP_VERSION;
|
||||
} else {
|
||||
LOG_DEBUG("Going from version %s to %s", version_, EMSESP_APP_VERSION);
|
||||
old_version = version_;
|
||||
// see if we're missing a version, will be < 3.5.0b13 from Dec 23 2022
|
||||
missing_version = (settingsVersion.empty() || (settingsVersion.length() < 5));
|
||||
if (missing_version) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("No version information found (%s)", settingsVersion.c_str());
|
||||
#endif
|
||||
settingsVersion = "3.4.4"; // this was the last stable version
|
||||
}
|
||||
// save the new version
|
||||
version_ = EMSESP_APP_VERSION;
|
||||
EMSESP::webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
settings.version = EMSESP_APP_VERSION;
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
}
|
||||
|
||||
if (old_version == EMSESP_APP_VERSION) {
|
||||
return false; // no upgrades or reboot needed. we're on the latest
|
||||
version::Semver200_version settings_version(settingsVersion);
|
||||
|
||||
if (!missing_version) {
|
||||
LOG_INFO("Current version from settings is %d.%d.%d-%s",
|
||||
settings_version.major(),
|
||||
settings_version.minor(),
|
||||
settings_version.patch(),
|
||||
settings_version.prerelease().c_str());
|
||||
}
|
||||
|
||||
LOG_DEBUG("Doing upgrade..."); // TODO remove
|
||||
// always save the new version to the settings
|
||||
EMSESP::webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
settings.version = EMSESP_APP_VERSION;
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
|
||||
if (factory_settings) {
|
||||
return false; // fresh install, do nothing
|
||||
}
|
||||
|
||||
// check variations between versions
|
||||
// get major version
|
||||
|
||||
// get minor version
|
||||
|
||||
// get patch version (ignore alphanumerics)
|
||||
version::Semver200_version this_version(EMSESP_APP_VERSION);
|
||||
|
||||
// compare versions
|
||||
bool reboot_required = false;
|
||||
if (this_version > settings_version) {
|
||||
LOG_NOTICE("Upgrading to version %d.%d.%d-%s", this_version.major(), this_version.minor(), this_version.patch(), this_version.prerelease().c_str());
|
||||
|
||||
// if we're coming from 3.4.4 or 3.5.0b14 then we need to apply new settings
|
||||
if (missing_version) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("Setting MQTT ID Entity to v3.4 format");
|
||||
#endif
|
||||
EMSESP::esp8266React.getMqttSettingsService()->update(
|
||||
[&](MqttSettings & mqttSettings) {
|
||||
mqttSettings.entity_format = 0; // use old Entity ID format from v3.4
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
}
|
||||
|
||||
} else if (this_version < settings_version) {
|
||||
LOG_NOTICE("Downgrading to version %d.%d.%d-%s", this_version.major(), this_version.minor(), this_version.patch(), this_version.prerelease().c_str());
|
||||
} else {
|
||||
// same version, do nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
return reboot_required;
|
||||
}
|
||||
@@ -1299,18 +1330,17 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
node["pbutton gpio"] = settings.pbutton_gpio;
|
||||
node["led gpio"] = settings.led_gpio;
|
||||
}
|
||||
node["hide led"] = settings.hide_led;
|
||||
node["notoken api"] = settings.notoken_api;
|
||||
node["readonly mode"] = settings.readonly_mode;
|
||||
node["fahrenheit"] = settings.fahrenheit;
|
||||
node["dallas parasite"] = settings.dallas_parasite;
|
||||
node["bool format"] = settings.bool_format;
|
||||
node["bool dashboard"] = settings.bool_dashboard;
|
||||
node["enum format"] = settings.enum_format;
|
||||
node["analog enabled"] = settings.analog_enabled;
|
||||
node["telnet enabled"] = settings.telnet_enabled;
|
||||
node["max web log buffer"] = settings.weblog_buffer;
|
||||
node["web log buffered"] = EMSESP::webLogService.num_log_messages();
|
||||
node["hide led"] = settings.hide_led;
|
||||
node["notoken api"] = settings.notoken_api;
|
||||
node["readonly mode"] = settings.readonly_mode;
|
||||
node["fahrenheit"] = settings.fahrenheit;
|
||||
node["dallas parasite"] = settings.dallas_parasite;
|
||||
node["bool format"] = settings.bool_format;
|
||||
node["bool dashboard"] = settings.bool_dashboard;
|
||||
node["enum format"] = settings.enum_format;
|
||||
node["analog enabled"] = settings.analog_enabled;
|
||||
node["telnet enabled"] = settings.telnet_enabled;
|
||||
node["web log buffer"] = settings.weblog_buffer;
|
||||
});
|
||||
|
||||
// Devices - show EMS devices if we have any
|
||||
|
||||
@@ -74,7 +74,7 @@ class System {
|
||||
void reload_settings();
|
||||
void wifi_tweak();
|
||||
void syslog_init();
|
||||
bool check_upgrade();
|
||||
bool check_upgrade(bool factory_settings);
|
||||
bool check_restore();
|
||||
bool heartbeat_json(JsonObject & output);
|
||||
void send_heartbeat();
|
||||
@@ -273,7 +273,7 @@ class System {
|
||||
|
||||
// EMS-ESP settings
|
||||
// copies from WebSettings class in WebSettingsService.h and loaded with reload_settings()
|
||||
std::string hostname_ = FACTORY_WIFI_HOSTNAME;
|
||||
std::string hostname_;
|
||||
String locale_;
|
||||
bool hide_led_;
|
||||
uint8_t led_gpio_;
|
||||
@@ -295,7 +295,7 @@ class System {
|
||||
uint8_t bool_format_;
|
||||
uint8_t enum_format_;
|
||||
bool readonly_mode_;
|
||||
std::string version_;
|
||||
String version_;
|
||||
|
||||
// ethernet
|
||||
uint8_t phy_type_;
|
||||
|
||||
@@ -29,6 +29,7 @@ bool Test::run_test(const char * command, int8_t id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (strcmp(command, "general") == 0) {
|
||||
EMSESP::logger().info("Testing general. Adding a Boiler and Thermostat");
|
||||
|
||||
@@ -53,6 +54,8 @@ bool Test::run_test(const char * command, int8_t id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef EMSESP_DEBUG_LIMITED
|
||||
|
||||
if (strcmp(command, "2thermostats") == 0) {
|
||||
EMSESP::logger().info("Testing with multiple thermostats...");
|
||||
|
||||
@@ -216,6 +219,8 @@ bool Test::run_test(const char * command, int8_t id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -231,6 +236,9 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
EMSESP::watch(EMSESP::Watch::WATCH_RAW); // raw
|
||||
|
||||
std::string command(20, '\0');
|
||||
|
||||
#ifndef EMSESP_DEBUG_LIMITED
|
||||
|
||||
if ((cmd.empty()) || (cmd == "default")) {
|
||||
command = EMSESP_DEBUG_DEFAULT;
|
||||
} else {
|
||||
@@ -1548,6 +1556,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (command == "limited") {
|
||||
shell.printfln("Run a limited memory test...");
|
||||
|
||||
run_test("general");
|
||||
}
|
||||
}
|
||||
|
||||
// simulates a telegram in the Rx queue, but without the CRC which is added automatically
|
||||
|
||||
@@ -1,11 +1 @@
|
||||
#define EMSESP_APP_VERSION "3.5.0b13"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
#define EMSESP_PLATFORM "ESP32-C3";
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#define EMSESP_PLATFORM "ESP32-S2";
|
||||
#elif CONFIG_IDF_TARGET_ESP32 || EMSESP_STANDALONE
|
||||
#define EMSESP_PLATFORM "ESP32";
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#define EMSESP_APP_VERSION "3.5.0-dev.14"
|
||||
|
||||
@@ -37,6 +37,7 @@ WebSettingsService::WebSettingsService(AsyncWebServer * server, FS * fs, Securit
|
||||
}
|
||||
|
||||
void WebSettings::read(WebSettings & settings, JsonObject & root) {
|
||||
root["version"] = settings.version;
|
||||
root["locale"] = settings.locale;
|
||||
root["tx_mode"] = settings.tx_mode;
|
||||
root["ems_bus_id"] = settings.ems_bus_id;
|
||||
@@ -82,7 +83,7 @@ void WebSettings::read(WebSettings & settings, JsonObject & root) {
|
||||
StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) {
|
||||
// load the version of the settings
|
||||
// will be picked up in System::check_upgrade()
|
||||
settings.version = root["version"] || "";
|
||||
settings.version = root["version"] | EMSESP_DEFAULT_VERSION;
|
||||
|
||||
// load default GPIO configuration based on board profile
|
||||
std::vector<int8_t> data; // // led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode
|
||||
|
||||
@@ -29,41 +29,41 @@ namespace emsesp {
|
||||
|
||||
class WebSettings {
|
||||
public:
|
||||
std::string version;
|
||||
String locale;
|
||||
uint8_t tx_mode;
|
||||
uint8_t ems_bus_id;
|
||||
bool shower_timer;
|
||||
bool shower_alert;
|
||||
uint8_t shower_alert_trigger;
|
||||
uint8_t shower_alert_coldshot;
|
||||
bool syslog_enabled;
|
||||
int8_t syslog_level; // uuid::log::Level
|
||||
uint32_t syslog_mark_interval;
|
||||
String syslog_host;
|
||||
uint16_t syslog_port;
|
||||
bool trace_raw;
|
||||
uint8_t rx_gpio;
|
||||
uint8_t tx_gpio;
|
||||
uint8_t dallas_gpio;
|
||||
bool dallas_parasite;
|
||||
uint8_t led_gpio;
|
||||
bool hide_led;
|
||||
bool low_clock;
|
||||
bool telnet_enabled;
|
||||
bool notoken_api;
|
||||
bool readonly_mode;
|
||||
bool analog_enabled;
|
||||
uint8_t pbutton_gpio;
|
||||
uint8_t solar_maxflow;
|
||||
String board_profile;
|
||||
uint8_t bool_format;
|
||||
uint8_t bool_dashboard;
|
||||
uint8_t enum_format;
|
||||
int8_t weblog_level;
|
||||
uint8_t weblog_buffer;
|
||||
bool weblog_compact;
|
||||
bool fahrenheit;
|
||||
String version;
|
||||
String locale;
|
||||
uint8_t tx_mode;
|
||||
uint8_t ems_bus_id;
|
||||
bool shower_timer;
|
||||
bool shower_alert;
|
||||
uint8_t shower_alert_trigger;
|
||||
uint8_t shower_alert_coldshot;
|
||||
bool syslog_enabled;
|
||||
int8_t syslog_level; // uuid::log::Level
|
||||
uint32_t syslog_mark_interval;
|
||||
String syslog_host;
|
||||
uint16_t syslog_port;
|
||||
bool trace_raw;
|
||||
uint8_t rx_gpio;
|
||||
uint8_t tx_gpio;
|
||||
uint8_t dallas_gpio;
|
||||
bool dallas_parasite;
|
||||
uint8_t led_gpio;
|
||||
bool hide_led;
|
||||
bool low_clock;
|
||||
bool telnet_enabled;
|
||||
bool notoken_api;
|
||||
bool readonly_mode;
|
||||
bool analog_enabled;
|
||||
uint8_t pbutton_gpio;
|
||||
uint8_t solar_maxflow;
|
||||
String board_profile;
|
||||
uint8_t bool_format;
|
||||
uint8_t bool_dashboard;
|
||||
uint8_t enum_format;
|
||||
int8_t weblog_level;
|
||||
uint8_t weblog_buffer;
|
||||
bool weblog_compact;
|
||||
bool fahrenheit;
|
||||
|
||||
uint8_t phy_type;
|
||||
int8_t eth_power; // -1 means disabled
|
||||
|
||||
Reference in New Issue
Block a user