Buildserver is now selectable
This commit is contained in:
@@ -125,6 +125,8 @@ set(headers
|
|||||||
displays/menus/mosfetsmenu.h
|
displays/menus/mosfetsmenu.h
|
||||||
displays/menus/motorfeedbackdebugmenu.h
|
displays/menus/motorfeedbackdebugmenu.h
|
||||||
displays/menus/motorstatedebugmenu.h
|
displays/menus/motorstatedebugmenu.h
|
||||||
|
displays/menus/otamenu.h
|
||||||
|
displays/menus/selectotabuildmenu.h
|
||||||
displays/menus/presetsmenu.h
|
displays/menus/presetsmenu.h
|
||||||
displays/menus/profilesmenu.h
|
displays/menus/profilesmenu.h
|
||||||
displays/menus/selectbatterytypemenu.h
|
displays/menus/selectbatterytypemenu.h
|
||||||
|
@@ -1,5 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <cpputils.h>
|
||||||
|
#include <cleanuphelper.h>
|
||||||
|
|
||||||
|
// 3rdparty lib includes
|
||||||
|
#include <asynchttprequest.h>
|
||||||
|
#include <delayedconstruction.h>
|
||||||
|
|
||||||
|
// local includes
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "fmt/core.h"
|
#include "fmt/core.h"
|
||||||
@@ -7,39 +16,169 @@
|
|||||||
// esp-idf
|
// esp-idf
|
||||||
#include "esp_http_client.h"
|
#include "esp_http_client.h"
|
||||||
|
|
||||||
/*
|
|
||||||
* ToDo:
|
|
||||||
* - get_ota_decriptor_json_string => returns std::string, takes std::string url
|
|
||||||
*/
|
|
||||||
namespace {
|
namespace {
|
||||||
|
void buildMenuFromJson(std::string json);
|
||||||
|
void buildMenuRequestError(std::string error);
|
||||||
|
static std::string url_for_hashes = "";
|
||||||
|
static std::string url_for_latest = "";
|
||||||
|
static std::array<std::string, 10> availableVersions = {};
|
||||||
|
bool request_running = false;
|
||||||
|
uint16_t request_failed = false;
|
||||||
|
bool parsing_finished = false;
|
||||||
|
cpputils::DelayedConstruction<AsyncHttpRequest> request;
|
||||||
|
|
||||||
std::string ota_descriptor_json = "";
|
std::string get_ota_url_from_index(uint16_t index)
|
||||||
|
|
||||||
std::string get_ota_url_from_index(uint16_t index)
|
|
||||||
{
|
|
||||||
if (index < stringSettings.otaServers.size())
|
|
||||||
{
|
{
|
||||||
auto otaServer = stringSettings.otaServers[index];
|
if (index < stringSettings.otaServers.size())
|
||||||
if (!otaServer.url.empty())
|
|
||||||
{
|
{
|
||||||
return otaServer.url;
|
auto otaServer = stringSettings.otaServers[index];
|
||||||
|
if (!otaServer.url.empty())
|
||||||
|
{
|
||||||
|
return otaServer.url;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGE("BOBBY", "Cannot get OTA url: otaServer.url is empty");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ESP_LOGE("BOBBY", "Cannot get OTA url: otaServer.url is empty");
|
ESP_LOGE("BOBBY", "Cannot get OTA url: Invalid Index");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
uint16_t count_available_buildserver()
|
||||||
{
|
{
|
||||||
ESP_LOGE("BOBBY", "Cannot get OTA url: Invalid Index");
|
uint16_t count = 0;
|
||||||
return "";
|
for (const auto &otaServer : stringSettings.otaServers) {
|
||||||
|
if (!otaServer.url.empty()) count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_hash_url(std::string hash)
|
||||||
|
{
|
||||||
|
return fmt::format(url_for_hashes, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_latest_url()
|
||||||
|
{
|
||||||
|
return url_for_latest;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string fix_url(std::string url)
|
||||||
|
{
|
||||||
|
std::string fixed_url = url;
|
||||||
|
if (fixed_url.find("http") == std::string::npos)
|
||||||
|
{
|
||||||
|
fixed_url = fmt::format("http://{}", fixed_url);
|
||||||
|
}
|
||||||
|
return fixed_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_descriptor_url(std::string base_url)
|
||||||
|
{
|
||||||
|
std::string url = fix_url(base_url);
|
||||||
|
return fmt::format("{}/otaDescriptor?username={}", url, OTA_USERNAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_response_into_variables(std::string response)
|
||||||
|
{
|
||||||
|
StaticJsonDocument<512> doc;
|
||||||
|
|
||||||
|
if (const auto error = deserializeJson(doc, response))
|
||||||
|
{
|
||||||
|
ESP_LOGE("BOBBY", "Error parsing server-response => %s", response.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject availableVersionsObject = doc["availableVersions"];
|
||||||
|
for (JsonPair kv : availableVersionsObject)
|
||||||
|
{
|
||||||
|
static auto index = 0;
|
||||||
|
auto hash = kv.key().c_str();
|
||||||
|
if (index > availableVersions.size())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
availableVersions.at(index) = hash;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
url_for_latest = doc["latest"].as<std::string>();
|
||||||
|
url_for_hashes = doc["url"].as<std::string>();
|
||||||
|
parsing_finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_request()
|
||||||
|
{
|
||||||
|
if (!request.constructed())
|
||||||
|
request.construct("ota-descriptor-request", espcpputils::CoreAffinity::Core0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_descriptor_request(std::string server_base_url)
|
||||||
|
{
|
||||||
|
if (!request.constructed())
|
||||||
|
{
|
||||||
|
ESP_LOGW("BOBBY", "request is im oarsch");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto url = get_descriptor_url(server_base_url);
|
||||||
|
ESP_LOGD("BOBBY", "requesting data...");
|
||||||
|
if (const auto result = request->start(url); !result)
|
||||||
|
{
|
||||||
|
ESP_LOGW("BOBBY", "request start failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
request_running = true;
|
||||||
|
request_failed = false;
|
||||||
|
url_for_latest.clear();
|
||||||
|
url_for_hashes.clear();
|
||||||
|
availableVersions = {};
|
||||||
|
parsing_finished = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_descriptor_request()
|
||||||
|
{
|
||||||
|
if (!request.constructed())
|
||||||
|
{
|
||||||
|
ESP_LOGW("BOBBY", "request is im oarsch");
|
||||||
|
request_running = false;
|
||||||
|
request_failed = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!request->finished())
|
||||||
|
{
|
||||||
|
// ESP_LOGW("BOBBY", "Request has not finished yet.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto helper = cpputils::makeCleanupHelper([](){ request->clearFinished(); });
|
||||||
|
const std::string content = std::move(request->takeBuffer());
|
||||||
|
|
||||||
|
if (const auto result = request->result(); !result)
|
||||||
|
{
|
||||||
|
ESP_LOGW("BOBBY", "request failed: %.*s", result.error().size(), result.error().data());
|
||||||
|
std::string failed_str = result.error().data();
|
||||||
|
auto statuscode = failed_str.substr(failed_str.length() - 3);
|
||||||
|
request_running = false;
|
||||||
|
request_failed = std::stoi(statuscode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = request->result();
|
||||||
|
ESP_LOGW("BOBBY", "Request finished: %s", content.c_str());
|
||||||
|
parse_response_into_variables(content);
|
||||||
|
request_running = false;
|
||||||
|
request_failed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_request_running()
|
||||||
|
{
|
||||||
|
return request_running;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_ota_descriptor(std::string url)
|
|
||||||
{
|
|
||||||
auto descriptorUrl = fmt::format("{}/otaDescriptor", url);
|
|
||||||
// Make GET request to descriptorUrl and store json somewhere to be decoded when needed, for example in ota_descriptor_json (buildserver.h:16)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -46,7 +46,7 @@ class Lockscreen;
|
|||||||
class MosfetsMenu;
|
class MosfetsMenu;
|
||||||
class DemosMenu;
|
class DemosMenu;
|
||||||
class GarageDisplay;
|
class GarageDisplay;
|
||||||
class UpdateDisplay;
|
class OtaMenu;
|
||||||
class PoweroffDisplay;
|
class PoweroffDisplay;
|
||||||
class DebugMenu;
|
class DebugMenu;
|
||||||
class BatteryMenu;
|
class BatteryMenu;
|
||||||
@@ -97,7 +97,7 @@ public:
|
|||||||
if (SHOWITEM) { constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_GARAGE>, SwitchScreenAction<GarageDisplay>>>(); }
|
if (SHOWITEM) { constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_GARAGE>, SwitchScreenAction<GarageDisplay>>>(); }
|
||||||
#endif
|
#endif
|
||||||
#ifdef FEATURE_OTA
|
#ifdef FEATURE_OTA
|
||||||
if (SHOWITEM) { constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_UPDATE>, SwitchScreenAction<UpdateDisplay>, StaticMenuItemIcon<&bobbyicons::update>>>(); }
|
if (SHOWITEM) { constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_UPDATE>, SwitchScreenAction<OtaMenu>, StaticMenuItemIcon<&bobbyicons::update>>>(); }
|
||||||
#endif
|
#endif
|
||||||
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_POWEROFF>, SwitchScreenAction<PoweroffDisplay>, StaticMenuItemIcon<&bobbyicons::poweroff>>>();
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_POWEROFF>, SwitchScreenAction<PoweroffDisplay>, StaticMenuItemIcon<&bobbyicons::poweroff>>>();
|
||||||
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_REBOOT>, RebootAction, StaticMenuItemIcon<&bobbyicons::reboot>>>();
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_REBOOT>, RebootAction, StaticMenuItemIcon<&bobbyicons::reboot>>>();
|
||||||
|
39
main/displays/menus/otamenu.h
Normal file
39
main/displays/menus/otamenu.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// local includes
|
||||||
|
#include "menudisplay.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "actions/dummyaction.h"
|
||||||
|
#include "icons/back.h"
|
||||||
|
#include "icons/update.h"
|
||||||
|
#include "icons/presets.h"
|
||||||
|
#include "texts.h"
|
||||||
|
|
||||||
|
|
||||||
|
// forward declares
|
||||||
|
namespace {
|
||||||
|
class MainMenu;
|
||||||
|
class UpdateDisplay;
|
||||||
|
class SelectBuildMenu;
|
||||||
|
class SelectBuildServerMenu;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
using namespace espgui;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class OtaMenu :
|
||||||
|
public MenuDisplay,
|
||||||
|
public StaticText<TEXT_UPDATE>,
|
||||||
|
public BackActionInterface<SwitchScreenAction<MainMenu>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OtaMenu()
|
||||||
|
{
|
||||||
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_SELECTBUILD>, SwitchScreenAction<SelectBuildMenu>, StaticMenuItemIcon<&bobbyicons::presets>>>();
|
||||||
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_UPDATENOW>, SwitchScreenAction<UpdateDisplay>, StaticMenuItemIcon<&bobbyicons::update>>>();
|
||||||
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_SELECTBUILDSERVERMENU>, SwitchScreenAction<SelectBuildServerMenu>>>();
|
||||||
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<MainMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace
|
147
main/displays/menus/selectotabuildmenu.h
Normal file
147
main/displays/menus/selectotabuildmenu.h
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <espwifistack.h>
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "fmt/core.h"
|
||||||
|
|
||||||
|
// local includes
|
||||||
|
#include "menudisplay.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "actions/dummyaction.h"
|
||||||
|
#include "icons/back.h"
|
||||||
|
#include "icons/update.h"
|
||||||
|
#include "texts.h"
|
||||||
|
|
||||||
|
#include "buildserver.h"
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
|
#define MESSAGE(text) constructMenuItem<makeComponent<MenuItem, StaticText<text>, DummyAction>>()
|
||||||
|
|
||||||
|
// forward declares
|
||||||
|
namespace {
|
||||||
|
class OtaMenu;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
using namespace espgui;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// ToDo: if (request_failed) => MESSAGE("An error occurred")
|
||||||
|
|
||||||
|
class VersionMenuItem : public MenuItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string text() const override { return m_hash; }
|
||||||
|
void setHash(std::string &&hash) { m_hash = std::move(hash); }
|
||||||
|
void setHash(const std::string &hash) { m_hash = hash; }
|
||||||
|
void setUrl(std::string &&url) { m_url = std::move(url); }
|
||||||
|
void setUrl(const std::string &url) { m_url = url; }
|
||||||
|
|
||||||
|
void triggered() override
|
||||||
|
{
|
||||||
|
stringSettings.otaUrl = m_url;
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::string m_url;
|
||||||
|
std::string m_hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SelectBuildMenu :
|
||||||
|
public MenuDisplay,
|
||||||
|
public StaticText<TEXT_SELECTBUILD>,
|
||||||
|
public BackActionInterface<SwitchScreenAction<OtaMenu>>
|
||||||
|
{
|
||||||
|
using Base = MenuDisplay;
|
||||||
|
public:
|
||||||
|
void update() override;
|
||||||
|
void buildMenuFromJson();
|
||||||
|
void buildMenuRequestError(std::string error);
|
||||||
|
SelectBuildMenu()
|
||||||
|
{
|
||||||
|
if (count_available_buildserver() < 1)
|
||||||
|
{
|
||||||
|
MESSAGE(TEXT_OTA_NOBUILDSERVERAVAILABLE);
|
||||||
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<OtaMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
|
||||||
|
}
|
||||||
|
else if (stringSettings.otaServerUrl.empty())
|
||||||
|
{
|
||||||
|
MESSAGE(TEXT_OTA_NOBUILDSERVERSELECTED);
|
||||||
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<OtaMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto staStatus = wifi_stack::get_sta_status();
|
||||||
|
if (staStatus != wifi_stack::WiFiStaStatus::CONNECTED)
|
||||||
|
{
|
||||||
|
MESSAGE(TEXT_OTA_NOCONNECTION);
|
||||||
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<OtaMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string serverUrl = stringSettings.otaServerUrl;
|
||||||
|
if (serverUrl.substr(serverUrl.length() - 4) == ".bin")
|
||||||
|
{
|
||||||
|
auto &menuitem = constructMenuItem<VersionMenuItem>();
|
||||||
|
std::size_t last_slash_index = serverUrl.find_last_of("/");
|
||||||
|
auto filename = serverUrl.substr(last_slash_index+1);
|
||||||
|
auto hash = filename.substr(0, filename.length() - 4);
|
||||||
|
menuitem.setHash(hash);
|
||||||
|
menuitem.setUrl(fix_url(serverUrl));
|
||||||
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<OtaMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setup_request();
|
||||||
|
start_descriptor_request(serverUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void SelectBuildMenu::update()
|
||||||
|
{
|
||||||
|
if(get_request_running())
|
||||||
|
{
|
||||||
|
check_descriptor_request();
|
||||||
|
if (request_failed)
|
||||||
|
{
|
||||||
|
this->buildMenuRequestError(fmt::format("Error: {}", request_failed));
|
||||||
|
request_failed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsing_finished)
|
||||||
|
{
|
||||||
|
parsing_finished = false;
|
||||||
|
if (!availableVersions.empty())
|
||||||
|
{
|
||||||
|
this->buildMenuFromJson();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Base::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SelectBuildMenu::buildMenuFromJson()
|
||||||
|
{
|
||||||
|
auto &latest = constructMenuItem<VersionMenuItem>();
|
||||||
|
latest.setHash("latest");
|
||||||
|
latest.setUrl(url_for_latest);
|
||||||
|
|
||||||
|
for (const std::string &hash : availableVersions)
|
||||||
|
{
|
||||||
|
auto &menuitem = constructMenuItem<VersionMenuItem>();
|
||||||
|
menuitem.setHash(hash);
|
||||||
|
menuitem.setUrl(fmt::format(url_for_hashes, hash));
|
||||||
|
}
|
||||||
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<OtaMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SelectBuildMenu::buildMenuRequestError(std::string error)
|
||||||
|
{
|
||||||
|
auto &item = constructMenuItem<makeComponent<MenuItem, ChangeableText, DummyAction>>();
|
||||||
|
item.setTitle(error);
|
||||||
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<OtaMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
|
||||||
|
}
|
||||||
|
} // namespace
|
@@ -23,12 +23,12 @@
|
|||||||
#include "ota.h"
|
#include "ota.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class MainMenu;
|
class OtaMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
#ifdef FEATURE_OTA
|
#ifdef FEATURE_OTA
|
||||||
class UpdateDisplay : public Display, public BackActionInterface<SwitchScreenAction<MainMenu>>
|
class UpdateDisplay : public Display, public BackActionInterface<SwitchScreenAction<OtaMenu>>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void start() override;
|
void start() override;
|
||||||
|
@@ -106,6 +106,8 @@ using namespace std::chrono_literals;
|
|||||||
#include "displays/statusdisplay.h"
|
#include "displays/statusdisplay.h"
|
||||||
#ifdef FEATURE_OTA
|
#ifdef FEATURE_OTA
|
||||||
#include "displays/updatedisplay.h"
|
#include "displays/updatedisplay.h"
|
||||||
|
#include "displays/menus/otamenu.h"
|
||||||
|
#include "displays/menus/selectotabuildmenu.h"
|
||||||
#endif
|
#endif
|
||||||
#include "screens.h"
|
#include "screens.h"
|
||||||
#include "dpad.h"
|
#include "dpad.h"
|
||||||
|
@@ -475,6 +475,14 @@ constexpr char TEXT_CRASH_DIVZERO[] = "42 / 0";
|
|||||||
constexpr char TEXT_SELECTBUILDSERVERMENU[] = "Select Buildserver";
|
constexpr char TEXT_SELECTBUILDSERVERMENU[] = "Select Buildserver";
|
||||||
constexpr char TEXT_NOBUILDSERVERCONFIGURED[] = "Not configured";
|
constexpr char TEXT_NOBUILDSERVERCONFIGURED[] = "Not configured";
|
||||||
|
|
||||||
|
//Otamenu
|
||||||
|
constexpr char TEXT_UPDATENOW[] = "Update now";
|
||||||
|
constexpr char TEXT_SELECTBUILD[] = "Select build";
|
||||||
|
constexpr char TEXT_OTA_NOBUILDSERVERAVAILABLE[] = "E:No server saved.";
|
||||||
|
constexpr char TEXT_OTA_NOBUILDSERVERSELECTED[] = "E:No server selected.";
|
||||||
|
constexpr char TEXT_OTA_NOCONNECTION[] = "E:No internet.";
|
||||||
|
constexpr char TEXT_OTA_WAITFORRESPONSE[] = "Wait for response...";
|
||||||
|
|
||||||
#ifdef FEATURE_CAN
|
#ifdef FEATURE_CAN
|
||||||
constexpr char TEXT_POWERSUPPLY[] = "Powersupply";
|
constexpr char TEXT_POWERSUPPLY[] = "Powersupply";
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user