higher cloud data rate
This commit is contained in:
@ -121,6 +121,7 @@ struct DisplayRedrawRateAccessor : public RefAccessorSaveSettings<int16_t> { int
|
|||||||
struct CanReceiveRateAccessor : public RefAccessorSaveSettings<int16_t> { int16_t &getRef() const override { return settings.boardcomputerHardware.timersSettings.canReceiveRate; } };
|
struct CanReceiveRateAccessor : public RefAccessorSaveSettings<int16_t> { int16_t &getRef() const override { return settings.boardcomputerHardware.timersSettings.canReceiveRate; } };
|
||||||
#endif
|
#endif
|
||||||
#ifdef FEATURE_CLOUD
|
#ifdef FEATURE_CLOUD
|
||||||
|
struct CloudCollectRateAccessor : public RefAccessorSaveSettings<int16_t> { int16_t &getRef() const override { return settings.boardcomputerHardware.timersSettings.cloudCollectRate; } };
|
||||||
struct CloudSendRateAccessor : public RefAccessorSaveSettings<int16_t> { int16_t &getRef() const override { return settings.boardcomputerHardware.timersSettings.cloudSendRate; } };
|
struct CloudSendRateAccessor : public RefAccessorSaveSettings<int16_t> { int16_t &getRef() const override { return settings.boardcomputerHardware.timersSettings.cloudSendRate; } };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
171
main/cloud.h
171
main/cloud.h
@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#define FEATURE_CLOUD
|
|
||||||
// esp-idf includes
|
// esp-idf includes
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
|
|
||||||
@ -19,6 +19,7 @@ espcpputils::websocket_client cloudClient;
|
|||||||
bool cloudStarted{};
|
bool cloudStarted{};
|
||||||
espchrono::millis_clock::time_point lastCreateTry;
|
espchrono::millis_clock::time_point lastCreateTry;
|
||||||
espchrono::millis_clock::time_point lastStartTry;
|
espchrono::millis_clock::time_point lastStartTry;
|
||||||
|
std::string cloudBuffer;
|
||||||
|
|
||||||
void createCloud();
|
void createCloud();
|
||||||
void destroyCloud();
|
void destroyCloud();
|
||||||
@ -41,7 +42,99 @@ void initCloud()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleCloud()
|
void cloudCollect()
|
||||||
|
{
|
||||||
|
if (!cloudClient)
|
||||||
|
{
|
||||||
|
cloudBuffer.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cloudStarted)
|
||||||
|
{
|
||||||
|
cloudBuffer.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cloudClient.is_connected())
|
||||||
|
{
|
||||||
|
cloudBuffer.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cloudBuffer.empty())
|
||||||
|
cloudBuffer = '[';
|
||||||
|
else
|
||||||
|
cloudBuffer += ',';
|
||||||
|
|
||||||
|
cloudBuffer += fmt::format("[{},{},{}",
|
||||||
|
std::chrono::milliseconds{espchrono::millis_clock::now().time_since_epoch()}.count(),
|
||||||
|
std::chrono::milliseconds{espchrono::utc_clock::now().time_since_epoch()}.count(),
|
||||||
|
heap_caps_get_free_size(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT));
|
||||||
|
if (wifi_stack::get_sta_status() == wifi_stack::WiFiStaStatus::CONNECTED)
|
||||||
|
{
|
||||||
|
if (const auto &result = wifi_stack::get_sta_ap_info(); result)
|
||||||
|
cloudBuffer += fmt::format(",{}", result->rssi);
|
||||||
|
else
|
||||||
|
cloudBuffer += ",null";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cloudBuffer += ",null";
|
||||||
|
|
||||||
|
if (raw_gas)
|
||||||
|
cloudBuffer += fmt::format(",{}", *raw_gas);
|
||||||
|
else
|
||||||
|
cloudBuffer += ",null";
|
||||||
|
|
||||||
|
if (raw_brems)
|
||||||
|
cloudBuffer += fmt::format(",{}", *raw_brems);
|
||||||
|
else
|
||||||
|
cloudBuffer += ",null";
|
||||||
|
|
||||||
|
if (gas)
|
||||||
|
cloudBuffer += fmt::format(",{:.1f}", *gas);
|
||||||
|
else
|
||||||
|
cloudBuffer += ",null";
|
||||||
|
|
||||||
|
if (brems)
|
||||||
|
cloudBuffer += fmt::format(",{:.1f}", *brems);
|
||||||
|
else
|
||||||
|
cloudBuffer += ",null";
|
||||||
|
|
||||||
|
constexpr const auto addController = [](const Controller &controller){
|
||||||
|
if (!controller.feedbackValid)
|
||||||
|
{
|
||||||
|
cloudBuffer += ",null";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cloudBuffer += fmt::format(",[{:.02f},{:.02f}",
|
||||||
|
fixBatVoltage(controller.feedback.batVoltage),
|
||||||
|
fixBoardTemp(controller.feedback.boardTemp));
|
||||||
|
|
||||||
|
constexpr const auto addMotor = [](const bobbycar::protocol::serial::MotorState &command,
|
||||||
|
const bobbycar::protocol::serial::MotorFeedback &feedback,
|
||||||
|
bool invert){
|
||||||
|
cloudBuffer += fmt::format(",[{},{:.2f},{:.2f},{}]",
|
||||||
|
command.pwm * (invert?-1:1),
|
||||||
|
convertToKmh(feedback.speed) * (invert?-1:1),
|
||||||
|
fixCurrent(feedback.dcLink),
|
||||||
|
feedback.error);
|
||||||
|
};
|
||||||
|
|
||||||
|
addMotor(controller.command.left, controller.feedback.left, controller.invertLeft);
|
||||||
|
addMotor(controller.command.right, controller.feedback.right, controller.invertRight);
|
||||||
|
|
||||||
|
cloudBuffer += ']';
|
||||||
|
};
|
||||||
|
|
||||||
|
addController(controllers.front);
|
||||||
|
addController(controllers.back);
|
||||||
|
|
||||||
|
cloudBuffer += "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
void cloudSend()
|
||||||
{
|
{
|
||||||
if (settings.cloudSettings.cloudEnabled &&
|
if (settings.cloudSettings.cloudEnabled &&
|
||||||
!stringSettings.cloudUrl.empty() &&
|
!stringSettings.cloudUrl.empty() &&
|
||||||
@ -72,84 +165,24 @@ void handleCloud()
|
|||||||
if (!cloudClient.is_connected())
|
if (!cloudClient.is_connected())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
static std::string msg;
|
if (cloudBuffer.empty())
|
||||||
msg = fmt::format("[[{},{},{}",
|
|
||||||
std::chrono::milliseconds{espchrono::millis_clock::now().time_since_epoch()}.count(),
|
|
||||||
std::chrono::milliseconds{espchrono::utc_clock::now().time_since_epoch()}.count(),
|
|
||||||
heap_caps_get_free_size(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT));
|
|
||||||
if (wifi_stack::get_sta_status() == wifi_stack::WiFiStaStatus::CONNECTED)
|
|
||||||
{
|
|
||||||
if (const auto &result = wifi_stack::get_sta_ap_info(); result)
|
|
||||||
msg += fmt::format(",{}", result->rssi);
|
|
||||||
else
|
|
||||||
msg += ",null";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
msg += ",null";
|
|
||||||
|
|
||||||
if (raw_gas)
|
|
||||||
msg += fmt::format(",{}", *raw_gas);
|
|
||||||
else
|
|
||||||
msg += ",null";
|
|
||||||
|
|
||||||
if (raw_brems)
|
|
||||||
msg += fmt::format(",{}", *raw_brems);
|
|
||||||
else
|
|
||||||
msg += ",null";
|
|
||||||
|
|
||||||
if (gas)
|
|
||||||
msg += fmt::format(",{:.1f}", *gas);
|
|
||||||
else
|
|
||||||
msg += ",null";
|
|
||||||
|
|
||||||
if (brems)
|
|
||||||
msg += fmt::format(",{:.1f}", *brems);
|
|
||||||
else
|
|
||||||
msg += ",null";
|
|
||||||
|
|
||||||
constexpr const auto addController = [](const Controller &controller){
|
|
||||||
if (!controller.feedbackValid)
|
|
||||||
{
|
|
||||||
msg += ",null";
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
msg += fmt::format(",[{:.02f},{:.02f}",
|
cloudBuffer += ']';
|
||||||
fixBatVoltage(controller.feedback.batVoltage),
|
|
||||||
fixBoardTemp(controller.feedback.boardTemp));
|
|
||||||
|
|
||||||
constexpr const auto addMotor = [](const bobbycar::protocol::serial::MotorState &command,
|
|
||||||
const bobbycar::protocol::serial::MotorFeedback &feedback,
|
|
||||||
bool invert){
|
|
||||||
msg += fmt::format(",[{},{:.2f},{:.2f},{}]",
|
|
||||||
command.pwm * (invert?-1:1),
|
|
||||||
convertToKmh(feedback.speed) * (invert?-1:1),
|
|
||||||
fixCurrent(feedback.dcLink),
|
|
||||||
feedback.error);
|
|
||||||
};
|
|
||||||
|
|
||||||
addMotor(controller.command.left, controller.feedback.left, controller.invertLeft);
|
|
||||||
addMotor(controller.command.right, controller.feedback.right, controller.invertRight);
|
|
||||||
|
|
||||||
msg += ']';
|
|
||||||
};
|
|
||||||
|
|
||||||
addController(controllers.front);
|
|
||||||
addController(controllers.back);
|
|
||||||
|
|
||||||
msg += "]]";
|
|
||||||
|
|
||||||
const auto timeout = std::chrono::ceil<espcpputils::ticks>(espchrono::milliseconds32{settings.cloudSettings.cloudTransmitTimeout}).count();
|
const auto timeout = std::chrono::ceil<espcpputils::ticks>(espchrono::milliseconds32{settings.cloudSettings.cloudTransmitTimeout}).count();
|
||||||
const auto written = cloudClient.send_text(msg, timeout);
|
const auto written = cloudClient.send_text(cloudBuffer, timeout);
|
||||||
|
|
||||||
if (written < 0)
|
if (written < 0)
|
||||||
{
|
{
|
||||||
ESP_LOGE("BOBBY", "cloudClient.send_text() failed with %i", written);
|
ESP_LOGE("BOBBY", "cloudClient.send_text() failed with %i", written);
|
||||||
}
|
}
|
||||||
else if (written != msg.size())
|
else if (written != cloudBuffer.size())
|
||||||
{
|
{
|
||||||
ESP_LOGE("BOBBY", "websocket sent size mismatch, sent=%i, expected=%i", written, msg.size());
|
ESP_LOGE("BOBBY", "websocket sent size mismatch, sent=%i, expected=%i", written, cloudBuffer.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cloudBuffer.clear();
|
||||||
}
|
}
|
||||||
else if (cloudClient)
|
else if (cloudClient)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// 3rdparty lib includes
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
// local includes
|
// local includes
|
||||||
#include "menudisplay.h"
|
#include "menudisplay.h"
|
||||||
#include "menuitem.h"
|
#include "menuitem.h"
|
||||||
@ -11,6 +14,8 @@
|
|||||||
#include "accessors/settingsaccessors.h"
|
#include "accessors/settingsaccessors.h"
|
||||||
#include "icons/back.h"
|
#include "icons/back.h"
|
||||||
#include "texts.h"
|
#include "texts.h"
|
||||||
|
#include "accessors/settingsaccessors.h"
|
||||||
|
#include "cloud.h"
|
||||||
|
|
||||||
// forward declares
|
// forward declares
|
||||||
namespace {
|
namespace {
|
||||||
@ -26,6 +31,30 @@ using CloudTransmitTimeoutChangeScreen = makeComponent<
|
|||||||
BackActionInterface<SwitchScreenAction<CloudSettingsMenu>>,
|
BackActionInterface<SwitchScreenAction<CloudSettingsMenu>>,
|
||||||
SwitchScreenAction<CloudSettingsMenu>
|
SwitchScreenAction<CloudSettingsMenu>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
struct CloudBufferLengthText : public virtual TextInterface {
|
||||||
|
public:
|
||||||
|
std::string text() const override
|
||||||
|
{
|
||||||
|
return fmt::format("buffer: {}", cloudBuffer.size());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using CloudCollectRateChangeDisplay = makeComponent<
|
||||||
|
ChangeValueDisplay<int16_t>,
|
||||||
|
StaticText<TEXT_CLOUDCOLLECTRATE>,
|
||||||
|
CloudCollectRateAccessor,
|
||||||
|
BackActionInterface<SwitchScreenAction<CloudSettingsMenu>>,
|
||||||
|
SwitchScreenAction<CloudSettingsMenu>
|
||||||
|
>;
|
||||||
|
|
||||||
|
using CloudSendRateChangeDisplay = makeComponent<
|
||||||
|
ChangeValueDisplay<int16_t>,
|
||||||
|
StaticText<TEXT_CLOUDSENDRATE>,
|
||||||
|
CloudSendRateAccessor,
|
||||||
|
BackActionInterface<SwitchScreenAction<CloudSettingsMenu>>,
|
||||||
|
SwitchScreenAction<CloudSettingsMenu>
|
||||||
|
>;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -43,6 +72,9 @@ public:
|
|||||||
constructMenuItem<makeComponent<MenuItem, CloudCreatedText, DisabledColor, DummyAction>>();
|
constructMenuItem<makeComponent<MenuItem, CloudCreatedText, DisabledColor, DummyAction>>();
|
||||||
constructMenuItem<makeComponent<MenuItem, CloudStartedText, DisabledColor, DummyAction>>();
|
constructMenuItem<makeComponent<MenuItem, CloudStartedText, DisabledColor, DummyAction>>();
|
||||||
constructMenuItem<makeComponent<MenuItem, CloudConnectedText, DisabledColor, DummyAction>>();
|
constructMenuItem<makeComponent<MenuItem, CloudConnectedText, DisabledColor, DummyAction>>();
|
||||||
|
constructMenuItem<makeComponent<MenuItem, CloudBufferLengthText, DisabledColor, DummyAction>>();
|
||||||
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_CLOUDCOLLECTRATE>, SwitchScreenAction<CloudCollectRateChangeDisplay>>>();
|
||||||
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_CLOUDSENDRATE>, SwitchScreenAction<CloudSendRateChangeDisplay>>>();
|
||||||
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<SettingsMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<SettingsMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -69,16 +69,6 @@ using CanReceiveRateChangeDisplay = makeComponent<
|
|||||||
>;
|
>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef FEATURE_CLOUD
|
|
||||||
using CloudSendRateChangeDisplay = makeComponent<
|
|
||||||
ChangeValueDisplay<int16_t>,
|
|
||||||
StaticText<TEXT_CLOUDSENDRATE>,
|
|
||||||
CloudSendRateAccessor,
|
|
||||||
BackActionInterface<SwitchScreenAction<TimersMenu>>,
|
|
||||||
SwitchScreenAction<TimersMenu>
|
|
||||||
>;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class TimersMenu :
|
class TimersMenu :
|
||||||
public MenuDisplay,
|
public MenuDisplay,
|
||||||
public StaticText<TEXT_TIMERS>,
|
public StaticText<TEXT_TIMERS>,
|
||||||
@ -94,9 +84,6 @@ public:
|
|||||||
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_DISPLAYREDRAWRATE>, SwitchScreenAction<DisplayRedrawRateChangeDisplay>>>();
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_DISPLAYREDRAWRATE>, SwitchScreenAction<DisplayRedrawRateChangeDisplay>>>();
|
||||||
#ifdef FEATURE_CAN
|
#ifdef FEATURE_CAN
|
||||||
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_CANRECEIVERATE>, SwitchScreenAction<CanReceiveRateChangeDisplay>>>();
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_CANRECEIVERATE>, SwitchScreenAction<CanReceiveRateChangeDisplay>>>();
|
||||||
#endif
|
|
||||||
#ifdef FEATURE_CLOUD
|
|
||||||
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_CLOUDSENDRATE>, SwitchScreenAction<CloudSendRateChangeDisplay>>>();
|
|
||||||
#endif
|
#endif
|
||||||
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<BoardcomputerHardwareSettingsMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<BoardcomputerHardwareSettingsMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,8 @@ std::optional<espchrono::millis_clock::time_point> lastCanParse;
|
|||||||
std::optional<espchrono::millis_clock::time_point> lastBleUpdate;
|
std::optional<espchrono::millis_clock::time_point> lastBleUpdate;
|
||||||
#endif
|
#endif
|
||||||
#ifdef FEATURE_CLOUD
|
#ifdef FEATURE_CLOUD
|
||||||
std::optional<espchrono::millis_clock::time_point> lastCloudUpdate;
|
std::optional<espchrono::millis_clock::time_point> lastCloudCollect;
|
||||||
|
std::optional<espchrono::millis_clock::time_point> lastCloudSend;
|
||||||
#endif
|
#endif
|
||||||
#ifdef FEATURE_NTP
|
#ifdef FEATURE_NTP
|
||||||
std::optional<espchrono::millis_clock::time_point> lastNtpUpdate;
|
std::optional<espchrono::millis_clock::time_point> lastNtpUpdate;
|
||||||
@ -459,11 +460,18 @@ extern "C" void app_main()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef FEATURE_CLOUD
|
#ifdef FEATURE_CLOUD
|
||||||
if (!lastCloudUpdate || now - *lastCloudUpdate >= 1000ms/settings.boardcomputerHardware.timersSettings.cloudSendRate)
|
if (!lastCloudCollect || now - *lastCloudCollect >= std::chrono::milliseconds{settings.boardcomputerHardware.timersSettings.cloudCollectRate})
|
||||||
{
|
{
|
||||||
handleCloud();
|
cloudCollect();
|
||||||
|
|
||||||
lastCloudUpdate = now;
|
lastCloudCollect = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lastCloudSend || now - *lastCloudSend >= 1000ms/settings.boardcomputerHardware.timersSettings.cloudSendRate)
|
||||||
|
{
|
||||||
|
cloudSend();
|
||||||
|
|
||||||
|
lastCloudSend = now;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -142,6 +142,7 @@ constexpr Settings::BoardcomputerHardware::TimersSettings defaultTimersSettings
|
|||||||
.canReceiveRate = 100,
|
.canReceiveRate = 100,
|
||||||
#endif
|
#endif
|
||||||
#ifdef FEATURE_CLOUD
|
#ifdef FEATURE_CLOUD
|
||||||
|
.cloudCollectRate = 100,
|
||||||
.cloudSendRate = 1,
|
.cloudSendRate = 1,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
@ -110,6 +110,7 @@ struct Settings
|
|||||||
int16_t canReceiveRate;
|
int16_t canReceiveRate;
|
||||||
#endif
|
#endif
|
||||||
#ifdef FEATURE_CLOUD
|
#ifdef FEATURE_CLOUD
|
||||||
|
int16_t cloudCollectRate;
|
||||||
int16_t cloudSendRate;
|
int16_t cloudSendRate;
|
||||||
#endif
|
#endif
|
||||||
} timersSettings;
|
} timersSettings;
|
||||||
@ -237,6 +238,7 @@ void Settings::executeForEveryCommonSetting(T &&callable)
|
|||||||
callable("canReceiveRate", boardcomputerHardware.timersSettings.canReceiveRate);
|
callable("canReceiveRate", boardcomputerHardware.timersSettings.canReceiveRate);
|
||||||
#endif
|
#endif
|
||||||
#ifdef FEATURE_CLOUD
|
#ifdef FEATURE_CLOUD
|
||||||
|
callable("cloudCollectRat", boardcomputerHardware.timersSettings.cloudCollectRate);
|
||||||
callable("cloudSendRate", boardcomputerHardware.timersSettings.cloudSendRate);
|
callable("cloudSendRate", boardcomputerHardware.timersSettings.cloudSendRate);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -45,6 +45,8 @@ constexpr char TEXT_BLEENABLED[] = "BLE enabled";
|
|||||||
constexpr char TEXT_CLOUDSETTINGS[] = "Cloud settings";
|
constexpr char TEXT_CLOUDSETTINGS[] = "Cloud settings";
|
||||||
constexpr char TEXT_CLOUDENABLED[] = "Cloud enabled";
|
constexpr char TEXT_CLOUDENABLED[] = "Cloud enabled";
|
||||||
constexpr char TEXT_CLOUDTRANSMITTIMEOUT[] = "Transmit timeout";
|
constexpr char TEXT_CLOUDTRANSMITTIMEOUT[] = "Transmit timeout";
|
||||||
|
constexpr char TEXT_CLOUDCOLLECTRATE[] = "Cloud collect rate";
|
||||||
|
constexpr char TEXT_CLOUDSENDRATE[] = "Cloud send rate";
|
||||||
//constexpr char TEXT_BACK[] = "Back";
|
//constexpr char TEXT_BACK[] = "Back";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -344,9 +346,6 @@ constexpr char TEXT_DISPLAYREDRAWRATE[] = "Display redraw rate";
|
|||||||
#ifdef FEATURE_CAN
|
#ifdef FEATURE_CAN
|
||||||
constexpr char TEXT_CANRECEIVERATE[] = "CAN receive rate";
|
constexpr char TEXT_CANRECEIVERATE[] = "CAN receive rate";
|
||||||
#endif
|
#endif
|
||||||
#ifdef FEATURE_CLOUD
|
|
||||||
constexpr char TEXT_CLOUDSENDRATE[] = "Cloud send rate";
|
|
||||||
#endif
|
|
||||||
//constexpr char TEXT_BACK[] = "Back";
|
//constexpr char TEXT_BACK[] = "Back";
|
||||||
|
|
||||||
//TimeSettingsMenu
|
//TimeSettingsMenu
|
||||||
|
Reference in New Issue
Block a user