Merge branch 'master' into peter_screen_improvements
This commit is contained in:
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -19,9 +19,6 @@
|
|||||||
[submodule "components/ArduinoJson"]
|
[submodule "components/ArduinoJson"]
|
||||||
path = components/ArduinoJson
|
path = components/ArduinoJson
|
||||||
url = ../../0xFEEDC0DE64/ArduinoJson.git
|
url = ../../0xFEEDC0DE64/ArduinoJson.git
|
||||||
[submodule "components/TFT_eSPI"]
|
|
||||||
path = components/TFT_eSPI
|
|
||||||
url = ../../0xFEEDC0DE64/TFT_eSPI.git
|
|
||||||
[submodule "components/arduino-esp32"]
|
[submodule "components/arduino-esp32"]
|
||||||
path = components/arduino-esp32
|
path = components/arduino-esp32
|
||||||
url = ../../0xFEEDC0DE64/arduino-esp32.git
|
url = ../../0xFEEDC0DE64/arduino-esp32.git
|
||||||
@@ -67,3 +64,6 @@
|
|||||||
[submodule "esp-protocols"]
|
[submodule "esp-protocols"]
|
||||||
path = esp-protocols
|
path = esp-protocols
|
||||||
url = ../../0xFEEDC0DE64/esp-protocols.git
|
url = ../../0xFEEDC0DE64/esp-protocols.git
|
||||||
|
[submodule "components/TFT_eSPI"]
|
||||||
|
path = components/TFT_eSPI
|
||||||
|
url = ../../0xFEEDC0DE64/TFT_eSPI.git
|
||||||
|
@@ -46,7 +46,7 @@ set(BOBBYCAR_BUILDFLAGS
|
|||||||
-DFEATURE_LEDBACKLIGHT
|
-DFEATURE_LEDBACKLIGHT
|
||||||
|
|
||||||
-DLEDBACKLIGHT_INVERTED
|
-DLEDBACKLIGHT_INVERTED
|
||||||
# -DLEDSTRIP_WRONG_DIRECTION
|
-DLEDSTRIP_WRONG_DIRECTION
|
||||||
-DHEAP_LRGST_CRASH_TEXT_FIX
|
-DHEAP_LRGST_CRASH_TEXT_FIX
|
||||||
|
|
||||||
# Default values
|
# Default values
|
||||||
|
89
config_testdevice.cmake
Normal file
89
config_testdevice.cmake
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
set(BOBBY_APP_NAME testdevice)
|
||||||
|
|
||||||
|
add_definitions(
|
||||||
|
-DUSER_SETUP_LOADED=1
|
||||||
|
-DLOAD_GLCD=1
|
||||||
|
-DLOAD_FONT2=1
|
||||||
|
-DLOAD_FONT4=1
|
||||||
|
-DLOAD_FONT7=1
|
||||||
|
-DILI9341_DRIVER=1
|
||||||
|
-DTFT_MOSI=13
|
||||||
|
-DTFT_SCLK=15
|
||||||
|
-DTFT_CS=14
|
||||||
|
-DTFT_DC=12
|
||||||
|
-DTFT_RST=2
|
||||||
|
-DSPI_FREQUENCY=40000000
|
||||||
|
)
|
||||||
|
|
||||||
|
set(BOBBYCAR_BUILDFLAGS
|
||||||
|
# Pins
|
||||||
|
-DPINS_GAS=34
|
||||||
|
-DPINS_BREMS=35
|
||||||
|
# -DFEATURE_JOYSTICK
|
||||||
|
# -DPINS_RX1=4
|
||||||
|
# -DPINS_TX1=5
|
||||||
|
# -DPINS_RX2=22
|
||||||
|
# -DPINS_TX2=23
|
||||||
|
# -DPINS_MOSFET0=18
|
||||||
|
# -DPINS_MOSFET1=19
|
||||||
|
# -DPINS_MOSFET2=21
|
||||||
|
# -DPINS_GAMETRAKX=34
|
||||||
|
# -DPINS_GAMETRAKY=39
|
||||||
|
# -DPINS_GAMETRAKDIST=36
|
||||||
|
-DPINS_LEDBACKLIGHT=23
|
||||||
|
-DPINS_LEDSTRIP=33
|
||||||
|
|
||||||
|
# Config flags
|
||||||
|
-DFEATURE_ADC_IN
|
||||||
|
-DFEATURE_CAN
|
||||||
|
# -DFEATURE_SERIAL
|
||||||
|
# -DFEATURE_MOSFETS
|
||||||
|
# -DFEATURE_BLUETOOTH
|
||||||
|
# -DFEATURE_BMS
|
||||||
|
# -DFEATURE_GAMETRAK
|
||||||
|
# -DFEATURE_POWERSUPPLY
|
||||||
|
-DFEATURE_LEDBACKLIGHT
|
||||||
|
|
||||||
|
-DLEDBACKLIGHT_INVERTED
|
||||||
|
# -DLEDSTRIP_WRONG_DIRECTION
|
||||||
|
-DHEAP_LRGST_CRASH_TEXT_FIX
|
||||||
|
|
||||||
|
# Default values
|
||||||
|
-DDEFAULT_SWAPSCREENBYTES=false
|
||||||
|
-DDEFAULT_INVERTFRONTLEFT=false
|
||||||
|
-DDEFAULT_INVERTFRONTRIGHT=true
|
||||||
|
-DDEFAULT_INVERTBACKLEFT=false
|
||||||
|
-DDEFAULT_INVERTBACKRIGHT=true
|
||||||
|
-DDEFAULT_WHEELDIAMETER=200
|
||||||
|
-DDEFAULT_IMOTMAX=28
|
||||||
|
-DDEFAULT_IDCMAX=30
|
||||||
|
-DDEFAULT_NMOTMAX=2000
|
||||||
|
-DDEFAULT_FIELDWEAKMAX=17
|
||||||
|
-DDEFAULT_FIELDADVMAX=40
|
||||||
|
|
||||||
|
# DPAD
|
||||||
|
# -DFEATURE_DPAD_5WIRESW
|
||||||
|
# -DPINS_DPAD_5WIRESW_OUT=4
|
||||||
|
# -DPINS_DPAD_5WIRESW_IN1=5
|
||||||
|
# -DPINS_DPAD_5WIRESW_IN2=27
|
||||||
|
# -DPINS_DPAD_5WIRESW_IN3=18
|
||||||
|
# -DPINS_DPAD_5WIRESW_IN4=19
|
||||||
|
# -DPINS_DPAD_EXTRASW_IN5=25
|
||||||
|
# -DFEATURE_DPAD_6WIRESW
|
||||||
|
# -DPINS_DPAD_6WIRESW_OUT=4
|
||||||
|
# -DPINS_DPAD_6WIRESW_IN1=5
|
||||||
|
# -DPINS_DPAD_6WIRESW_IN2=27
|
||||||
|
# -DPINS_DPAD_6WIRESW_IN3=18
|
||||||
|
# -DPINS_DPAD_6WIRESW_IN4=19
|
||||||
|
# -DPINS_DPAD_6WIRESW_IN5=26
|
||||||
|
# -DFEATURE_GSCHISSENE_DIODE
|
||||||
|
-DDPAD_BOARDCOMPUTER_V2
|
||||||
|
-DPINS_DPAD_BOARDCOMPUTER_V2_OUT1=19
|
||||||
|
-DPINS_DPAD_BOARDCOMPUTER_V2_OUT2=5
|
||||||
|
-DPINS_DPAD_BOARDCOMPUTER_V2_IN1=4
|
||||||
|
-DPINS_DPAD_BOARDCOMPUTER_V2_IN2=18
|
||||||
|
-DPINS_DPAD_BOARDCOMPUTER_V2_IN3=27
|
||||||
|
-DPINS_DPAD_BOARDCOMPUTER_V2_IN4=16
|
||||||
|
# -DDPAD_BOARDCOMPUTER_V2_DEBUG
|
||||||
|
)
|
||||||
|
|
@@ -240,6 +240,7 @@ set(headers
|
|||||||
texthelpers/wifistatexthelpers.h
|
texthelpers/wifistatexthelpers.h
|
||||||
time_bobbycar.h
|
time_bobbycar.h
|
||||||
types.h
|
types.h
|
||||||
|
typeutils.h
|
||||||
udpcloud.h
|
udpcloud.h
|
||||||
unifiedmodelmode.h
|
unifiedmodelmode.h
|
||||||
utils.h
|
utils.h
|
||||||
|
@@ -44,6 +44,7 @@ struct BleFenceEnabledAccessor : public NewSettingsAccessor<bool> { ConfigWrappe
|
|||||||
// Cloud
|
// Cloud
|
||||||
struct CloudEnabledAccessor : public NewSettingsAccessor<bool> { ConfigWrapper<bool> &getConfig() const override { return configs.cloudSettings.cloudEnabled; } };
|
struct CloudEnabledAccessor : public NewSettingsAccessor<bool> { ConfigWrapper<bool> &getConfig() const override { return configs.cloudSettings.cloudEnabled; } };
|
||||||
struct CloudTransmitTimeoutAccessor : public NewSettingsAccessor<int16_t> { ConfigWrapper<int16_t> &getConfig() const override { return configs.cloudSettings.cloudTransmitTimeout; } };
|
struct CloudTransmitTimeoutAccessor : public NewSettingsAccessor<int16_t> { ConfigWrapper<int16_t> &getConfig() const override { return configs.cloudSettings.cloudTransmitTimeout; } };
|
||||||
|
struct CloudSendStatisticsAccessor : public NewSettingsAccessor<bool> { ConfigWrapper<bool> &getConfig() const override { return configs.cloudSettings.sendStatistic; } };
|
||||||
|
|
||||||
// Time
|
// Time
|
||||||
//struct TimezoneOffsetAccessor : public NewSettingsAccessor<int32_t> { ConfigWrapper<int32_t> &getConfig() const override { return configs.timezoneOffset; } };
|
//struct TimezoneOffsetAccessor : public NewSettingsAccessor<int32_t> { ConfigWrapper<int32_t> &getConfig() const override { return configs.timezoneOffset; } };
|
||||||
|
@@ -6,9 +6,7 @@
|
|||||||
// local includes
|
// local includes
|
||||||
#include "newsettings.h"
|
#include "newsettings.h"
|
||||||
#include "settingsutils.h"
|
#include "settingsutils.h"
|
||||||
#include "modes/defaultmode.h"
|
|
||||||
#include "ledstripdefines.h"
|
#include "ledstripdefines.h"
|
||||||
#include "ledstrip.h"
|
|
||||||
|
|
||||||
#include "bobbyquickactions.h"
|
#include "bobbyquickactions.h"
|
||||||
|
|
||||||
|
@@ -21,7 +21,8 @@ enum BobbyButton
|
|||||||
Extra3,
|
Extra3,
|
||||||
Extra4,
|
Extra4,
|
||||||
Confirm = espgui::Button::Right,
|
Confirm = espgui::Button::Right,
|
||||||
Back = espgui::Button::Left
|
Back = espgui::Button::Left,
|
||||||
|
ButtonMax = Back
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] std::optional<espgui::Button> translateRawButton(uint8_t button);
|
[[nodiscard]] std::optional<espgui::Button> translateRawButton(uint8_t button);
|
||||||
|
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
// 3rdparty lib includes
|
// 3rdparty lib includes
|
||||||
#include <buttonsinterface.h>
|
#include <buttonsinterface.h>
|
||||||
#include <cpptypesafeenum.h>
|
|
||||||
|
|
||||||
// local includes
|
// local includes
|
||||||
|
#include "bobbytypesafeenum.h"
|
||||||
#include "bobbybuttons.h"
|
#include "bobbybuttons.h"
|
||||||
|
|
||||||
#define BobbyQuickActionsValues(x) \
|
#define BobbyQuickActionsValues(x) \
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
x(HUPE) \
|
x(HUPE) \
|
||||||
x(COMPRESSOR_TOGGLE)
|
x(COMPRESSOR_TOGGLE)
|
||||||
|
|
||||||
DECLARE_TYPESAFE_ENUM(BobbyQuickActions, : uint8_t, BobbyQuickActionsValues)
|
DECLARE_BOBBYTYPESAFE_ENUM(BobbyQuickActions, : uint8_t, BobbyQuickActionsValues)
|
||||||
|
|
||||||
namespace quickactions {
|
namespace quickactions {
|
||||||
|
|
||||||
|
@@ -265,6 +265,9 @@ void sendCanCommands()
|
|||||||
static uint32_t can_sequential_error_cnt = 0;
|
static uint32_t can_sequential_error_cnt = 0;
|
||||||
static uint32_t can_sequential_bus_errors = 0;
|
static uint32_t can_sequential_bus_errors = 0;
|
||||||
|
|
||||||
|
if (!configs.controllerHardware.sendFrontCanCmd.value() && !configs.controllerHardware.sendBackCanCmd.value())
|
||||||
|
return;
|
||||||
|
|
||||||
constexpr auto send = [](uint32_t addr, auto value){
|
constexpr auto send = [](uint32_t addr, auto value){
|
||||||
twai_message_t message;
|
twai_message_t message;
|
||||||
twai_status_info_t status_info;
|
twai_status_info_t status_info;
|
||||||
|
840
main/cloud.cpp
840
main/cloud.cpp
@@ -7,45 +7,561 @@
|
|||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
|
|
||||||
// 3rdparty lib includes
|
// 3rdparty lib includes
|
||||||
|
#include <ArduinoJson.h>
|
||||||
#include <espwifistack.h>
|
#include <espwifistack.h>
|
||||||
#include <esphttpdutils.h>
|
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
|
#include <menudisplay.h>
|
||||||
|
#include <numberparsing.h>
|
||||||
|
#include <screenmanager.h>
|
||||||
|
#include <tftinstance.h>
|
||||||
#include <tickchrono.h>
|
#include <tickchrono.h>
|
||||||
#include <wrappers/websocket_client.h>
|
#include <wrappers/websocket_client.h>
|
||||||
|
|
||||||
// local includes
|
// local includes
|
||||||
|
#include "bobbyerrorhandler.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "utils.h"
|
|
||||||
#include "newsettings.h"
|
#include "newsettings.h"
|
||||||
|
#include "ota.h"
|
||||||
|
#include "typeutils.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
namespace {
|
|
||||||
constexpr const char * const TAG = "BOBBYCLOUD";
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
|
|
||||||
espcpputils::websocket_client cloudClient;
|
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;
|
std::string cloudBuffer;
|
||||||
|
|
||||||
|
namespace {
|
||||||
std::optional<espchrono::millis_clock::time_point> lastCloudCollect;
|
std::optional<espchrono::millis_clock::time_point> lastCloudCollect;
|
||||||
std::optional<espchrono::millis_clock::time_point> lastCloudSend;
|
std::optional<espchrono::millis_clock::time_point> lastCloudSend;
|
||||||
|
std::optional<espchrono::millis_clock::time_point> lastHeartbeat;
|
||||||
|
std::optional<espchrono::millis_clock::time_point> lastOtaStatus;
|
||||||
|
|
||||||
|
bool hasAnnouncedItself{};
|
||||||
|
espchrono::millis_clock::time_point isSendingNvs{};
|
||||||
|
|
||||||
|
constexpr const char * const TAG = "BOBBYCLOUD";
|
||||||
|
constexpr const auto json_document_size = 1024;
|
||||||
|
StaticJsonDocument<json_document_size> doc;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct is_duration : std::false_type {};
|
||||||
|
|
||||||
|
template<class Rep, class Period>
|
||||||
|
struct is_duration<std::chrono::duration<Rep, Period>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename _Tp>
|
||||||
|
inline constexpr bool is_duration_v = is_duration<_Tp>::value;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename std::enable_if<
|
||||||
|
!std::is_same_v<T, bool> &&
|
||||||
|
!std::is_integral_v<T> &&
|
||||||
|
!is_duration_v<T> &&
|
||||||
|
!std::is_same_v<T, std::string> &&
|
||||||
|
!std::is_same_v<T, wifi_stack::ip_address_t> &&
|
||||||
|
!std::is_same_v<T, wifi_stack::mac_t> &&
|
||||||
|
!std::is_same_v<T, std::optional<wifi_stack::mac_t>> &&
|
||||||
|
!std::is_same_v<T, wifi_auth_mode_t> &&
|
||||||
|
!std::is_same_v<T, sntp_sync_mode_t> &&
|
||||||
|
!std::is_same_v<T, espchrono::DayLightSavingMode> &&
|
||||||
|
!std::is_same_v<T, OtaAnimationModes> &&
|
||||||
|
!std::is_same_v<T, LedstripAnimation> &&
|
||||||
|
!std::is_same_v<T, HandbremseMode> &&
|
||||||
|
!std::is_same_v<T, BobbyQuickActions> &&
|
||||||
|
!std::is_same_v<T, BatteryCellType>
|
||||||
|
, void>::type
|
||||||
|
toArduinoJson(std::string_view key, T value, T defaultValue, JsonObject &object)
|
||||||
|
{
|
||||||
|
object["n"] = key;
|
||||||
|
object["v"] = nullptr;
|
||||||
|
object["d"] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same_v<T, bool> ||
|
||||||
|
std::is_integral_v<T>
|
||||||
|
, void>::type
|
||||||
|
toArduinoJson(std::string_view key, T value, T defaultValue, JsonObject &object)
|
||||||
|
{
|
||||||
|
object["n"] = key;
|
||||||
|
object["v"] = value;
|
||||||
|
object["d"] = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename std::enable_if<
|
||||||
|
is_duration_v<T>
|
||||||
|
, void>::type
|
||||||
|
toArduinoJson(std::string_view key, T value, T defaultValue, JsonObject &object)
|
||||||
|
{
|
||||||
|
object["n"] = key;
|
||||||
|
object["v"] = value.count();
|
||||||
|
object["d"] = defaultValue.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same_v<T, std::string>
|
||||||
|
, void>::type
|
||||||
|
toArduinoJson(std::string_view key, T value, T defaultValue, JsonObject &object)
|
||||||
|
{
|
||||||
|
object["n"] = key;
|
||||||
|
object["v"] = value;
|
||||||
|
object["d"] = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same_v<T, wifi_stack::ip_address_t> ||
|
||||||
|
std::is_same_v<T, wifi_stack::mac_t> ||
|
||||||
|
std::is_same_v<T, wifi_auth_mode_t>
|
||||||
|
, void>::type
|
||||||
|
toArduinoJson(std::string_view key, T value, T defaultValue, JsonObject &object)
|
||||||
|
{
|
||||||
|
object["n"] = key;
|
||||||
|
object["v"] = wifi_stack::toString(value);
|
||||||
|
object["d"] = wifi_stack::toString(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same_v<T, std::optional<wifi_stack::mac_t>>
|
||||||
|
, void>::type
|
||||||
|
toArduinoJson(std::string_view key, T value, T defaultValue, JsonObject &object)
|
||||||
|
{
|
||||||
|
object["n"] = key;
|
||||||
|
if (value)
|
||||||
|
object["v"] = wifi_stack::toString(*value);
|
||||||
|
else
|
||||||
|
object["v"] = nullptr;
|
||||||
|
|
||||||
|
if (defaultValue)
|
||||||
|
object["d"] = wifi_stack::toString(*defaultValue);
|
||||||
|
else
|
||||||
|
object["d"] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same_v<T, OtaAnimationModes> ||
|
||||||
|
std::is_same_v<T, LedstripAnimation> ||
|
||||||
|
std::is_same_v<T, HandbremseMode> ||
|
||||||
|
std::is_same_v<T, BobbyQuickActions> ||
|
||||||
|
std::is_same_v<T, BatteryCellType>
|
||||||
|
, void>::type
|
||||||
|
toArduinoJson(std::string_view key, T value, T defaultValue, JsonObject &object)
|
||||||
|
{
|
||||||
|
object["n"] = key;
|
||||||
|
object["v"] = std::to_underlying(value);
|
||||||
|
object["d"] = std::to_underlying(defaultValue);
|
||||||
|
|
||||||
|
JsonArray enumObject = object.createNestedArray("e");
|
||||||
|
|
||||||
|
iterateEnum<T>::iterate([&](T enum_value, const auto &string_value){
|
||||||
|
enumObject.add(toString(enum_value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same_v<T, sntp_sync_mode_t> ||
|
||||||
|
std::is_same_v<T, espchrono::DayLightSavingMode>
|
||||||
|
, void>::type
|
||||||
|
toArduinoJson(std::string_view key, T value, T defaultValue, JsonObject &object)
|
||||||
|
{
|
||||||
|
object["n"] = key;
|
||||||
|
object["v"] = std::to_underlying(value);
|
||||||
|
object["d"] = std::to_underlying(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// setter
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename std::enable_if<
|
||||||
|
!std::is_same_v<T, bool> &&
|
||||||
|
!std::is_integral_v<T> &&
|
||||||
|
!std::is_same_v<T, std::string> &&
|
||||||
|
!std::is_same_v<T, wifi_stack::ip_address_t> &&
|
||||||
|
!std::is_same_v<T, wifi_stack::mac_t> &&
|
||||||
|
!std::is_same_v<T, std::optional<wifi_stack::mac_t>> &&
|
||||||
|
!std::is_same_v<T, wifi_auth_mode_t> &&
|
||||||
|
!std::is_same_v<T, sntp_sync_mode_t> &&
|
||||||
|
!std::is_same_v<T, espchrono::DayLightSavingMode> &&
|
||||||
|
!std::is_same_v<T, OtaAnimationModes> &&
|
||||||
|
!std::is_same_v<T, LedstripAnimation> &&
|
||||||
|
!std::is_same_v<T, HandbremseMode> &&
|
||||||
|
!std::is_same_v<T, BobbyQuickActions> &&
|
||||||
|
!std::is_same_v<T, BatteryCellType>
|
||||||
|
, tl::expected<void, std::string>>::type
|
||||||
|
set_config(ConfigWrapper<T> &config, std::string_view newValue)
|
||||||
|
{
|
||||||
|
return tl::make_unexpected("Unsupported config type");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same_v<T, bool>
|
||||||
|
, tl::expected<void, std::string>>::type
|
||||||
|
set_config(ConfigWrapper<T> &config, std::string_view newValue)
|
||||||
|
{
|
||||||
|
if (cpputils::is_in(newValue, "true", "false"))
|
||||||
|
return configs.write_config(config, newValue == "true");
|
||||||
|
else
|
||||||
|
return tl::make_unexpected(fmt::format("only true and false allowed, not {}", newValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename std::enable_if<
|
||||||
|
!std::is_same_v<T, bool> &&
|
||||||
|
std::is_integral_v<T>
|
||||||
|
, tl::expected<void, std::string>>::type
|
||||||
|
set_config(ConfigWrapper<T> &config, std::string_view newValue)
|
||||||
|
{
|
||||||
|
if (auto parsed = cpputils::fromString<T>(newValue))
|
||||||
|
return configs.write_config(config, *parsed);
|
||||||
|
else
|
||||||
|
return tl::make_unexpected(fmt::format("could not parse {}", newValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same_v<T, std::string>
|
||||||
|
, tl::expected<void, std::string>>::type
|
||||||
|
set_config(ConfigWrapper<T> &config, std::string_view newValue)
|
||||||
|
{
|
||||||
|
return configs.write_config(config, std::string{newValue});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same_v<T, wifi_stack::ip_address_t>
|
||||||
|
, tl::expected<void, std::string>>::type
|
||||||
|
set_config(ConfigWrapper<T> &config, std::string_view newValue)
|
||||||
|
{
|
||||||
|
if (const auto parsed = wifi_stack::fromString<wifi_stack::ip_address_t>(newValue); parsed)
|
||||||
|
return configs.write_config(config, *parsed);
|
||||||
|
else
|
||||||
|
return tl::make_unexpected(parsed.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same_v<T, wifi_stack::mac_t>
|
||||||
|
, tl::expected<void, std::string>>::type
|
||||||
|
set_config(ConfigWrapper<T> &config, std::string_view newValue)
|
||||||
|
{
|
||||||
|
if (const auto parsed = wifi_stack::fromString<wifi_stack::mac_t>(newValue); parsed)
|
||||||
|
return configs.write_config(config, *parsed);
|
||||||
|
else
|
||||||
|
return tl::make_unexpected(parsed.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same_v<T, std::optional<wifi_stack::mac_t>>
|
||||||
|
, tl::expected<void, std::string>>::type
|
||||||
|
set_config(ConfigWrapper<T> &config, std::string_view newValue)
|
||||||
|
{
|
||||||
|
if (newValue.empty())
|
||||||
|
return configs.write_config(config, std::nullopt);
|
||||||
|
else if (const auto parsed = wifi_stack::fromString<wifi_stack::mac_t>(newValue); parsed)
|
||||||
|
return configs.write_config(config, *parsed);
|
||||||
|
else
|
||||||
|
return tl::make_unexpected(parsed.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same_v<T, wifi_auth_mode_t> ||
|
||||||
|
std::is_same_v<T, sntp_sync_mode_t> ||
|
||||||
|
std::is_same_v<T, espchrono::DayLightSavingMode> ||
|
||||||
|
std::is_same_v<T, OtaAnimationModes> ||
|
||||||
|
std::is_same_v<T, LedstripAnimation> ||
|
||||||
|
std::is_same_v<T, HandbremseMode> ||
|
||||||
|
std::is_same_v<T, BobbyQuickActions> ||
|
||||||
|
std::is_same_v<T, BatteryCellType>
|
||||||
|
, tl::expected<void, std::string>>::type
|
||||||
|
set_config(ConfigWrapper<T> &config, std::string_view newValue)
|
||||||
|
{
|
||||||
|
if (auto parsed = cpputils::fromString<std::underlying_type_t<T>>(newValue))
|
||||||
|
return configs.write_config(config, T(*parsed));
|
||||||
|
else
|
||||||
|
return tl::make_unexpected(fmt::format("could not parse {}", newValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_config(uint32_t skipCount)
|
||||||
|
{
|
||||||
|
if (!cloudClient.is_connected())
|
||||||
|
return;
|
||||||
|
doc.clear();
|
||||||
|
|
||||||
|
{
|
||||||
|
const std::string body = fmt::format(R"({{"type":"configCount","count":{}}})", configs.getConfigCount());
|
||||||
|
const auto timeout = std::chrono::ceil<espcpputils::ticks>(
|
||||||
|
espchrono::milliseconds32{configs.cloudSettings.cloudTransmitTimeout.value()}).count();
|
||||||
|
const auto result_size = cloudClient.send_text(body, timeout);
|
||||||
|
if (result_size != body.size()) {
|
||||||
|
ESP_LOGE(TAG, "send_text failed: %d", result_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doc["type"] = "config";
|
||||||
|
JsonArray configsArray = doc.createNestedArray("configs");
|
||||||
|
|
||||||
|
bool stop{false};
|
||||||
|
bool has_overflowed{false};
|
||||||
|
|
||||||
|
size_t i{0};
|
||||||
|
|
||||||
|
configs.callForEveryConfig([&](auto &config) {
|
||||||
|
if (skipCount > 0)
|
||||||
|
{
|
||||||
|
--skipCount;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stop)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::string_view nvsName{config.nvsName()};
|
||||||
|
|
||||||
|
i++;
|
||||||
|
|
||||||
|
JsonObject configObject = configsArray.createNestedObject();
|
||||||
|
toArduinoJson(nvsName, config.value(), config.defaultValue(), configObject);
|
||||||
|
configObject["T"] = typeutils::t_to_str<decltype(config.value())>::str;
|
||||||
|
configObject["t"] = config.touched();
|
||||||
|
|
||||||
|
if (doc.overflowed())
|
||||||
|
{
|
||||||
|
has_overflowed = true;
|
||||||
|
// send data, clear doc and try again
|
||||||
|
std::string body;
|
||||||
|
configsArray.remove(i-1);
|
||||||
|
serializeJson(doc, body);
|
||||||
|
const auto timeout = std::chrono::ceil<espcpputils::ticks>(espchrono::milliseconds32{configs.cloudSettings.cloudTransmitTimeout.value()}).count();
|
||||||
|
const auto result_size = cloudClient.send_text(body, timeout);
|
||||||
|
if (result_size != body.size())
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "send_text failed: %d", result_size);
|
||||||
|
}
|
||||||
|
stop = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!has_overflowed)
|
||||||
|
{
|
||||||
|
std::string body;
|
||||||
|
doc["last"] = true;
|
||||||
|
doc["type"] = "lastConfig";
|
||||||
|
serializeJson(doc, body);
|
||||||
|
const auto timeout = std::chrono::ceil<espcpputils::ticks>(espchrono::milliseconds32{configs.cloudSettings.cloudTransmitTimeout.value()}).count();
|
||||||
|
const auto result_size = cloudClient.send_text(body, timeout);
|
||||||
|
if (result_size != body.size())
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "send_text failed: %d", result_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_single_config(const std::string &nvsName, bool force_update = false)
|
||||||
|
{
|
||||||
|
if (!cloudClient.is_connected())
|
||||||
|
return;
|
||||||
|
doc.clear();
|
||||||
|
doc["type"] = "singleConfig";
|
||||||
|
|
||||||
|
bool success{false};
|
||||||
|
configs.callForEveryConfig([&](auto &config) {
|
||||||
|
if (config.nvsName() == nvsName)
|
||||||
|
{
|
||||||
|
JsonObject configObject = doc.createNestedObject("config");
|
||||||
|
toArduinoJson(nvsName, config.value(), config.defaultValue(), configObject);
|
||||||
|
configObject["T"] = typeutils::t_to_str<decltype(config.value())>::str;
|
||||||
|
configObject["t"] = config.touched();
|
||||||
|
configObject["f"] = force_update;
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
std::string body;
|
||||||
|
if (!success)
|
||||||
|
doc["error"] = "Config not found";
|
||||||
|
serializeJson(doc, body);
|
||||||
|
doc.clear();
|
||||||
|
const auto timeout = std::chrono::ceil<espcpputils::ticks>(
|
||||||
|
espchrono::milliseconds32{configs.cloudSettings.cloudTransmitTimeout.value()}).count();
|
||||||
|
cloudClient.send_text(body, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_information()
|
||||||
|
{
|
||||||
|
if (!cloudClient.is_connected())
|
||||||
|
return;
|
||||||
|
doc.clear();
|
||||||
|
|
||||||
|
doc["type"] = "info";
|
||||||
|
JsonObject infoObject = doc.createNestedObject("info");
|
||||||
|
JsonObject gitObject = infoObject.createNestedObject("git");
|
||||||
|
gitObject["branch"] = GIT_BRANCH;
|
||||||
|
gitObject["commit"] = GIT_REV;
|
||||||
|
|
||||||
|
JsonObject wifiObject = infoObject.createNestedObject("wifi");
|
||||||
|
const bool wifi_connected = wifi_stack::get_sta_status() == wifi_stack::WiFiStaStatus::CONNECTED;
|
||||||
|
wifiObject["connected"] = wifi_connected;
|
||||||
|
if (wifi_connected)
|
||||||
|
{
|
||||||
|
if (const auto result = wifi_stack::get_ip_info(wifi_stack::esp_netifs[ESP_IF_WIFI_STA]); result)
|
||||||
|
{
|
||||||
|
wifiObject["ip"] = wifi_stack::toString(result->ip);
|
||||||
|
wifiObject["mask"] = wifi_stack::toString(result->netmask);
|
||||||
|
wifiObject["gw"] = wifi_stack::toString(result->gw);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wifiObject["error"] = "Could not get IP info";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto result = wifi_stack::get_sta_ap_info(); result)
|
||||||
|
{
|
||||||
|
wifiObject["ssid"] = std::string_view{reinterpret_cast<const char*>(result->ssid)};
|
||||||
|
wifiObject["bssid"] = wifi_stack::toString(wifi_stack::mac_t{result->bssid});
|
||||||
|
wifiObject["channel"] = result->primary;
|
||||||
|
wifiObject["rssi"] = result->rssi;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wifiObject["error"] = "Could not get STA info";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto currentDisplay = static_cast<const espgui::Display *>(espgui::currentDisplay.get()))
|
||||||
|
{
|
||||||
|
JsonObject displayObject = infoObject.createNestedObject("display");
|
||||||
|
if (const auto *textInterface = currentDisplay->asTextInterface())
|
||||||
|
{
|
||||||
|
displayObject["name"] = textInterface->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto *display = currentDisplay->asMenuDisplay())
|
||||||
|
{
|
||||||
|
displayObject["name"] = display->text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
infoObject["display"] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
infoObject["uptime"] = espchrono::millis_clock::now().time_since_epoch().count();
|
||||||
|
|
||||||
|
// battery
|
||||||
|
if (const auto avgVoltage = controllers.getAvgVoltage(); avgVoltage)
|
||||||
|
{
|
||||||
|
infoObject["percentage"] = fmt::format("{:.1f}", getBatteryPercentage(*avgVoltage, BatteryCellType(configs.battery.cellType.value())));
|
||||||
|
infoObject["voltage"] = *avgVoltage;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
infoObject["percentage"] = nullptr;
|
||||||
|
infoObject["voltage"] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
infoObject["tempFront"] = fixBoardTemp(controllers.front.feedback.boardTemp);
|
||||||
|
infoObject["tempBack"] = fixBoardTemp(controllers.back.feedback.boardTemp);
|
||||||
|
infoObject["current"] = sumCurrent;
|
||||||
|
|
||||||
|
infoObject["btnCnt"] = bobbydpad::ButtonCount;
|
||||||
|
|
||||||
|
std::string body;
|
||||||
|
serializeJson(doc, body);
|
||||||
|
doc.clear();
|
||||||
|
const auto timeout = std::chrono::ceil<espcpputils::ticks>(
|
||||||
|
espchrono::milliseconds32{configs.cloudSettings.cloudTransmitTimeout.value()}).count();
|
||||||
|
cloudClient.send_text(body, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_uptime()
|
||||||
|
{
|
||||||
|
if (!cloudClient.is_connected())
|
||||||
|
return;
|
||||||
|
doc.clear();
|
||||||
|
doc["type"] = "uptime";
|
||||||
|
doc["info"] = espchrono::millis_clock::now().time_since_epoch().count();
|
||||||
|
std::string body;
|
||||||
|
serializeJson(doc, body);
|
||||||
|
doc.clear();
|
||||||
|
const auto timeout = std::chrono::ceil<espcpputils::ticks>(
|
||||||
|
espchrono::milliseconds32{configs.cloudSettings.cloudTransmitTimeout.value()}).count();
|
||||||
|
cloudClient.send_text(body, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_ota_status()
|
||||||
|
{
|
||||||
|
if (!cloudClient.is_connected())
|
||||||
|
return;
|
||||||
|
doc.clear();
|
||||||
|
doc["type"] = "otaStatus";
|
||||||
|
if (!asyncOta)
|
||||||
|
{
|
||||||
|
doc["info"] = nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JsonObject otaObject = doc.createNestedObject("info");
|
||||||
|
otaObject["status"] = toString(asyncOta->status());
|
||||||
|
otaObject["progress"] = asyncOta->progress();
|
||||||
|
if (const auto totalSize = asyncOta->totalSize(); totalSize)
|
||||||
|
{
|
||||||
|
otaObject["totalSize"] = *totalSize;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
otaObject["totalSize"] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto &appDesc = asyncOta->appDesc())
|
||||||
|
{
|
||||||
|
otaObject["newVersion"] = appDesc->version;
|
||||||
|
otaObject["date"] = appDesc->date;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
otaObject["newVersion"] = nullptr;
|
||||||
|
otaObject["date"] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string body;
|
||||||
|
serializeJson(doc, body);
|
||||||
|
doc.clear();
|
||||||
|
const auto timeout = std::chrono::ceil<espcpputils::ticks>(
|
||||||
|
espchrono::milliseconds32{configs.cloudSettings.cloudTransmitTimeout.value()}).count();
|
||||||
|
cloudClient.send_text(body, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cloudHeartbeat()
|
||||||
|
{
|
||||||
|
if (!cloudClient.is_connected())
|
||||||
|
return;
|
||||||
|
const auto timeout = std::chrono::ceil<espcpputils::ticks>(
|
||||||
|
espchrono::milliseconds32{configs.cloudSettings.cloudTransmitTimeout.value()}).count();
|
||||||
|
cloudClient.send_text(R"({"type":"heartbeat"})", timeout);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void initCloud()
|
void initCloud()
|
||||||
{
|
{
|
||||||
if (configs.cloudSettings.cloudEnabled.value() &&
|
if (configs.cloudSettings.cloudEnabled.value() &&
|
||||||
!configs.cloudUrl.value().empty())
|
!configs.cloudUrl.value().empty() && wifi_stack::get_sta_status() == wifi_stack::WiFiStaStatus::CONNECTED)
|
||||||
{
|
{
|
||||||
createCloud();
|
createCloud();
|
||||||
if (!cloudClient)
|
if (!cloudClient)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (wifi_stack::get_sta_status() != wifi_stack::WiFiStaStatus::CONNECTED)
|
|
||||||
return;
|
|
||||||
|
|
||||||
startCloud();
|
startCloud();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,12 +573,15 @@ void updateCloud()
|
|||||||
|
|
||||||
const auto now = espchrono::millis_clock::now();
|
const auto now = espchrono::millis_clock::now();
|
||||||
|
|
||||||
if (!lastCloudCollect || now - *lastCloudCollect >= std::chrono::milliseconds{configs.boardcomputerHardware.timersSettings.cloudCollectRate.value()})
|
if (configs.cloudSettings.sendStatistic.value())
|
||||||
{
|
{
|
||||||
|
if (!lastCloudCollect || now - *lastCloudCollect >= std::chrono::milliseconds{
|
||||||
|
configs.boardcomputerHardware.timersSettings.cloudCollectRate.value()}) {
|
||||||
cloudCollect();
|
cloudCollect();
|
||||||
|
|
||||||
lastCloudCollect = now;
|
lastCloudCollect = now;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!lastCloudSend || now - *lastCloudSend >= 1000ms/configs.boardcomputerHardware.timersSettings.cloudSendRate.value())
|
if (!lastCloudSend || now - *lastCloudSend >= 1000ms/configs.boardcomputerHardware.timersSettings.cloudSendRate.value())
|
||||||
{
|
{
|
||||||
@@ -70,6 +589,21 @@ void updateCloud()
|
|||||||
|
|
||||||
lastCloudSend = now;
|
lastCloudSend = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!lastHeartbeat || now - *lastHeartbeat >= 1500ms)
|
||||||
|
{
|
||||||
|
cloudHeartbeat();
|
||||||
|
lastHeartbeat = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asyncOtaTaskStarted)
|
||||||
|
{
|
||||||
|
if (!lastOtaStatus || now - *lastOtaStatus >= 1000ms)
|
||||||
|
{
|
||||||
|
send_ota_status();
|
||||||
|
lastOtaStatus = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cloudCollect()
|
void cloudCollect()
|
||||||
@@ -92,6 +626,8 @@ void cloudCollect()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (configs.cloudSettings.sendStatistic.value())
|
||||||
|
{
|
||||||
if (cloudBuffer.empty())
|
if (cloudBuffer.empty())
|
||||||
cloudBuffer = '[';
|
cloudBuffer = '[';
|
||||||
else
|
else
|
||||||
@@ -165,19 +701,27 @@ void cloudCollect()
|
|||||||
|
|
||||||
cloudBuffer += "]";
|
cloudBuffer += "]";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void cloudSend()
|
void cloudSend()
|
||||||
{
|
{
|
||||||
if (configs.cloudSettings.cloudEnabled.value() &&
|
if (!configs.cloudSettings.cloudEnabled.value())
|
||||||
!configs.cloudUrl.value().empty())
|
|
||||||
{
|
{
|
||||||
if (!cloudClient)
|
if (cloudClient)
|
||||||
{
|
destroyCloud();
|
||||||
if (espchrono::ago(lastCreateTry) < 10s)
|
|
||||||
return;
|
return;
|
||||||
createCloud();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cloudClient)
|
if (!cloudClient)
|
||||||
|
{
|
||||||
|
initCloud();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configs.cloudUrl.value().empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!configs.cloudSettings.sendStatistic.value())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!cloudStarted)
|
if (!cloudStarted)
|
||||||
@@ -189,19 +733,28 @@ void cloudSend()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
startCloud();
|
startCloud();
|
||||||
}
|
|
||||||
if (!cloudStarted)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!cloudClient.is_connected())
|
if (!cloudClient.is_connected())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (cloudBuffer.empty())
|
if (cloudBuffer.empty() && hasAnnouncedItself)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cloudBuffer += ']';
|
cloudBuffer += ']';
|
||||||
|
|
||||||
const auto timeout = std::chrono::ceil<espcpputils::ticks>(espchrono::milliseconds32{configs.cloudSettings.cloudTransmitTimeout.value()}).count();
|
const auto timeout = std::chrono::ceil<espcpputils::ticks>(espchrono::milliseconds32{configs.cloudSettings.cloudTransmitTimeout.value()}).count();
|
||||||
|
|
||||||
|
if (!hasAnnouncedItself && configs.cloudSettings.cloudEnabled.value())
|
||||||
|
{
|
||||||
|
std::string helloWorld = getLoginMessage();
|
||||||
|
ESP_LOGW(TAG, "=====> %s", helloWorld.c_str());
|
||||||
|
const auto written_helloWorld = cloudClient.send_text(helloWorld, timeout);
|
||||||
|
if (written_helloWorld == helloWorld.size())
|
||||||
|
hasAnnouncedItself = true;
|
||||||
|
}
|
||||||
|
|
||||||
const auto written = cloudClient.send_text(cloudBuffer, timeout);
|
const auto written = cloudClient.send_text(cloudBuffer, timeout);
|
||||||
|
|
||||||
if (written < 0)
|
if (written < 0)
|
||||||
@@ -215,14 +768,241 @@ void cloudSend()
|
|||||||
|
|
||||||
cloudBuffer.clear();
|
cloudBuffer.clear();
|
||||||
}
|
}
|
||||||
else if (cloudClient)
|
|
||||||
|
std::string getLoginMessage()
|
||||||
{
|
{
|
||||||
destroyCloud();
|
using namespace espgui;
|
||||||
|
return fmt::format(R"({{"type": "hello", "name": "{}", "res": "{}x{}", "pass": "{}", "key": "{}"}})",
|
||||||
|
configs.otaUsername.value(), tft.width(), tft.height(), configs.webserverPassword.value(), configs.cloudSettings.cloudKey.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
void cloudEventHandler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
||||||
|
{
|
||||||
|
CPP_UNUSED(event_handler_arg);
|
||||||
|
|
||||||
|
const esp_websocket_event_data_t *data = reinterpret_cast<const esp_websocket_event_data_t *>(event_data);
|
||||||
|
|
||||||
|
switch (esp_websocket_event_id_t(event_id))
|
||||||
|
{
|
||||||
|
case WEBSOCKET_EVENT_CONNECTED:
|
||||||
|
ESP_LOGI(TAG, "WEBSOCKET_EVENT_CONNECTED");
|
||||||
|
hasAnnouncedItself = false;
|
||||||
|
break;
|
||||||
|
case WEBSOCKET_EVENT_DISCONNECTED:
|
||||||
|
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DISCONNECTED");
|
||||||
|
break;
|
||||||
|
case WEBSOCKET_EVENT_DATA:
|
||||||
|
{
|
||||||
|
if (data->op_code != 1) // text
|
||||||
|
return;
|
||||||
|
|
||||||
|
doc.clear();
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Received: %.*s", data->data_len, data->data_ptr);
|
||||||
|
|
||||||
|
if (const auto err = deserializeJson(doc, data->data_ptr, data->data_len); err)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "deserializeJson() failed with %s", err.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string type = doc["type"];
|
||||||
|
if (type == "popup")
|
||||||
|
{
|
||||||
|
std::string text = doc["msg"];
|
||||||
|
std::string id = doc["id"];
|
||||||
|
doc.clear();
|
||||||
|
ESP_LOGI(TAG, "popup: %s, id: %s", text.c_str(), id.c_str());
|
||||||
|
BobbyErrorHandler{}.errorOccurred(std::move(text));
|
||||||
|
|
||||||
|
if (id.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto timeout = std::chrono::ceil<espcpputils::ticks>(espchrono::milliseconds32{configs.cloudSettings.cloudTransmitTimeout.value()}).count();
|
||||||
|
const auto message = fmt::format(R"({{"type":"response","id":"{}"}})", id);
|
||||||
|
ESP_LOGI(TAG, "sending response: %s", message.c_str());
|
||||||
|
cloudClient.send_text(message, timeout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (type == "getConfig")
|
||||||
|
{
|
||||||
|
JsonVariant _id = doc["id"];
|
||||||
|
if (_id.isNull())
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "getConfig: no id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto id = _id.as<uint32_t>();
|
||||||
|
doc.clear();
|
||||||
|
isSendingNvs = espchrono::millis_clock::now();
|
||||||
|
send_config(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (type == "getSingleConfig")
|
||||||
|
{
|
||||||
|
JsonVariant nvskey = doc["nvskey"];
|
||||||
|
if (nvskey.isNull())
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "getSingleConfig: nvskey is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string name = nvskey.as<std::string>();
|
||||||
|
doc.clear();
|
||||||
|
send_single_config(name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (type == "setConfig")
|
||||||
|
{
|
||||||
|
std::string name = doc["nvskey"];
|
||||||
|
std::string value = doc["value"];
|
||||||
|
doc.clear();
|
||||||
|
bool success{false};
|
||||||
|
configs.callForEveryConfig([&](auto &config){
|
||||||
|
const std::string_view nvsName{config.nvsName()};
|
||||||
|
|
||||||
|
if (nvsName == name)
|
||||||
|
{
|
||||||
|
if (const auto result = set_config(config, value); !result)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "set_config() failed with %s", result.error().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "set_config() failed with %s", "unknown config");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
send_single_config(name);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (type == "resetConfig")
|
||||||
|
{
|
||||||
|
std::string name = doc["nvskey"];
|
||||||
|
doc.clear();
|
||||||
|
bool success{false};
|
||||||
|
configs.callForEveryConfig([&](auto &config){
|
||||||
|
const std::string_view nvsName{config.nvsName()};
|
||||||
|
|
||||||
|
if (nvsName == name)
|
||||||
|
{
|
||||||
|
if (const auto result = configs.reset_config(config); !result)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "reset_config() failed with %s", result.error().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "reset_config() failed with %s", "unknown config");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
send_single_config(name, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == "getInformation")
|
||||||
|
{
|
||||||
|
send_information();
|
||||||
|
}
|
||||||
|
else if (type == "getUptime")
|
||||||
|
{
|
||||||
|
send_uptime();
|
||||||
|
}
|
||||||
|
else if (type == "getOtaStatus")
|
||||||
|
{
|
||||||
|
send_ota_status();
|
||||||
|
}
|
||||||
|
else if (type == "rawBtnPrssd")
|
||||||
|
{
|
||||||
|
int8_t button;
|
||||||
|
JsonVariant btn_id = doc["btn"];
|
||||||
|
if (btn_id.isNull())
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "btnPressed: btn is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto parsed = cpputils::fromString<decltype(button)>(btn_id.as<std::string>()))
|
||||||
|
{
|
||||||
|
button = *parsed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "btnPressed: btn is not a number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!espgui::currentDisplay)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "espgui::currentDisplay is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rawButtonRequest = button;
|
||||||
|
}
|
||||||
|
else if (type == "btnPressed")
|
||||||
|
{
|
||||||
|
int8_t button;
|
||||||
|
JsonVariant btn_id = doc["btn"];
|
||||||
|
if (btn_id.isNull())
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "btnPressed: btn is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto parsed = cpputils::fromString<decltype(button)>(btn_id.as<std::string>()))
|
||||||
|
{
|
||||||
|
button = *parsed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "btnPressed: btn is not a number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!espgui::currentDisplay)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "espgui::currentDisplay is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonRequest = button;
|
||||||
|
}
|
||||||
|
else if (type == "initScreen")
|
||||||
|
{
|
||||||
|
initScreenRequest = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "unknown type: %s", type.c_str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WEBSOCKET_EVENT_ERROR:
|
||||||
|
ESP_LOGE(TAG, "%s event_id=%s %.*s", event_base, "WEBSOCKET_EVENT_ERROR", data->data_len, data->data_ptr);
|
||||||
|
break;
|
||||||
|
case WEBSOCKET_EVENT_CLOSED:
|
||||||
|
ESP_LOGE(TAG, "%s event_id=%s %.*s", event_base, "WEBSOCKET_EVENT_CLOSED", data->data_len, data->data_ptr);
|
||||||
|
hasAnnouncedItself = false;
|
||||||
|
cloudStarted = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_LOGI(TAG, "%s unknown event_id %i", event_base, event_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void createCloud()
|
void createCloud()
|
||||||
{
|
{
|
||||||
|
hasAnnouncedItself = false;
|
||||||
ESP_LOGI("BOBBY", "called");
|
ESP_LOGI("BOBBY", "called");
|
||||||
|
|
||||||
if (cloudClient)
|
if (cloudClient)
|
||||||
@@ -234,11 +1014,21 @@ void createCloud()
|
|||||||
lastCreateTry = espchrono::millis_clock::now();
|
lastCreateTry = espchrono::millis_clock::now();
|
||||||
|
|
||||||
const esp_websocket_client_config_t config = {
|
const esp_websocket_client_config_t config = {
|
||||||
.uri = configs.cloudUrl.value().c_str(),
|
.uri = configs.cloudUrl.value().c_str()
|
||||||
};
|
};
|
||||||
|
|
||||||
cloudClient = espcpputils::websocket_client{&config};
|
cloudClient = espcpputils::websocket_client{&config};
|
||||||
|
|
||||||
|
cloudClient.register_events(WEBSOCKET_EVENT_CONNECTED, [](void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
|
||||||
|
hasAnnouncedItself = false;
|
||||||
|
}, nullptr);
|
||||||
|
|
||||||
|
if (const auto result = cloudClient.register_events(WEBSOCKET_EVENT_ANY, &cloudEventHandler, nullptr); result != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "register_events() failed with %s", esp_err_to_name(result));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!cloudClient)
|
if (!cloudClient)
|
||||||
{
|
{
|
||||||
ESP_LOGE(TAG, "websocket could not be constructed");
|
ESP_LOGE(TAG, "websocket could not be constructed");
|
||||||
@@ -250,6 +1040,7 @@ void createCloud()
|
|||||||
|
|
||||||
void startCloud()
|
void startCloud()
|
||||||
{
|
{
|
||||||
|
hasAnnouncedItself = false;
|
||||||
ESP_LOGI("BOBBY", "called");
|
ESP_LOGI("BOBBY", "called");
|
||||||
|
|
||||||
if (!cloudClient)
|
if (!cloudClient)
|
||||||
@@ -275,6 +1066,7 @@ void startCloud()
|
|||||||
|
|
||||||
void destroyCloud()
|
void destroyCloud()
|
||||||
{
|
{
|
||||||
|
hasAnnouncedItself = false;
|
||||||
ESP_LOGI("BOBBY", "called");
|
ESP_LOGI("BOBBY", "called");
|
||||||
|
|
||||||
if (!cloudClient)
|
if (!cloudClient)
|
||||||
|
@@ -7,6 +7,9 @@
|
|||||||
#include <wrappers/websocket_client.h>
|
#include <wrappers/websocket_client.h>
|
||||||
#include <espchrono.h>
|
#include <espchrono.h>
|
||||||
|
|
||||||
|
// local includes
|
||||||
|
#include "bobbytypesafeenum.h"
|
||||||
|
|
||||||
extern espcpputils::websocket_client cloudClient;
|
extern espcpputils::websocket_client cloudClient;
|
||||||
extern bool cloudStarted;
|
extern bool cloudStarted;
|
||||||
extern espchrono::millis_clock::time_point lastCreateTry;
|
extern espchrono::millis_clock::time_point lastCreateTry;
|
||||||
@@ -21,3 +24,5 @@ void initCloud();
|
|||||||
void updateCloud();
|
void updateCloud();
|
||||||
void cloudCollect();
|
void cloudCollect();
|
||||||
void cloudSend();
|
void cloudSend();
|
||||||
|
|
||||||
|
std::string getLoginMessage();
|
||||||
|
@@ -6,12 +6,13 @@
|
|||||||
|
|
||||||
// local includes
|
// local includes
|
||||||
#include "battery.h"
|
#include "battery.h"
|
||||||
#include "ledstrip.h"
|
|
||||||
#include "handbremse.h"
|
|
||||||
#include "bobbyquickactions.h"
|
#include "bobbyquickactions.h"
|
||||||
|
#include "cloud.h"
|
||||||
|
#include "handbremse.h"
|
||||||
|
#include "ledstrip.h"
|
||||||
|
|
||||||
IMPLEMENT_NVS_GET_SET_ENUM(OtaAnimationModes)
|
IMPLEMENT_NVS_GET_SET_ENUM(BatteryCellType)
|
||||||
|
IMPLEMENT_NVS_GET_SET_ENUM(BobbyQuickActions)
|
||||||
IMPLEMENT_NVS_GET_SET_ENUM(HandbremseMode)
|
IMPLEMENT_NVS_GET_SET_ENUM(HandbremseMode)
|
||||||
IMPLEMENT_NVS_GET_SET_ENUM(LedstripAnimation)
|
IMPLEMENT_NVS_GET_SET_ENUM(LedstripAnimation)
|
||||||
IMPLEMENT_NVS_GET_SET_ENUM(BobbyQuickActions)
|
IMPLEMENT_NVS_GET_SET_ENUM(OtaAnimationModes)
|
||||||
IMPLEMENT_NVS_GET_SET_ENUM(BatteryCellType)
|
|
||||||
|
@@ -4,8 +4,8 @@
|
|||||||
#define CONFIGWRAPPER_TOSTRING_USINGS using ::toString;
|
#define CONFIGWRAPPER_TOSTRING_USINGS using ::toString;
|
||||||
#include <configwrapper_priv.h>
|
#include <configwrapper_priv.h>
|
||||||
|
|
||||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(OtaAnimationModes)
|
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(BatteryCellType)
|
||||||
|
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(BobbyQuickActions)
|
||||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(HandbremseMode)
|
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(HandbremseMode)
|
||||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(LedstripAnimation)
|
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(LedstripAnimation)
|
||||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(BobbyQuickActions)
|
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(OtaAnimationModes)
|
||||||
INSTANTIATE_CONFIGWRAPPER_TEMPLATES(BatteryCellType)
|
|
||||||
|
@@ -10,17 +10,19 @@
|
|||||||
#include "icons/back.h"
|
#include "icons/back.h"
|
||||||
|
|
||||||
// local includes
|
// local includes
|
||||||
#include "displays/bobbychangevaluedisplay.h"
|
|
||||||
#include "cloudtexthelpers.h"
|
|
||||||
#include "accessors/settingsaccessors.h"
|
#include "accessors/settingsaccessors.h"
|
||||||
#include "cloud.h"
|
|
||||||
#include "displays/menus/settingsmenu.h"
|
|
||||||
#include "bobbycheckbox.h"
|
#include "bobbycheckbox.h"
|
||||||
|
#include "cloud.h"
|
||||||
|
#include "cloudtexthelpers.h"
|
||||||
|
#include "displays/bobbychangevaluedisplay.h"
|
||||||
|
#include "displays/menus/settingsmenu.h"
|
||||||
|
#include "displays/menus/typesafeenumchangemenu.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
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_SENDSTATISTICS[] = "Send Statistics";
|
||||||
constexpr char TEXT_CLOUDCOLLECTRATE[] = "Cloud collect rate";
|
constexpr char TEXT_CLOUDCOLLECTRATE[] = "Cloud collect rate";
|
||||||
constexpr char TEXT_CLOUDSENDRATE[] = "Cloud send rate";
|
constexpr char TEXT_CLOUDSENDRATE[] = "Cloud send rate";
|
||||||
constexpr char TEXT_BACK[] = "Back";
|
constexpr char TEXT_BACK[] = "Back";
|
||||||
@@ -65,13 +67,14 @@ CloudSettingsMenu::CloudSettingsMenu()
|
|||||||
{
|
{
|
||||||
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_CLOUDENABLED>, BobbyCheckbox, CloudEnabledAccessor>>();
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_CLOUDENABLED>, BobbyCheckbox, CloudEnabledAccessor>>();
|
||||||
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_CLOUDTRANSMITTIMEOUT>, PushScreenAction<CloudTransmitTimeoutChangeScreen>>>();
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_CLOUDTRANSMITTIMEOUT>, PushScreenAction<CloudTransmitTimeoutChangeScreen>>>();
|
||||||
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_SENDSTATISTICS>, BobbyCheckbox, CloudSendStatisticsAccessor>>();
|
||||||
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, CloudBufferLengthText, DisabledColor, DummyAction>>();
|
||||||
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_CLOUDCOLLECTRATE>, PushScreenAction<CloudCollectRateChangeDisplay>>>();
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_CLOUDCOLLECTRATE>, PushScreenAction<CloudCollectRateChangeDisplay>>>();
|
||||||
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_CLOUDSENDRATE>, PushScreenAction<CloudSendRateChangeDisplay>>>();
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_CLOUDSENDRATE>, PushScreenAction<CloudSendRateChangeDisplay>>>();
|
||||||
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, PopScreenAction, StaticMenuItemIcon<&espgui::icons::back>>>();
|
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, PushScreenAction<SettingsMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CloudSettingsMenu::text() const
|
std::string CloudSettingsMenu::text() const
|
||||||
|
@@ -22,7 +22,7 @@ namespace {
|
|||||||
|
|
||||||
class MainMenu : public bobbygui::MenuDisplayWithTime
|
class MainMenu : public bobbygui::MenuDisplayWithTime
|
||||||
{
|
{
|
||||||
using Base = espgui::MenuDisplay;
|
using Base = bobbygui::MenuDisplayWithTime;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MainMenu();
|
MainMenu();
|
||||||
|
@@ -96,6 +96,8 @@ void update()
|
|||||||
debounceBack = now;
|
debounceBack = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr const uint8_t BUTTON_COUNT = 4;
|
||||||
#endif
|
#endif
|
||||||
}
|
} // namespace dpad
|
||||||
}
|
} // namespace
|
||||||
|
@@ -111,6 +111,7 @@ void update()
|
|||||||
debounceBack = now;
|
debounceBack = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
constexpr const uint8_t BUTTON_COUNT = 4;
|
||||||
#endif
|
#endif
|
||||||
}
|
} // namespace dpad3wire
|
||||||
}
|
} // namespace
|
||||||
|
@@ -1,8 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// system includes
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#ifdef FEATURE_DPAD_5WIRESW
|
#ifdef FEATURE_DPAD_5WIRESW
|
||||||
namespace dpad5wire {
|
namespace dpad5wire {
|
||||||
void init();
|
void init();
|
||||||
void update();
|
void update();
|
||||||
|
constexpr const uint8_t BUTTON_COUNT = 8;
|
||||||
} // namespace dpad5wire
|
} // namespace dpad5wire
|
||||||
#endif
|
#endif
|
||||||
|
@@ -1,8 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// system includes
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#ifdef FEATURE_DPAD_5WIRESW_2OUT
|
#ifdef FEATURE_DPAD_5WIRESW_2OUT
|
||||||
namespace dpad5wire_2out {
|
namespace dpad5wire_2out {
|
||||||
void init();
|
void init();
|
||||||
void update();
|
void update();
|
||||||
|
constexpr const uint8_t BUTTON_COUNT = 12;
|
||||||
} // namespace dpad5wire_2out
|
} // namespace dpad5wire_2out
|
||||||
#endif
|
#endif
|
||||||
|
@@ -1,8 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// system includes
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#ifdef FEATURE_DPAD_6WIRESW
|
#ifdef FEATURE_DPAD_6WIRESW
|
||||||
namespace dpad6wire {
|
namespace dpad6wire {
|
||||||
void init();
|
void init();
|
||||||
void update();
|
void update();
|
||||||
|
constexpr const uint8_t BUTTON_COUNT = 10;
|
||||||
} // namespace dpad6wire
|
} // namespace dpad6wire
|
||||||
#endif
|
#endif
|
||||||
|
@@ -1,8 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// system includes
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#ifdef DPAD_BOARDCOMPUTER_V2
|
#ifdef DPAD_BOARDCOMPUTER_V2
|
||||||
namespace dpad_boardcomputer_v2 {
|
namespace dpad_boardcomputer_v2 {
|
||||||
void init();
|
void init();
|
||||||
void update();
|
void update();
|
||||||
|
constexpr const uint8_t BUTTON_COUNT = 16;
|
||||||
} // namespace dpad_boardcomputer_v2
|
} // namespace dpad_boardcomputer_v2
|
||||||
#endif
|
#endif
|
||||||
|
@@ -39,6 +39,10 @@ bool simplified =
|
|||||||
ProfileSettings profileSettings;
|
ProfileSettings profileSettings;
|
||||||
SettingsPersister settingsPersister;
|
SettingsPersister settingsPersister;
|
||||||
|
|
||||||
|
std::atomic<int8_t> rawButtonRequest;
|
||||||
|
std::atomic<int8_t> buttonRequest;
|
||||||
|
bool initScreenRequest{false};
|
||||||
|
|
||||||
Controllers controllers;
|
Controllers controllers;
|
||||||
|
|
||||||
#ifdef FEATURE_BLUETOOTH
|
#ifdef FEATURE_BLUETOOTH
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
// system includes
|
// system includes
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
@@ -25,11 +26,11 @@
|
|||||||
// local includes
|
// local includes
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include "modeinterface.h"
|
|
||||||
#include "profilesettings.h"
|
|
||||||
#include "newsettings.h"
|
|
||||||
#include "settingspersister.h"
|
|
||||||
#include "macros_bobbycar.h"
|
#include "macros_bobbycar.h"
|
||||||
|
#include "modeinterface.h"
|
||||||
|
#include "newsettings.h"
|
||||||
|
#include "profilesettings.h"
|
||||||
|
#include "settingspersister.h"
|
||||||
|
|
||||||
extern std::optional<int16_t> raw_gas, raw_brems;
|
extern std::optional<int16_t> raw_gas, raw_brems;
|
||||||
extern std::optional<float> gas, brems;
|
extern std::optional<float> gas, brems;
|
||||||
@@ -66,6 +67,10 @@ extern bool simplified;
|
|||||||
extern ProfileSettings profileSettings;
|
extern ProfileSettings profileSettings;
|
||||||
extern SettingsPersister settingsPersister;
|
extern SettingsPersister settingsPersister;
|
||||||
|
|
||||||
|
extern std::atomic<int8_t> rawButtonRequest;
|
||||||
|
extern std::atomic<int8_t> buttonRequest;
|
||||||
|
extern bool initScreenRequest;
|
||||||
|
|
||||||
class Controllers : public std::array<Controller, 2>
|
class Controllers : public std::array<Controller, 2>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@@ -27,6 +27,10 @@
|
|||||||
#include "handbremse.h"
|
#include "handbremse.h"
|
||||||
#include "ledstrip.h"
|
#include "ledstrip.h"
|
||||||
#include "unifiedmodelmode.h"
|
#include "unifiedmodelmode.h"
|
||||||
|
#include "displays/lockscreen.h"
|
||||||
|
#include "handbremse.h"
|
||||||
|
#include "bobbyquickactions.h"
|
||||||
|
#include "cloud.h"
|
||||||
|
|
||||||
using namespace espconfig;
|
using namespace espconfig;
|
||||||
|
|
||||||
@@ -197,6 +201,11 @@ public:
|
|||||||
url {std::string{}, DoReset, StringOr<StringEmpty, StringValidUrl>, urlKey }
|
url {std::string{}, DoReset, StringOr<StringEmpty, StringValidUrl>, urlKey }
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
ConfiguredOtaServer(const char *nameKey, const char *urlKey, const char *default_name, const char *default_url) :
|
||||||
|
name{default_name, DoReset, {}, nameKey },
|
||||||
|
url {default_url, DoReset, StringOr<StringEmpty, StringValidUrl>, urlKey }
|
||||||
|
{}
|
||||||
|
|
||||||
ConfigWrapperLegacy<std::string> name;
|
ConfigWrapperLegacy<std::string> name;
|
||||||
ConfigWrapperLegacy<std::string> url;
|
ConfigWrapperLegacy<std::string> url;
|
||||||
};
|
};
|
||||||
@@ -265,12 +274,12 @@ public:
|
|||||||
ConfigWrapperLegacy<int16_t> gasMax {4095, DoReset, MinMaxValue<int16_t, 0, 4095>, "gasMax" };
|
ConfigWrapperLegacy<int16_t> gasMax {4095, DoReset, MinMaxValue<int16_t, 0, 4095>, "gasMax" };
|
||||||
ConfigWrapperLegacy<int16_t> gasMitte {2048, DoReset, MinMaxValue<int16_t, 0, 4095>, "gasMiddle" };
|
ConfigWrapperLegacy<int16_t> gasMitte {2048, DoReset, MinMaxValue<int16_t, 0, 4095>, "gasMiddle" };
|
||||||
ConfigWrapperLegacy<int16_t> bremsMin {0, DoReset, MinMaxValue<int16_t, 0, 4095>, "bremsMin" };
|
ConfigWrapperLegacy<int16_t> bremsMin {0, DoReset, MinMaxValue<int16_t, 0, 4095>, "bremsMin" };
|
||||||
ConfigWrapperLegacy<int16_t> bremsMax {4096, DoReset, MinMaxValue<int16_t, 0, 4095>, "bremsMax" };
|
ConfigWrapperLegacy<int16_t> bremsMax {4095, DoReset, MinMaxValue<int16_t, 0, 4095>, "bremsMax" };
|
||||||
ConfigWrapperLegacy<int16_t> bremsMitte {2048, DoReset, MinMaxValue<int16_t, 0, 4095>, "bremsMiddle" };
|
ConfigWrapperLegacy<int16_t> bremsMitte {2048, DoReset, MinMaxValue<int16_t, 0, 4095>, "bremsMiddle" };
|
||||||
ConfigWrapperLegacy<uint16_t> deadband {20, DoReset, MinMaxValue<uint16_t, 0, 4095>,"deadband" };
|
ConfigWrapperLegacy<uint16_t> deadband {20, DoReset, MinMaxValue<uint16_t, 0, 4095>,"deadband" };
|
||||||
|
|
||||||
ConfigWrapperLegacy<uint8_t> dpadDebounce {25, DoReset, {}, "dpadDebounce" };
|
ConfigWrapperLegacy<uint8_t> dpadDebounce {25, DoReset, {}, "dpadDebounce" };
|
||||||
ConfigWrapperLegacy<uint16_t> buttonReadDelay {1, DoReset, {}, "buttonDelay" };
|
ConfigWrapperLegacy<uint16_t> buttonReadDelay {20, DoReset, {}, "buttonDelay" };
|
||||||
|
|
||||||
ConfigWrapperLegacy<uint8_t> dpadMappingLeft {INPUT_MAPPING_NONE, DoReset, {}, "dpadMapLeft" };
|
ConfigWrapperLegacy<uint8_t> dpadMappingLeft {INPUT_MAPPING_NONE, DoReset, {}, "dpadMapLeft" };
|
||||||
ConfigWrapperLegacy<uint8_t> dpadMappingRight {INPUT_MAPPING_NONE, DoReset, {}, "dpadMapRight" };
|
ConfigWrapperLegacy<uint8_t> dpadMappingRight {INPUT_MAPPING_NONE, DoReset, {}, "dpadMapRight" };
|
||||||
@@ -314,15 +323,15 @@ public:
|
|||||||
ConfigWrapperLegacy<int16_t> reverseBeepDuration0{500, DoReset, {}, "revBeepDur0" };
|
ConfigWrapperLegacy<int16_t> reverseBeepDuration0{500, DoReset, {}, "revBeepDur0" };
|
||||||
ConfigWrapperLegacy<int16_t> reverseBeepDuration1{500, DoReset, {}, "revBeepDur1" };
|
ConfigWrapperLegacy<int16_t> reverseBeepDuration1{500, DoReset, {}, "revBeepDur1" };
|
||||||
|
|
||||||
ConfigWrapperLegacy<std::string> cloudUrl {std::string{}, DoReset, StringOr<StringEmpty, StringValidUrl>, "cloudUrl" };
|
ConfigWrapperLegacy<std::string> cloudUrl {"ws://api.bobbycar.cloud/ws", DoReset, StringOr<StringEmpty, StringValidUrl>, "cloudUrl" };
|
||||||
ConfigWrapperLegacy<std::string> udpCloudHost {std::string{}, DoReset, {}, "udpCloudHost" };
|
ConfigWrapperLegacy<std::string> udpCloudHost {"updates.bobbycar.cloud", DoReset, {}, "udpCloudHost" };
|
||||||
|
|
||||||
ConfigWrapperLegacy<std::string> otaUrl {std::string{}, DoReset, StringOr<StringEmpty, StringValidUrl>, "otaUrl" };
|
ConfigWrapperLegacy<std::string> otaUrl {std::string{}, DoReset, StringOr<StringEmpty, StringValidUrl>, "otaUrl" };
|
||||||
ConfigWrapperLegacy<std::string> otaUsername {std::string{BOBBY_DEFAULT_OTA_NAME}, DoReset, {}, "otaUsername" };
|
ConfigWrapperLegacy<std::string> otaUsername {std::string{BOBBY_DEFAULT_OTA_NAME}, DoReset, {}, "otaUsername" };
|
||||||
ConfigWrapperLegacy<std::string> otaServerUrl {std::string{}, DoReset, StringOr<StringEmpty, StringValidUrl>, "otaServerUrl" };
|
ConfigWrapperLegacy<std::string> otaServerUrl {std::string{}, DoReset, StringOr<StringEmpty, StringValidUrl>, "otaServerUrl" };
|
||||||
ConfigWrapperLegacy<std::string> otaServerBranch {std::string{}, DoReset, {}, "otaServerBranch" };
|
ConfigWrapperLegacy<std::string> otaServerBranch {std::string{}, DoReset, {}, "otaServerBranch" };
|
||||||
std::array<ConfiguredOtaServer, 5> otaServers {
|
std::array<ConfiguredOtaServer, 5> otaServers {
|
||||||
ConfiguredOtaServer { "otaName0", "otaUrl0" },
|
ConfiguredOtaServer { "otaName0", "otaUrl0", "bobbycloud", "http://updates.bobbycar.cloud" },
|
||||||
ConfiguredOtaServer { "otaName1", "otaUrl1" },
|
ConfiguredOtaServer { "otaName1", "otaUrl1" },
|
||||||
ConfiguredOtaServer { "otaName2", "otaUrl2" },
|
ConfiguredOtaServer { "otaName2", "otaUrl2" },
|
||||||
ConfiguredOtaServer { "otaName3", "otaUrl3" },
|
ConfiguredOtaServer { "otaName3", "otaUrl3" },
|
||||||
@@ -331,7 +340,7 @@ public:
|
|||||||
|
|
||||||
ConfigWrapperLegacy<bool> dns_announce_enabled{true, DoReset, {}, "dnsAnnounceEnab" };
|
ConfigWrapperLegacy<bool> dns_announce_enabled{true, DoReset, {}, "dnsAnnounceEnab" };
|
||||||
ConfigWrapperLegacy<std::string> dns_announce_key {std::string{}, DoReset, {}, "dnsAnnounceKey" };
|
ConfigWrapperLegacy<std::string> dns_announce_key {std::string{}, DoReset, {}, "dnsAnnounceKey" };
|
||||||
ConfigWrapperLegacy<std::string> webserverPassword {std::string{}, DoReset, {}, "websPassword" };
|
ConfigWrapperLegacy<std::string> webserverPassword {"Passwort_123", DoReset, {}, "websPassword" };
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
ConfigWrapperLegacy<int16_t> wheelDiameter {DEFAULT_WHEELDIAMETER, DoReset, {}, "wheelDiameter" };
|
ConfigWrapperLegacy<int16_t> wheelDiameter {DEFAULT_WHEELDIAMETER, DoReset, {}, "wheelDiameter" };
|
||||||
@@ -361,6 +370,8 @@ public:
|
|||||||
struct {
|
struct {
|
||||||
ConfigWrapperLegacy<bool> cloudEnabled {false, DoReset, {}, "cloudEnabled" };
|
ConfigWrapperLegacy<bool> cloudEnabled {false, DoReset, {}, "cloudEnabled" };
|
||||||
ConfigWrapperLegacy<int16_t> cloudTransmitTimeout{10, DoReset, {}, "clodTransmTmout" };
|
ConfigWrapperLegacy<int16_t> cloudTransmitTimeout{10, DoReset, {}, "clodTransmTmout" };
|
||||||
|
ConfigWrapperLegacy<std::string> cloudKey {std::string{}, DoReset, {}, "cloudKey" };
|
||||||
|
ConfigWrapperLegacy<bool> sendStatistic {false, DoReset, {}, "cloudSendStats" };
|
||||||
} cloudSettings;
|
} cloudSettings;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@@ -702,6 +713,8 @@ public:
|
|||||||
\
|
\
|
||||||
x(cloudSettings.cloudEnabled) \
|
x(cloudSettings.cloudEnabled) \
|
||||||
x(cloudSettings.cloudTransmitTimeout) \
|
x(cloudSettings.cloudTransmitTimeout) \
|
||||||
|
x(cloudSettings.cloudKey) \
|
||||||
|
x(cloudSettings.sendStatistic) \
|
||||||
\
|
\
|
||||||
x(udpCloudSettings.udpUid) \
|
x(udpCloudSettings.udpUid) \
|
||||||
x(udpCloudSettings.udpCloudEnabled) \
|
x(udpCloudSettings.udpCloudEnabled) \
|
||||||
@@ -839,6 +852,15 @@ public:
|
|||||||
std::ref<ConfiguredFeatureFlag>(feature.webserver_disable_lock)
|
std::ref<ConfiguredFeatureFlag>(feature.webserver_disable_lock)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr size_t getConfigCount() const
|
||||||
|
{
|
||||||
|
size_t count = 0;
|
||||||
|
#define HELPER(x) count++;
|
||||||
|
NEW_SETTINGS(HELPER)
|
||||||
|
#undef HELPER
|
||||||
|
return count;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
extern ConfigManager<ConfigContainer> configs;
|
extern ConfigManager<ConfigContainer> configs;
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
// local includes
|
// local includes
|
||||||
#include "esptexthelpers.h"
|
#include "esptexthelpers.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "utils.h"
|
|
||||||
#include "icons/logo.h"
|
#include "icons/logo.h"
|
||||||
|
|
||||||
using namespace espgui;
|
using namespace espgui;
|
||||||
@@ -42,6 +41,27 @@ void updateDisplay()
|
|||||||
changeScreenCallback();
|
changeScreenCallback();
|
||||||
changeScreenCallback = {};
|
changeScreenCallback = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const int8_t rawButton = rawButtonRequest.load(); rawButton != -1 && currentDisplay)
|
||||||
|
{
|
||||||
|
currentDisplay->rawButtonPressed(rawButton);
|
||||||
|
currentDisplay->rawButtonReleased(rawButton);
|
||||||
|
rawButtonRequest = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const int8_t button = buttonRequest.load(); button != -1 && currentDisplay)
|
||||||
|
{
|
||||||
|
const auto btn = espgui::Button(button);
|
||||||
|
currentDisplay->buttonPressed(btn);
|
||||||
|
currentDisplay->buttonReleased(btn);
|
||||||
|
buttonRequest = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (initScreenRequest && currentDisplay)
|
||||||
|
{
|
||||||
|
currentDisplay->initScreen();
|
||||||
|
initScreenRequest = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void redrawDisplay()
|
void redrawDisplay()
|
||||||
|
@@ -10,22 +10,6 @@
|
|||||||
|
|
||||||
// local includes
|
// local includes
|
||||||
#include "wifi_bobbycar.h"
|
#include "wifi_bobbycar.h"
|
||||||
#include "dpad.h"
|
|
||||||
#ifdef FEATURE_DPAD_3WIRESW
|
|
||||||
#include "dpad3wire.h"
|
|
||||||
#endif
|
|
||||||
#ifdef FEATURE_DPAD_5WIRESW
|
|
||||||
#include "dpad5wire.h"
|
|
||||||
#endif
|
|
||||||
#ifdef FEATURE_DPAD_5WIRESW_2OUT
|
|
||||||
#include "dpad5wire_2out.h"
|
|
||||||
#endif
|
|
||||||
#ifdef FEATURE_DPAD_6WIRESW
|
|
||||||
#include "dpad6wire.h"
|
|
||||||
#endif
|
|
||||||
#ifdef DPAD_BOARDCOMPUTER_V2
|
|
||||||
#include "dpad_boardcomputer_v2.h"
|
|
||||||
#endif
|
|
||||||
#ifdef FEATURE_ROTARY
|
#ifdef FEATURE_ROTARY
|
||||||
#include "rotary.h"
|
#include "rotary.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -58,6 +42,7 @@
|
|||||||
#include "drivingstatistics.h"
|
#include "drivingstatistics.h"
|
||||||
#include "dnsannounce.h"
|
#include "dnsannounce.h"
|
||||||
#include "screens.h"
|
#include "screens.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
@@ -68,23 +53,8 @@ void not_needed() {}
|
|||||||
|
|
||||||
BobbySchedulerTask schedulerTasksArr[] {
|
BobbySchedulerTask schedulerTasksArr[] {
|
||||||
BobbySchedulerTask { "wifi", wifi_begin, wifi_update, 100ms },
|
BobbySchedulerTask { "wifi", wifi_begin, wifi_update, 100ms },
|
||||||
#ifdef FEATURE_DPAD
|
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW) || defined(FEATURE_DPAD_5WIRESW) || defined(FEATURE_DPAD_5WIRESW_2OUT) || defined(FEATURE_DPAD_6WIRESW) || defined(DPAD_BOARDCOMPUTER_V2)
|
||||||
BobbySchedulerTask { "dpad", dpad::init, dpad::update, 20ms },
|
BobbySchedulerTask { bobbydpad::dpad_name, bobbydpad::dpad_init, bobbydpad::dpad_update, 20ms },
|
||||||
#endif
|
|
||||||
#ifdef FEATURE_DPAD_3WIRESW
|
|
||||||
BobbySchedulerTask { "dpad3wire", dpad3wire::init, dpad3wire::update, 20ms },
|
|
||||||
#endif
|
|
||||||
#ifdef FEATURE_DPAD_5WIRESW
|
|
||||||
BobbySchedulerTask { "dpad5wire", dpad5wire::init, dpad5wire::update, 20ms },
|
|
||||||
#endif
|
|
||||||
#ifdef FEATURE_DPAD_5WIRESW_2OUT
|
|
||||||
BobbySchedulerTask { "dpad5wire_2out", dpad5wire_2out::init, dpad5wire_2out::update, 20ms },
|
|
||||||
#endif
|
|
||||||
#ifdef FEATURE_DPAD_6WIRESW
|
|
||||||
BobbySchedulerTask { "dpad6wire", dpad6wire::init, dpad6wire::update, 20ms },
|
|
||||||
#endif
|
|
||||||
#ifdef DPAD_BOARDCOMPUTER_V2
|
|
||||||
BobbySchedulerTask { "dpad_boardcomputer_v2", dpad_boardcomputer_v2::init, dpad_boardcomputer_v2::update, 20ms },
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef FEATURE_ROTARY
|
#ifdef FEATURE_ROTARY
|
||||||
BobbySchedulerTask { "rotary", initRotary, updateRotary, 20ms },
|
BobbySchedulerTask { "rotary", initRotary, updateRotary, 20ms },
|
||||||
|
53
main/typeutils.h
Normal file
53
main/typeutils.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace typeutils {
|
||||||
|
using namespace std;
|
||||||
|
template<typename T>
|
||||||
|
struct t_to_str
|
||||||
|
{
|
||||||
|
t_to_str() = delete;
|
||||||
|
~t_to_str() = delete;
|
||||||
|
|
||||||
|
static const char *str;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const char *t_to_str<T>::str = "unknown";
|
||||||
|
|
||||||
|
#define DEFINE_FOR_TYPE2(TYPE, STR) \
|
||||||
|
template<> const char *t_to_str<TYPE>::str = #STR; \
|
||||||
|
template<> const char *t_to_str<TYPE const&>::str = #STR; \
|
||||||
|
template<> const char *t_to_str<std::optional<TYPE>>::str = "std::optional<" #STR ">"; \
|
||||||
|
template<> const char *t_to_str<std::optional<TYPE> const&>::str = "std::optional<" #STR ">"; \
|
||||||
|
template<> const char *t_to_str<ConfigWrapper<TYPE>>::str = #STR; \
|
||||||
|
template<> const char *t_to_str<ConfigWrapper<TYPE> const&>::str = #STR; \
|
||||||
|
template<> const char *t_to_str<ConfigWrapperLegacy<TYPE>>::str = #STR; \
|
||||||
|
template<> const char *t_to_str<ConfigWrapperLegacy<TYPE> const&>::str = #STR;
|
||||||
|
|
||||||
|
#define DEFINE_FOR_TYPE(TYPE) DEFINE_FOR_TYPE2(TYPE, TYPE)
|
||||||
|
DEFINE_FOR_TYPE(bool)
|
||||||
|
DEFINE_FOR_TYPE(int8_t)
|
||||||
|
DEFINE_FOR_TYPE(uint8_t)
|
||||||
|
DEFINE_FOR_TYPE(int16_t)
|
||||||
|
DEFINE_FOR_TYPE(uint16_t)
|
||||||
|
DEFINE_FOR_TYPE(int32_t)
|
||||||
|
DEFINE_FOR_TYPE(uint32_t)
|
||||||
|
DEFINE_FOR_TYPE(int64_t)
|
||||||
|
DEFINE_FOR_TYPE(uint64_t)
|
||||||
|
DEFINE_FOR_TYPE(float)
|
||||||
|
DEFINE_FOR_TYPE(double)
|
||||||
|
DEFINE_FOR_TYPE(string)
|
||||||
|
DEFINE_FOR_TYPE(sntp_sync_mode_t)
|
||||||
|
DEFINE_FOR_TYPE(espchrono::DayLightSavingMode)
|
||||||
|
DEFINE_FOR_TYPE(OtaAnimationModes)
|
||||||
|
DEFINE_FOR_TYPE(LedstripAnimation)
|
||||||
|
DEFINE_FOR_TYPE(HandbremseMode)
|
||||||
|
DEFINE_FOR_TYPE(BobbyQuickActions)
|
||||||
|
DEFINE_FOR_TYPE(BatteryCellType)
|
||||||
|
DEFINE_FOR_TYPE(wifi_auth_mode_t)
|
||||||
|
DEFINE_FOR_TYPE(wifi_stack::mac_t)
|
||||||
|
DEFINE_FOR_TYPE(wifi_stack::ip_address_t)
|
||||||
|
DEFINE_FOR_TYPE(espchrono::milliseconds32)
|
||||||
|
DEFINE_FOR_TYPE(espchrono::minutes32)
|
||||||
|
#undef DEFINE_FOR_TYPE
|
||||||
|
} // namespace typeutils
|
64
main/utils.h
64
main/utils.h
@@ -28,6 +28,25 @@
|
|||||||
#include "can.h"
|
#include "can.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef FEATURE_DPAD
|
||||||
|
#include "dpad.h"
|
||||||
|
#endif
|
||||||
|
#ifdef FEATURE_DPAD_3WIRESW
|
||||||
|
#include "dpad3wire.h"
|
||||||
|
#endif
|
||||||
|
#ifdef FEATURE_DPAD_5WIRESW
|
||||||
|
#include "dpad5wire.h"
|
||||||
|
#endif
|
||||||
|
#ifdef FEATURE_DPAD_5WIRESW_2OUT
|
||||||
|
#include "dpad5wire_2out.h"
|
||||||
|
#endif
|
||||||
|
#ifdef FEATURE_DPAD_6WIRESW
|
||||||
|
#include "dpad6wire.h"
|
||||||
|
#endif
|
||||||
|
#ifdef DPAD_BOARDCOMPUTER_V2
|
||||||
|
#include "dpad_boardcomputer_v2.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
extern bool currentlyReverseBeeping;
|
extern bool currentlyReverseBeeping;
|
||||||
extern bool reverseBeepToggle;
|
extern bool reverseBeepToggle;
|
||||||
extern espchrono::millis_clock::time_point lastReverseBeepToggle;
|
extern espchrono::millis_clock::time_point lastReverseBeepToggle;
|
||||||
@@ -80,3 +99,48 @@ inline CRGB UINT32_TO_CRGB(uint32_t color)
|
|||||||
std::string get_wifi_security_string(wifi_auth_mode_t authMode);
|
std::string get_wifi_security_string(wifi_auth_mode_t authMode);
|
||||||
float float_map(float x, float in_min, float in_max, float out_min, float out_max);
|
float float_map(float x, float in_min, float in_max, float out_min, float out_max);
|
||||||
bool is_valid_timestamp(espchrono::utc_clock::time_point timestamp);
|
bool is_valid_timestamp(espchrono::utc_clock::time_point timestamp);
|
||||||
|
|
||||||
|
namespace bobbydpad {
|
||||||
|
#ifdef FEATURE_DPAD
|
||||||
|
static constexpr const auto ButtonCount = dpad::BUTTON_COUNT;
|
||||||
|
static constexpr const auto &dpad_init = dpad::init;
|
||||||
|
static constexpr const auto &dpad_update = dpad::update;
|
||||||
|
static constexpr const char * const dpad_name = "dpad";
|
||||||
|
#endif
|
||||||
|
#ifdef FEATURE_DPAD_3WIRESW
|
||||||
|
static constexpr const auto ButtonCount = dpad3wire::BUTTON_COUNT;
|
||||||
|
static constexpr const auto &dpad_init = dpad3wire::init;
|
||||||
|
static constexpr const auto &dpad_update = dpad3wire::update;
|
||||||
|
static constexpr const char * const dpad_name = "dpad3wire";
|
||||||
|
#endif
|
||||||
|
#ifdef FEATURE_DPAD_5WIRESW
|
||||||
|
static constexpr const auto ButtonCount = dpad5wire::BUTTON_COUNT;
|
||||||
|
static constexpr const auto &dpad_init = dpad5wire::init;
|
||||||
|
static constexpr const auto &dpad_update = dpad5wire::update;
|
||||||
|
static constexpr const char * const dpad_name = "dpad5wire";
|
||||||
|
#endif
|
||||||
|
#ifdef FEATURE_DPAD_5WIRESW_2OUT
|
||||||
|
static constexpr const auto ButtonCount = dpad5wire_2out::BUTTON_COUNT;
|
||||||
|
static constexpr const auto &dpad_init = dpad5wire_2out::init;
|
||||||
|
static constexpr const auto &dpad_update = dpad5wire_2out::update;
|
||||||
|
static constexpr const char * const dpad_name = "dpad5wire_2out";
|
||||||
|
#endif
|
||||||
|
#ifdef FEATURE_DPAD_6WIRESW
|
||||||
|
static constexpr const auto ButtonCount = dpad6wire::BUTTON_COUNT;
|
||||||
|
static constexpr const auto &dpad_init = dpad6wire::init;
|
||||||
|
static constexpr const auto &dpad_update = dpad6wire::update;
|
||||||
|
static constexpr const char * const dpad_name = "dpad6wire";
|
||||||
|
#endif
|
||||||
|
#ifdef DPAD_BOARDCOMPUTER_V2
|
||||||
|
static constexpr const auto ButtonCount = dpad_boardcomputer_v2::BUTTON_COUNT;
|
||||||
|
static constexpr const auto &dpad_init = dpad_boardcomputer_v2::init;
|
||||||
|
static constexpr const auto &dpad_update = dpad_boardcomputer_v2::update;
|
||||||
|
static constexpr const char * const dpad_name = "dpad_boardcomputer_v2";
|
||||||
|
#endif
|
||||||
|
#if !defined(FEATURE_DPAD) && !defined(FEATURE_DPAD_3WIRESW) && !defined(FEATURE_DPAD_5WIRESW) && !defined(FEATURE_DPAD_5WIRESW_2OUT) && !defined(FEATURE_DPAD_6WIRESW) && !defined(DPAD_BOARDCOMPUTER_V2)
|
||||||
|
static constexpr const auto ButtonCount = 0;
|
||||||
|
static constexpr const auto &dpad_init = nullptr;
|
||||||
|
static constexpr const auto &dpad_update = nullptr;
|
||||||
|
static constexpr const char * const dpad_name = "";
|
||||||
|
#endif
|
||||||
|
} // namespace bobbydpad
|
||||||
|
@@ -23,8 +23,9 @@
|
|||||||
|
|
||||||
// local includes
|
// local includes
|
||||||
#include "bobbybuttons.h"
|
#include "bobbybuttons.h"
|
||||||
#include "webserver_lock.h"
|
#include "globals.h"
|
||||||
#include "newsettings.h"
|
#include "newsettings.h"
|
||||||
|
#include "webserver_lock.h"
|
||||||
|
|
||||||
using esphttpdutils::HtmlTag;
|
using esphttpdutils::HtmlTag;
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
@@ -190,7 +191,11 @@ esp_err_t webserver_root_handler(httpd_req_t *req)
|
|||||||
"<a href=\"/triggerRawButton?button=8\">Button8</a> "
|
"<a href=\"/triggerRawButton?button=8\">Button8</a> "
|
||||||
"<a href=\"/triggerRawButton?button=9\">Button9</a> "
|
"<a href=\"/triggerRawButton?button=9\">Button9</a> "
|
||||||
"<a href=\"/triggerRawButton?button=10\">Button10</a> "
|
"<a href=\"/triggerRawButton?button=10\">Button10</a> "
|
||||||
"<a href=\"/triggerRawButton?button=11\">Button11</a>";
|
"<a href=\"/triggerRawButton?button=11\">Button11</a> "
|
||||||
|
"<a href=\"/triggerRawButton?button=12\">Button12</a> "
|
||||||
|
"<a href=\"/triggerRawButton?button=13\">Button13</a> "
|
||||||
|
"<a href=\"/triggerRawButton?button=14\">Button14</a> "
|
||||||
|
"<a href=\"/triggerRawButton?button=15\">Button15</a>";
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -207,7 +212,11 @@ esp_err_t webserver_root_handler(httpd_req_t *req)
|
|||||||
"<a href=\"/triggerButton?button={}\">Left2</a> "
|
"<a href=\"/triggerButton?button={}\">Left2</a> "
|
||||||
"<a href=\"/triggerButton?button={}\">Right2</a> "
|
"<a href=\"/triggerButton?button={}\">Right2</a> "
|
||||||
"<a href=\"/triggerButton?button={}\">Up2</a> "
|
"<a href=\"/triggerButton?button={}\">Up2</a> "
|
||||||
"<a href=\"/triggerButton?button={}\">Down2</a>",
|
"<a href=\"/triggerButton?button={}\">Down2</a>"
|
||||||
|
"<a href=\"/triggerButton?button={}\">Extra1</a>"
|
||||||
|
"<a href=\"/triggerButton?button={}\">Extra2</a>"
|
||||||
|
"<a href=\"/triggerButton?button={}\">Extra3</a>"
|
||||||
|
"<a href=\"/triggerButton?button={}\">Extra4</a>",
|
||||||
std::to_underlying(espgui::Button::Left),
|
std::to_underlying(espgui::Button::Left),
|
||||||
std::to_underlying(espgui::Button::Right),
|
std::to_underlying(espgui::Button::Right),
|
||||||
std::to_underlying(espgui::Button::Up),
|
std::to_underlying(espgui::Button::Up),
|
||||||
@@ -219,7 +228,11 @@ esp_err_t webserver_root_handler(httpd_req_t *req)
|
|||||||
std::to_underlying(BobbyButton::Left2),
|
std::to_underlying(BobbyButton::Left2),
|
||||||
std::to_underlying(BobbyButton::Right2),
|
std::to_underlying(BobbyButton::Right2),
|
||||||
std::to_underlying(BobbyButton::Up2),
|
std::to_underlying(BobbyButton::Up2),
|
||||||
std::to_underlying(BobbyButton::Down2));
|
std::to_underlying(BobbyButton::Down2),
|
||||||
|
std::to_underlying(BobbyButton::Extra1),
|
||||||
|
std::to_underlying(BobbyButton::Extra2),
|
||||||
|
std::to_underlying(BobbyButton::Extra3),
|
||||||
|
std::to_underlying(BobbyButton::Extra4));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto currentDisplay = static_cast<const espgui::Display *>(espgui::currentDisplay.get()))
|
if (auto currentDisplay = static_cast<const espgui::Display *>(espgui::currentDisplay.get()))
|
||||||
@@ -285,7 +298,7 @@ esp_err_t webserver_triggerRawButton_handler(httpd_req_t *req)
|
|||||||
CALL_AND_EXIT(esphttpdutils::webserver_resp_send, req, esphttpdutils::ResponseStatus::BadRequest, "text/plain", result.error());
|
CALL_AND_EXIT(esphttpdutils::webserver_resp_send, req, esphttpdutils::ResponseStatus::BadRequest, "text/plain", result.error());
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t button;
|
int8_t button;
|
||||||
constexpr const std::string_view buttonParamName{"button"};
|
constexpr const std::string_view buttonParamName{"button"};
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -323,15 +336,7 @@ esp_err_t webserver_triggerRawButton_handler(httpd_req_t *req)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!espgui::currentDisplay)
|
rawButtonRequest.store(button);
|
||||||
{
|
|
||||||
constexpr const std::string_view msg = "espgui::currentDisplay is null";
|
|
||||||
ESP_LOGW(TAG, "%.*s", msg.size(), msg.data());
|
|
||||||
CALL_AND_EXIT(esphttpdutils::webserver_resp_send, req, esphttpdutils::ResponseStatus::BadRequest, "text/plain", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
espgui::currentDisplay->rawButtonPressed(button);
|
|
||||||
espgui::currentDisplay->rawButtonReleased(button);
|
|
||||||
|
|
||||||
CALL_AND_EXIT_ON_ERROR(httpd_resp_set_hdr, req, "Location", "/")
|
CALL_AND_EXIT_ON_ERROR(httpd_resp_set_hdr, req, "Location", "/")
|
||||||
CALL_AND_EXIT_ON_ERROR(httpd_resp_set_hdr, req, "Access-Control-Allow-Origin", "http://web.bobbycar.cloud");
|
CALL_AND_EXIT_ON_ERROR(httpd_resp_set_hdr, req, "Access-Control-Allow-Origin", "http://web.bobbycar.cloud");
|
||||||
@@ -350,7 +355,7 @@ esp_err_t webserver_triggerButton_handler(httpd_req_t *req)
|
|||||||
CALL_AND_EXIT(esphttpdutils::webserver_resp_send, req, esphttpdutils::ResponseStatus::BadRequest, "text/plain", result.error());
|
CALL_AND_EXIT(esphttpdutils::webserver_resp_send, req, esphttpdutils::ResponseStatus::BadRequest, "text/plain", result.error());
|
||||||
}
|
}
|
||||||
|
|
||||||
espgui::Button button;
|
int8_t button;
|
||||||
constexpr const std::string_view buttonParamName{"button"};
|
constexpr const std::string_view buttonParamName{"button"};
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -376,9 +381,9 @@ esp_err_t webserver_triggerButton_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
std::string_view value{valueBuf};
|
std::string_view value{valueBuf};
|
||||||
|
|
||||||
if (auto parsed = cpputils::fromString<std::underlying_type_t<espgui::Button>>(value))
|
if (auto parsed = cpputils::fromString<int8_t>(value))
|
||||||
{
|
{
|
||||||
button = espgui::Button(*parsed);
|
button = *parsed;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -388,15 +393,7 @@ esp_err_t webserver_triggerButton_handler(httpd_req_t *req)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!espgui::currentDisplay)
|
buttonRequest.store(button);
|
||||||
{
|
|
||||||
constexpr const std::string_view msg = "espgui::currentDisplay is null";
|
|
||||||
ESP_LOGW(TAG, "%.*s", msg.size(), msg.data());
|
|
||||||
CALL_AND_EXIT(esphttpdutils::webserver_resp_send, req, esphttpdutils::ResponseStatus::BadRequest, "text/plain", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
espgui::currentDisplay->buttonPressed(button);
|
|
||||||
espgui::currentDisplay->buttonReleased(button);
|
|
||||||
|
|
||||||
CALL_AND_EXIT_ON_ERROR(httpd_resp_set_hdr, req, "Location", "/")
|
CALL_AND_EXIT_ON_ERROR(httpd_resp_set_hdr, req, "Location", "/")
|
||||||
CALL_AND_EXIT_ON_ERROR(httpd_resp_set_hdr, req, "Access-Control-Allow-Origin", "http://web.bobbycar.cloud");
|
CALL_AND_EXIT_ON_ERROR(httpd_resp_set_hdr, req, "Access-Control-Allow-Origin", "http://web.bobbycar.cloud");
|
||||||
|
@@ -52,7 +52,7 @@ typename std::enable_if<
|
|||||||
!std::is_same_v<T, OtaAnimationModes> &&
|
!std::is_same_v<T, OtaAnimationModes> &&
|
||||||
!std::is_same_v<T, LedstripAnimation> &&
|
!std::is_same_v<T, LedstripAnimation> &&
|
||||||
!std::is_same_v<T, HandbremseMode> &&
|
!std::is_same_v<T, HandbremseMode> &&
|
||||||
!std::is_same_v<T, BobbyQuickActions>
|
!std::is_same_v<T, BatteryCellType>
|
||||||
, void>::type
|
, void>::type
|
||||||
showInputForSetting(std::string_view key, T value, std::string &body)
|
showInputForSetting(std::string_view key, T value, std::string &body)
|
||||||
{
|
{
|
||||||
@@ -202,55 +202,16 @@ showInputForSetting(std::string_view key, T value, std::string &body)
|
|||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
std::is_same_v<T, OtaAnimationModes>
|
std::is_same_v<T, OtaAnimationModes> ||
|
||||||
, void>::type
|
std::is_same_v<T, LedstripAnimation> ||
|
||||||
showInputForSetting(std::string_view key, T value, std::string &body)
|
std::is_same_v<T, BatteryCellType> ||
|
||||||
{
|
|
||||||
HtmlTag select{"select", fmt::format("name=\"{}\"", esphttpdutils::htmlentities(key)), body};
|
|
||||||
|
|
||||||
iterateOtaAnimationModes([&](T enumVal, std::string_view enumKey){
|
|
||||||
HtmlTag option{"option", fmt::format("value=\"{}\"{}", std::to_underlying(enumVal), value == enumVal ? " selected" : ""), body};
|
|
||||||
body += esphttpdutils::htmlentities(enumKey);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same_v<T, LedstripAnimation>
|
|
||||||
, void>::type
|
|
||||||
showInputForSetting(std::string_view key, T value, std::string &body)
|
|
||||||
{
|
|
||||||
HtmlTag select{"select", fmt::format("name=\"{}\"", esphttpdutils::htmlentities(key)), body};
|
|
||||||
|
|
||||||
iterateLedstripAnimation([&](T enumVal, std::string_view enumKey){
|
|
||||||
HtmlTag option{"option", fmt::format("value=\"{}\"{}", std::to_underlying(enumVal), value == enumVal ? " selected" : ""), body};
|
|
||||||
body += esphttpdutils::htmlentities(enumKey);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same_v<T, HandbremseMode>
|
std::is_same_v<T, HandbremseMode>
|
||||||
, void>::type
|
, void>::type
|
||||||
showInputForSetting(std::string_view key, T value, std::string &body)
|
showInputForSetting(std::string_view key, T value, std::string &body)
|
||||||
{
|
{
|
||||||
HtmlTag select{"select", fmt::format("name=\"{}\"", esphttpdutils::htmlentities(key)), body};
|
HtmlTag select{"select", fmt::format("name=\"{}\"", esphttpdutils::htmlentities(key)), body};
|
||||||
|
|
||||||
iterateHandbremseMode([&](T enumVal, std::string_view enumKey){
|
iterateEnum<T>::iterate([&](T enumVal, std::string_view enumKey){
|
||||||
HtmlTag option{"option", fmt::format("value=\"{}\"{}", std::to_underlying(enumVal), value == enumVal ? " selected" : ""), body};
|
|
||||||
body += esphttpdutils::htmlentities(enumKey);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same_v<T, BobbyQuickActions>
|
|
||||||
, void>::type
|
|
||||||
showInputForSetting(std::string_view key, T value, std::string &body)
|
|
||||||
{
|
|
||||||
HtmlTag select{"select", fmt::format("name=\"{}\"", esphttpdutils::htmlentities(key)), body};
|
|
||||||
|
|
||||||
iterateBobbyQuickActions([&](T enumVal, std::string_view enumKey){
|
|
||||||
HtmlTag option{"option", fmt::format("value=\"{}\"{}", std::to_underlying(enumVal), value == enumVal ? " selected" : ""), body};
|
HtmlTag option{"option", fmt::format("value=\"{}\"{}", std::to_underlying(enumVal), value == enumVal ? " selected" : ""), body};
|
||||||
body += esphttpdutils::htmlentities(enumKey);
|
body += esphttpdutils::htmlentities(enumKey);
|
||||||
});
|
});
|
||||||
|
1850
sdkconfig_testdevice
Normal file
1850
sdkconfig_testdevice
Normal file
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,7 @@ VALID_CONFIGS=(
|
|||||||
"gernot"
|
"gernot"
|
||||||
"comred_new"
|
"comred_new"
|
||||||
"aveexy"
|
"aveexy"
|
||||||
|
"testdevice"
|
||||||
)
|
)
|
||||||
|
|
||||||
print_usage() {
|
print_usage() {
|
||||||
|
Reference in New Issue
Block a user