From 9dde0857a70571c5f6becec1424a2227a20471e9 Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Mon, 7 Oct 2024 23:42:36 +0200 Subject: [PATCH 1/7] Better gpio pin value handling (#294) * Improve gpio pin handling * Make flatbuffer gpio types signed * Fix formatting --- .clang-format | 6 +- .gitmodules | 1 + .../configuration/estop-config.ts | 2 +- .../serialization/configuration/rfconfig.ts | 2 +- .../local/set-estop-pin-command-result.ts | 2 +- .../local/set-estop-pin-command.ts | 2 +- .../local/set-rf-tx-pin-command-result.ts | 2 +- .../local/set-rf-tx-pin-command.ts | 2 +- include/Chipset.h | 44 ++-- include/CommandHandler.h | 10 +- include/Common.h | 2 +- include/Convert.h | 3 + include/config/Config.h | 4 +- include/config/RFConfig.h | 6 +- include/config/internal/utils.h | 27 ++- include/radio/RFTransmitter.h | 7 +- .../serialization/_fbs/HubConfig_generated.h | 24 +-- .../_fbs/HubToLocalMessage_generated.h | 24 +-- .../_fbs/LocalToHubMessage_generated.h | 24 +-- schemas | 2 +- src/CommandHandler.cpp | 57 ++++-- src/Convert.cpp | 136 +++++++++---- src/PinPatternManager.cpp | 30 ++- src/RGBPatternManager.cpp | 37 ++-- src/config/Config.cpp | 192 ++++++++++++------ src/config/EStopConfig.cpp | 29 +-- src/config/RFConfig.cpp | 27 ++- src/config/internal/utils.cpp | 89 ++++++-- .../websocket/local/SetEStopPinCommand.cpp | 12 +- .../websocket/local/SetRfTxPinCommand.cpp | 12 +- src/radio/RFTransmitter.cpp | 58 +++--- src/serial/command_handlers/estoppin.cpp | 13 +- src/serial/command_handlers/rftxpin.cpp | 16 +- 33 files changed, 591 insertions(+), 313 deletions(-) diff --git a/.clang-format b/.clang-format index 22be87b5..2ac8f3bf 100644 --- a/.clang-format +++ b/.clang-format @@ -67,7 +67,7 @@ AlignTrailingComments: Kind: Always OverEmptyLines: 1 AllowAllArgumentsOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: Always AllowShortCaseLabelsOnASingleLine: false AllowShortEnumsOnASingleLine: false @@ -87,7 +87,7 @@ BraceWrapping: AfterClass: false AfterControlStatement: MultiLine AfterEnum: false - AfterFunction: false + AfterFunction: true AfterNamespace: false AfterStruct: false AfterUnion: false @@ -100,7 +100,7 @@ BraceWrapping: SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true -BreakAfterAttributes: Never +BreakAfterAttributes: Leave BreakBeforeBinaryOperators: All BreakBeforeConceptDeclarations: Allowed BreakBeforeInlineASMColon: Always diff --git a/.gitmodules b/.gitmodules index dafad5fb..e4a756bb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "schemas"] path = schemas url = https://github.com/OpenShock/flatbuffers-schemas + branch = feature/better-gpio-pin-handling diff --git a/frontend/src/lib/_fbs/open-shock/serialization/configuration/estop-config.ts b/frontend/src/lib/_fbs/open-shock/serialization/configuration/estop-config.ts index 34ff30c0..b4946777 100644 --- a/frontend/src/lib/_fbs/open-shock/serialization/configuration/estop-config.ts +++ b/frontend/src/lib/_fbs/open-shock/serialization/configuration/estop-config.ts @@ -32,7 +32,7 @@ enabled():boolean { */ gpioPin():number { const offset = this.bb!.__offset(this.bb_pos, 6); - return offset ? this.bb!.readUint8(this.bb_pos + offset) : 0; + return offset ? this.bb!.readInt8(this.bb_pos + offset) : 0; } static startEStopConfig(builder:flatbuffers.Builder) { diff --git a/frontend/src/lib/_fbs/open-shock/serialization/configuration/rfconfig.ts b/frontend/src/lib/_fbs/open-shock/serialization/configuration/rfconfig.ts index 0ef62da2..f40bc3b4 100644 --- a/frontend/src/lib/_fbs/open-shock/serialization/configuration/rfconfig.ts +++ b/frontend/src/lib/_fbs/open-shock/serialization/configuration/rfconfig.ts @@ -27,7 +27,7 @@ static getSizePrefixedRootAsRFConfig(bb:flatbuffers.ByteBuffer, obj?:RFConfig):R */ txPin():number { const offset = this.bb!.__offset(this.bb_pos, 4); - return offset ? this.bb!.readUint8(this.bb_pos + offset) : 0; + return offset ? this.bb!.readInt8(this.bb_pos + offset) : 0; } /** diff --git a/frontend/src/lib/_fbs/open-shock/serialization/local/set-estop-pin-command-result.ts b/frontend/src/lib/_fbs/open-shock/serialization/local/set-estop-pin-command-result.ts index 1f8098e5..be877382 100644 --- a/frontend/src/lib/_fbs/open-shock/serialization/local/set-estop-pin-command-result.ts +++ b/frontend/src/lib/_fbs/open-shock/serialization/local/set-estop-pin-command-result.ts @@ -27,7 +27,7 @@ static getSizePrefixedRootAsSetEstopPinCommandResult(bb:flatbuffers.ByteBuffer, gpioPin():number { const offset = this.bb!.__offset(this.bb_pos, 4); - return offset ? this.bb!.readUint8(this.bb_pos + offset) : 0; + return offset ? this.bb!.readInt8(this.bb_pos + offset) : 0; } result():SetGPIOResultCode { diff --git a/frontend/src/lib/_fbs/open-shock/serialization/local/set-estop-pin-command.ts b/frontend/src/lib/_fbs/open-shock/serialization/local/set-estop-pin-command.ts index e4744e1c..f912ffa2 100644 --- a/frontend/src/lib/_fbs/open-shock/serialization/local/set-estop-pin-command.ts +++ b/frontend/src/lib/_fbs/open-shock/serialization/local/set-estop-pin-command.ts @@ -24,7 +24,7 @@ static getSizePrefixedRootAsSetEstopPinCommand(bb:flatbuffers.ByteBuffer, obj?:S pin():number { const offset = this.bb!.__offset(this.bb_pos, 4); - return offset ? this.bb!.readUint8(this.bb_pos + offset) : 0; + return offset ? this.bb!.readInt8(this.bb_pos + offset) : 0; } static startSetEstopPinCommand(builder:flatbuffers.Builder) { diff --git a/frontend/src/lib/_fbs/open-shock/serialization/local/set-rf-tx-pin-command-result.ts b/frontend/src/lib/_fbs/open-shock/serialization/local/set-rf-tx-pin-command-result.ts index befaaa4e..809147f5 100644 --- a/frontend/src/lib/_fbs/open-shock/serialization/local/set-rf-tx-pin-command-result.ts +++ b/frontend/src/lib/_fbs/open-shock/serialization/local/set-rf-tx-pin-command-result.ts @@ -27,7 +27,7 @@ static getSizePrefixedRootAsSetRfTxPinCommandResult(bb:flatbuffers.ByteBuffer, o pin():number { const offset = this.bb!.__offset(this.bb_pos, 4); - return offset ? this.bb!.readUint8(this.bb_pos + offset) : 0; + return offset ? this.bb!.readInt8(this.bb_pos + offset) : 0; } result():SetGPIOResultCode { diff --git a/frontend/src/lib/_fbs/open-shock/serialization/local/set-rf-tx-pin-command.ts b/frontend/src/lib/_fbs/open-shock/serialization/local/set-rf-tx-pin-command.ts index 18e0c948..0776b598 100644 --- a/frontend/src/lib/_fbs/open-shock/serialization/local/set-rf-tx-pin-command.ts +++ b/frontend/src/lib/_fbs/open-shock/serialization/local/set-rf-tx-pin-command.ts @@ -24,7 +24,7 @@ static getSizePrefixedRootAsSetRfTxPinCommand(bb:flatbuffers.ByteBuffer, obj?:Se pin():number { const offset = this.bb!.__offset(this.bb_pos, 4); - return offset ? this.bb!.readUint8(this.bb_pos + offset) : 0; + return offset ? this.bb!.readInt8(this.bb_pos + offset) : 0; } static startSetRfTxPinCommand(builder:flatbuffers.Builder) { diff --git a/include/Chipset.h b/include/Chipset.h index 5cb9a0ae..c05b49ec 100644 --- a/include/Chipset.h +++ b/include/Chipset.h @@ -76,8 +76,8 @@ // See: ESP32 Series Datasheet Version 4.3 Section 2.2 Pin Overview // See: ESP32 Series Datasheet Version 4.3 Section 2.4 Strapping Pins #define CHIP_UNSAFE_GPIO(pin) \ - ((pin) == GPIO_NUM_1 || (pin) == GPIO_NUM_3 || (pin) == GPIO_NUM_0 || (pin) == GPIO_NUM_2 || (pin) == GPIO_NUM_5 || (pin) == GPIO_NUM_15 || (pin) == GPIO_NUM_6 || (pin) == GPIO_NUM_7 || (pin) == GPIO_NUM_8 || (pin) == GPIO_NUM_9 || (pin) == GPIO_NUM_11 || (pin) == GPIO_NUM_16 \ - || (pin) == GPIO_NUM_17) + ((pin) == GPIO_NUM_1 || (pin) == GPIO_NUM_3 || (pin) == GPIO_NUM_0 || (pin) == GPIO_NUM_2 || (pin) == GPIO_NUM_5 || (pin) == GPIO_NUM_15 || (pin) == GPIO_NUM_6 || (pin) == GPIO_NUM_7 || (pin) == GPIO_NUM_8 || (pin) == GPIO_NUM_9 || (pin) == GPIO_NUM_11 \ + || (pin) == GPIO_NUM_16 || (pin) == GPIO_NUM_17) #endif // ESP32-PICO-D4 @@ -90,7 +90,9 @@ // GPIO3, GPIO1 is used for UART0 RXD/TXD. // GPIO25, GPIO27, GPIO29, GPIO30, GPIO31, GPIO32, GPIO33 is used for SPI flash connection. (DO NOT TOUCH) // GPIO12, GPIO0, GPIO2, GPIO15, and GPIO5 are used for boot mode and SDIO slave timing selection. -#define CHIP_UNSAFE_GPIO(pin) ((pin) == GPIO_NUM_3 || (pin) == GPIO_NUM_1 || (pin) == GPIO_NUM_25 || (pin) == GPIO_NUM_27 || (pin) == GPIO_NUM_29 || (pin) == GPIO_NUM_30 || (pin) == GPIO_NUM_31 || (pin) == GPIO_NUM_32 || (pin) == GPIO_NUM_33 || (pin) == GPIO_NUM_12 || (pin) == GPIO_NUM_0 || (pin) == GPIO_NUM_2 || (pin) == GPIO_NUM_15 || (pin) == GPIO_NUM_5) +#define CHIP_UNSAFE_GPIO(pin) \ + ((pin) == GPIO_NUM_3 || (pin) == GPIO_NUM_1 || (pin) == GPIO_NUM_25 || (pin) == GPIO_NUM_27 || (pin) == GPIO_NUM_29 || (pin) == GPIO_NUM_30 || (pin) == GPIO_NUM_31 || (pin) == GPIO_NUM_32 || (pin) == GPIO_NUM_33 || (pin) == GPIO_NUM_12 \ + || (pin) == GPIO_NUM_0 || (pin) == GPIO_NUM_2 || (pin) == GPIO_NUM_15 || (pin) == GPIO_NUM_5) #endif // ESP32-PICO-V3 @@ -103,7 +105,9 @@ // GPIO3, GPIO1 is used for UART0 RXD/TXD. // GPIO6, GPIO11, GPIO9, GPIO10 is used for SPI flash connection. (DO NOT TOUCH) // GPIO12, GPIO0, GPIO2, GPIO15, and GPIO5 are used for boot mode and SDIO slave timing selection. -#define CHIP_UNSAFE_GPIO(pin) ((pin) == GPIO_NUM_3 || (pin) == GPIO_NUM_1 || (pin) == GPIO_NUM_6 || (pin) == GPIO_NUM_11 || (pin) == GPIO_NUM_9 || (pin) == GPIO_NUM_10 || (pin) == GPIO_NUM_12 || (pin) == GPIO_NUM_0 || (pin) == GPIO_NUM_2 || (pin) == GPIO_NUM_15 || (pin) == GPIO_NUM_5) +#define CHIP_UNSAFE_GPIO(pin) \ + ((pin) == GPIO_NUM_3 || (pin) == GPIO_NUM_1 || (pin) == GPIO_NUM_6 || (pin) == GPIO_NUM_11 || (pin) == GPIO_NUM_9 || (pin) == GPIO_NUM_10 || (pin) == GPIO_NUM_12 || (pin) == GPIO_NUM_0 || (pin) == GPIO_NUM_2 || (pin) == GPIO_NUM_15 \ + || (pin) == GPIO_NUM_5) #endif // ESP32-S2 @@ -116,7 +120,9 @@ // GPIO44, GPIO43 is used for UART0 RXD/TXD. // GPIO29, GPIO26, GPIO32, GPIO31, GPIO30, GPIO28, GPIO27 is used for SPI flash connection. (DO NOT TOUCH) // GPIO0, GPIO45, GPIO46 is strapping pins used to control the boot mode and misc. functions. -#define CHIP_UNSAFE_GPIO(pin) ((pin) == GPIO_NUM_44 || (pin) == GPIO_NUM_43 || (pin) == GPIO_NUM_29 || (pin) == GPIO_NUM_26 || (pin) == GPIO_NUM_32 || (pin) == GPIO_NUM_31 || (pin) == GPIO_NUM_30 || (pin) == GPIO_NUM_28 || (pin) == GPIO_NUM_27 || (pin) == GPIO_NUM_0 || (pin) == GPIO_NUM_45 || (pin) == GPIO_NUM_46) +#define CHIP_UNSAFE_GPIO(pin) \ + ((pin) == GPIO_NUM_44 || (pin) == GPIO_NUM_43 || (pin) == GPIO_NUM_29 || (pin) == GPIO_NUM_26 || (pin) == GPIO_NUM_32 || (pin) == GPIO_NUM_31 || (pin) == GPIO_NUM_30 || (pin) == GPIO_NUM_28 || (pin) == GPIO_NUM_27 || (pin) == GPIO_NUM_0 \ + || (pin) == GPIO_NUM_45 || (pin) == GPIO_NUM_46) #endif // ESP32-S3 @@ -130,7 +136,9 @@ // GPIO19, GPIO20 is used for USB serial, flashing, and debugging. // GPIO30, GPIO29, GPIO26, GPIO32, GPIO31, GPIO28, GPIO27, GPIO33, GPIO34, GPIO35, GPIO36, GPIO37 is used for SPI flash connection. (DO NOT TOUCH) // GPIO0, GPIO3, GPIO45, GPIO46 is strapping pins used to control the boot mode and misc. functions. -#define CHIP_UNSAFE_GPIO(pin) ((pin) == GPIO_NUM_44 || (pin) == GPIO_NUM_43 || (pin) == GPIO_NUM_19 || (pin) == GPIO_NUM_20 || (pin) == GPIO_NUM_30 || (pin) == GPIO_NUM_29 || (pin) == GPIO_NUM_26 || (pin) == GPIO_NUM_32 || (pin) == GPIO_NUM_31 || (pin) == GPIO_NUM_28 || (pin) == GPIO_NUM_27 || (pin) == GPIO_NUM_33 || (pin) == GPIO_NUM_34 || (pin) == GPIO_NUM_35 || (pin) == GPIO_NUM_36 || (pin) == GPIO_NUM_37 || (pin) == GPIO_NUM_0 || (pin) == GPIO_NUM_3 || (pin) == GPIO_NUM_45 || (pin) == GPIO_NUM_46) +#define CHIP_UNSAFE_GPIO(pin) \ + ((pin) == GPIO_NUM_44 || (pin) == GPIO_NUM_43 || (pin) == GPIO_NUM_19 || (pin) == GPIO_NUM_20 || (pin) == GPIO_NUM_30 || (pin) == GPIO_NUM_29 || (pin) == GPIO_NUM_26 || (pin) == GPIO_NUM_32 || (pin) == GPIO_NUM_31 || (pin) == GPIO_NUM_28 \ + || (pin) == GPIO_NUM_27 || (pin) == GPIO_NUM_33 || (pin) == GPIO_NUM_34 || (pin) == GPIO_NUM_35 || (pin) == GPIO_NUM_36 || (pin) == GPIO_NUM_37 || (pin) == GPIO_NUM_0 || (pin) == GPIO_NUM_3 || (pin) == GPIO_NUM_45 || (pin) == GPIO_NUM_46) #endif // ESP32-S3-PICO-1 @@ -154,7 +162,9 @@ // GPIO18, GPIO19, GPIO4, GPIO5, GPIO6, GPIO7 is used for USB serial, flashing, and debugging. // GPIO12, GPIO13, GPIO14, GPIO15, GPIO16, GPIO17 is used for SPI flash connection. (DO NOT TOUCH) // GPIO2, GPIO8, GPIO9 is strapping pins used to control the boot mode and misc. functions. -#define CHIP_UNSAFE_GPIO(pin) ((pin) == GPIO_NUM_20 || (pin) == GPIO_NUM_21 || (pin) == GPIO_NUM_18 || (pin) == GPIO_NUM_19 || (pin) == GPIO_NUM_4 || (pin) == GPIO_NUM_5 || (pin) == GPIO_NUM_6 || (pin) == GPIO_NUM_7 || (pin) == GPIO_NUM_12 || (pin) == GPIO_NUM_13 || (pin) == GPIO_NUM_14 || (pin) == GPIO_NUM_15 || (pin) == GPIO_NUM_16 || (pin) == GPIO_NUM_17 || (pin) == GPIO_NUM_2 || (pin) == GPIO_NUM_8 || (pin) == GPIO_NUM_9) +#define CHIP_UNSAFE_GPIO(pin) \ + ((pin) == GPIO_NUM_20 || (pin) == GPIO_NUM_21 || (pin) == GPIO_NUM_18 || (pin) == GPIO_NUM_19 || (pin) == GPIO_NUM_4 || (pin) == GPIO_NUM_5 || (pin) == GPIO_NUM_6 || (pin) == GPIO_NUM_7 || (pin) == GPIO_NUM_12 || (pin) == GPIO_NUM_13 \ + || (pin) == GPIO_NUM_14 || (pin) == GPIO_NUM_15 || (pin) == GPIO_NUM_16 || (pin) == GPIO_NUM_17 || (pin) == GPIO_NUM_2 || (pin) == GPIO_NUM_8 || (pin) == GPIO_NUM_9) #endif // ESP32-C6 @@ -202,7 +212,8 @@ #pragma endregion namespace OpenShock { - constexpr bool IsValidGPIOPin(uint8_t pin) { + constexpr bool IsValidGPIOPin(int8_t pin) + { if (pin == OPENSHOCK_GPIO_INVALID) { return false; } @@ -225,10 +236,12 @@ namespace OpenShock { return true; } - constexpr bool IsValidInputPin(uint8_t pin) { + constexpr bool IsValidInputPin(int8_t pin) + { return IsValidGPIOPin(pin); } - constexpr bool IsValidOutputPin(uint8_t pin) { + constexpr bool IsValidOutputPin(int8_t pin) + { if (!IsValidGPIOPin(pin)) { return false; } @@ -240,10 +253,11 @@ namespace OpenShock { return true; } - const std::size_t GPIOPinSetSize = GPIO_NUM_MAX + 1; + const std::size_t GPIOPinSetSize = GPIO_NUM_MAX; typedef std::bitset GPIOPinSet; - constexpr GPIOPinSet GetValidGPIOPins() { + constexpr GPIOPinSet GetValidGPIOPins() + { GPIOPinSet pins; for (std::size_t i = 0; i < GPIOPinSetSize; i++) { if (IsValidGPIOPin(i)) { @@ -252,7 +266,8 @@ namespace OpenShock { } return pins; } - constexpr GPIOPinSet GetValidInputPins() { + constexpr GPIOPinSet GetValidInputPins() + { GPIOPinSet pins; for (std::size_t i = 0; i < GPIOPinSetSize; i++) { if (IsValidInputPin(i)) { @@ -261,7 +276,8 @@ namespace OpenShock { } return pins; } - constexpr GPIOPinSet GetValidOutputPins() { + constexpr GPIOPinSet GetValidOutputPins() + { GPIOPinSet pins; for (std::size_t i = 0; i < GPIOPinSetSize; i++) { if (IsValidOutputPin(i)) { diff --git a/include/CommandHandler.h b/include/CommandHandler.h index dd09dc2e..0d7bedad 100644 --- a/include/CommandHandler.h +++ b/include/CommandHandler.h @@ -4,6 +4,8 @@ #include "ShockerCommandType.h" #include "ShockerModelType.h" +#include + #include // TODO: This is horrible architecture. Fix it. @@ -12,11 +14,11 @@ namespace OpenShock::CommandHandler { [[nodiscard]] bool Init(); bool Ok(); - SetGPIOResultCode SetRfTxPin(uint8_t txPin); - uint8_t GetRfTxPin(); + gpio_num_t GetRfTxPin(); + SetGPIOResultCode SetRfTxPin(gpio_num_t txPin); - SetGPIOResultCode SetEstopPin(uint8_t estopPin); - uint8_t GetEstopPin(); + SetGPIOResultCode SetEstopPin(gpio_num_t estopPin); + gpio_num_t GetEstopPin(); bool SetKeepAliveEnabled(bool enabled); bool SetKeepAlivePaused(bool paused); diff --git a/include/Common.h b/include/Common.h index 442254cc..23c4d0e8 100644 --- a/include/Common.h +++ b/include/Common.h @@ -22,7 +22,7 @@ #define OPENSHOCK_FW_CDN_URL(path) "https://" OPENSHOCK_FW_CDN_DOMAIN path -#define OPENSHOCK_GPIO_INVALID 0 +#define OPENSHOCK_GPIO_INVALID -1 #ifndef OPENSHOCK_RF_TX_GPIO #warning "OPENSHOCK_RF_TX_GPIO is not defined, setting to OPENSHOCK_GPIO_INVALID" diff --git a/include/Convert.h b/include/Convert.h index 325f7eb2..9b56207c 100644 --- a/include/Convert.h +++ b/include/Convert.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include @@ -13,4 +15,5 @@ namespace OpenShock::Convert { bool ToInt64(std::string_view str, int64_t& val); bool ToUint64(std::string_view str, uint64_t& val); bool ToBool(std::string_view str, bool& val); + bool ToGpioNum(std::string_view str, gpio_num_t& val); } // namespace OpenShock::Convert diff --git a/include/config/Config.h b/include/config/Config.h index 2cac8708..9e654cc0 100644 --- a/include/config/Config.h +++ b/include/config/Config.h @@ -57,8 +57,8 @@ namespace OpenShock::Config { bool GetWiFiCredentials(cJSON* array, bool withSensitiveData); bool SetWiFiCredentials(const std::vector& credentials); - bool GetRFConfigTxPin(uint8_t& out); - bool SetRFConfigTxPin(uint8_t txPin); + bool GetRFConfigTxPin(gpio_num_t& out); + bool SetRFConfigTxPin(gpio_num_t txPin); bool GetRFConfigKeepAliveEnabled(bool& out); bool SetRFConfigKeepAliveEnabled(bool enabled); diff --git a/include/config/RFConfig.h b/include/config/RFConfig.h index 59e76124..cf766e69 100644 --- a/include/config/RFConfig.h +++ b/include/config/RFConfig.h @@ -1,13 +1,15 @@ #pragma once +#include + #include "config/ConfigBase.h" namespace OpenShock::Config { struct RFConfig : public ConfigBase { RFConfig(); - RFConfig(uint8_t txPin, bool keepAliveEnabled); + RFConfig(gpio_num_t txPin, bool keepAliveEnabled); - uint8_t txPin; + gpio_num_t txPin; bool keepAliveEnabled; void ToDefault() override; diff --git a/include/config/internal/utils.h b/include/config/internal/utils.h index 92bec784..af66f054 100644 --- a/include/config/internal/utils.h +++ b/include/config/internal/utils.h @@ -3,23 +3,35 @@ #include "config/ConfigBase.h" #include +#include #include #include #include namespace OpenShock::Config::Internal::Utils { + bool FromU8GpioNum(gpio_num_t& val, uint8_t u8Val); + void FromU8GpioNum(gpio_num_t& val, uint8_t u8Val, gpio_num_t defaultVal); + void FromFbsStr(std::string& str, const flatbuffers::String* fbsStr, const char* defaultStr); bool FromFbsIPAddress(IPAddress& ip, const flatbuffers::String* fbsIP, const IPAddress& defaultIP); bool FromJsonBool(bool& val, const cJSON* json, const char* name, bool defaultVal); bool FromJsonU8(uint8_t& val, const cJSON* json, const char* name, uint8_t defaultVal); bool FromJsonU16(uint16_t& val, const cJSON* json, const char* name, uint16_t defaultVal); bool FromJsonI32(int32_t& val, const cJSON* json, const char* name, int32_t defaultVal); - bool FromJsonStr(std::string& str, const cJSON* json, const char* name, const char* defaultStr); - bool FromJsonIPAddress(IPAddress& ip, const cJSON* json, const char* name, const IPAddress& defaultIP); + + bool FromJsonStr(std::string& str, const cJSON* json, const char* name); + void FromJsonStr(std::string& str, const cJSON* json, const char* name, const char* defaultStr); + + bool FromJsonIPAddress(IPAddress& ip, const cJSON* json, const char* name); + void FromJsonIPAddress(IPAddress& ip, const cJSON* json, const char* name, const IPAddress& defaultIP); + + bool FromJsonGpioNum(gpio_num_t& val, const cJSON* json, const char* name); + void FromJsonGpioNum(gpio_num_t& val, const cJSON* json, const char* name, gpio_num_t defaultVal); template // T inherits from ConfigBase - void FromFbsVec(std::vector& vec, const flatbuffers::Vector>* fbsVec) { + void FromFbsVec(std::vector& vec, const flatbuffers::Vector>* fbsVec) + { vec.clear(); if (fbsVec == nullptr) { @@ -34,7 +46,8 @@ namespace OpenShock::Config::Internal::Utils { } } template // T inherits from ConfigBase - bool FromJsonStrParsed(T& val, const cJSON* json, const char* name, bool (*StringParser)(T&, const char*), T defaultVal) { + bool FromJsonStrParsed(T& val, const cJSON* json, const char* name, bool (*StringParser)(T&, const char*), T defaultVal) + { const cJSON* jsonVal = cJSON_GetObjectItemCaseSensitive(json, name); if (jsonVal == nullptr) { val = defaultVal; @@ -52,14 +65,16 @@ namespace OpenShock::Config::Internal::Utils { return true; } template // T inherits from ConfigBase - bool FromJsonArray(std::vector& vec, const cJSON* jsonArray) { + bool FromJsonArray(std::vector& vec, const cJSON* jsonArray) + { vec.clear(); if (jsonArray == nullptr) { return true; } const cJSON* jsonItem = nullptr; - cJSON_ArrayForEach(jsonItem, jsonArray) { + cJSON_ArrayForEach(jsonItem, jsonArray) + { T item; if (item.FromJSON(jsonItem)) { vec.emplace_back(std::move(item)); diff --git a/include/radio/RFTransmitter.h b/include/radio/RFTransmitter.h index 9305d8de..80d56d14 100644 --- a/include/radio/RFTransmitter.h +++ b/include/radio/RFTransmitter.h @@ -4,6 +4,7 @@ #include "ShockerModelType.h" #include +#include #include #include @@ -13,10 +14,10 @@ namespace OpenShock { class RFTransmitter { public: - RFTransmitter(uint8_t gpioPin); + RFTransmitter(gpio_num_t gpioPin); ~RFTransmitter(); - inline uint8_t GetTxPin() const { return m_txPin; } + inline gpio_num_t GetTxPin() const { return m_txPin; } inline bool ok() const { return m_rmtHandle != nullptr && m_queueHandle != nullptr && m_taskHandle != nullptr; } @@ -27,7 +28,7 @@ namespace OpenShock { void destroy(); void TransmitTask(); - uint8_t m_txPin; + gpio_num_t m_txPin; rmt_obj_t* m_rmtHandle; QueueHandle_t m_queueHandle; TaskHandle_t m_taskHandle; diff --git a/include/serialization/_fbs/HubConfig_generated.h b/include/serialization/_fbs/HubConfig_generated.h index 7587971e..6a73ae91 100644 --- a/include/serialization/_fbs/HubConfig_generated.h +++ b/include/serialization/_fbs/HubConfig_generated.h @@ -130,8 +130,8 @@ struct RFConfig FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { VT_KEEPALIVE_ENABLED = 6 }; /// The GPIO pin connected to the RF modulator's data pin for transmitting (TX) - uint8_t tx_pin() const { - return GetField(VT_TX_PIN, 0); + int8_t tx_pin() const { + return GetField(VT_TX_PIN, 0); } /// Whether to transmit keepalive messages to keep the shockers from entering sleep mode bool keepalive_enabled() const { @@ -139,7 +139,7 @@ struct RFConfig FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { } bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && - VerifyField(verifier, VT_TX_PIN, 1) && + VerifyField(verifier, VT_TX_PIN, 1) && VerifyField(verifier, VT_KEEPALIVE_ENABLED, 1) && verifier.EndTable(); } @@ -149,8 +149,8 @@ struct RFConfigBuilder { typedef RFConfig Table; ::flatbuffers::FlatBufferBuilder &fbb_; ::flatbuffers::uoffset_t start_; - void add_tx_pin(uint8_t tx_pin) { - fbb_.AddElement(RFConfig::VT_TX_PIN, tx_pin, 0); + void add_tx_pin(int8_t tx_pin) { + fbb_.AddElement(RFConfig::VT_TX_PIN, tx_pin, 0); } void add_keepalive_enabled(bool keepalive_enabled) { fbb_.AddElement(RFConfig::VT_KEEPALIVE_ENABLED, static_cast(keepalive_enabled), 0); @@ -168,7 +168,7 @@ struct RFConfigBuilder { inline ::flatbuffers::Offset CreateRFConfig( ::flatbuffers::FlatBufferBuilder &_fbb, - uint8_t tx_pin = 0, + int8_t tx_pin = 0, bool keepalive_enabled = false) { RFConfigBuilder builder_(_fbb); builder_.add_keepalive_enabled(keepalive_enabled); @@ -195,13 +195,13 @@ struct EStopConfig FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { return GetField(VT_ENABLED, 0) != 0; } /// The GPIO pin connected to the E-Stop button - uint8_t gpio_pin() const { - return GetField(VT_GPIO_PIN, 0); + int8_t gpio_pin() const { + return GetField(VT_GPIO_PIN, 0); } bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_ENABLED, 1) && - VerifyField(verifier, VT_GPIO_PIN, 1) && + VerifyField(verifier, VT_GPIO_PIN, 1) && verifier.EndTable(); } }; @@ -213,8 +213,8 @@ struct EStopConfigBuilder { void add_enabled(bool enabled) { fbb_.AddElement(EStopConfig::VT_ENABLED, static_cast(enabled), 0); } - void add_gpio_pin(uint8_t gpio_pin) { - fbb_.AddElement(EStopConfig::VT_GPIO_PIN, gpio_pin, 0); + void add_gpio_pin(int8_t gpio_pin) { + fbb_.AddElement(EStopConfig::VT_GPIO_PIN, gpio_pin, 0); } explicit EStopConfigBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { @@ -230,7 +230,7 @@ struct EStopConfigBuilder { inline ::flatbuffers::Offset CreateEStopConfig( ::flatbuffers::FlatBufferBuilder &_fbb, bool enabled = false, - uint8_t gpio_pin = 0) { + int8_t gpio_pin = 0) { EStopConfigBuilder builder_(_fbb); builder_.add_gpio_pin(gpio_pin); builder_.add_enabled(enabled); diff --git a/include/serialization/_fbs/HubToLocalMessage_generated.h b/include/serialization/_fbs/HubToLocalMessage_generated.h index 0a75027f..dfba7811 100644 --- a/include/serialization/_fbs/HubToLocalMessage_generated.h +++ b/include/serialization/_fbs/HubToLocalMessage_generated.h @@ -679,15 +679,15 @@ struct SetRfTxPinCommandResult FLATBUFFERS_FINAL_CLASS : private ::flatbuffers:: VT_PIN = 4, VT_RESULT = 6 }; - uint8_t pin() const { - return GetField(VT_PIN, 0); + int8_t pin() const { + return GetField(VT_PIN, 0); } OpenShock::Serialization::Local::SetGPIOResultCode result() const { return static_cast(GetField(VT_RESULT, 0)); } bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && - VerifyField(verifier, VT_PIN, 1) && + VerifyField(verifier, VT_PIN, 1) && VerifyField(verifier, VT_RESULT, 1) && verifier.EndTable(); } @@ -697,8 +697,8 @@ struct SetRfTxPinCommandResultBuilder { typedef SetRfTxPinCommandResult Table; ::flatbuffers::FlatBufferBuilder &fbb_; ::flatbuffers::uoffset_t start_; - void add_pin(uint8_t pin) { - fbb_.AddElement(SetRfTxPinCommandResult::VT_PIN, pin, 0); + void add_pin(int8_t pin) { + fbb_.AddElement(SetRfTxPinCommandResult::VT_PIN, pin, 0); } void add_result(OpenShock::Serialization::Local::SetGPIOResultCode result) { fbb_.AddElement(SetRfTxPinCommandResult::VT_RESULT, static_cast(result), 0); @@ -716,7 +716,7 @@ struct SetRfTxPinCommandResultBuilder { inline ::flatbuffers::Offset CreateSetRfTxPinCommandResult( ::flatbuffers::FlatBufferBuilder &_fbb, - uint8_t pin = 0, + int8_t pin = 0, OpenShock::Serialization::Local::SetGPIOResultCode result = OpenShock::Serialization::Local::SetGPIOResultCode::Success) { SetRfTxPinCommandResultBuilder builder_(_fbb); builder_.add_result(result); @@ -799,15 +799,15 @@ struct SetEstopPinCommandResult FLATBUFFERS_FINAL_CLASS : private ::flatbuffers: VT_GPIO_PIN = 4, VT_RESULT = 6 }; - uint8_t gpio_pin() const { - return GetField(VT_GPIO_PIN, 0); + int8_t gpio_pin() const { + return GetField(VT_GPIO_PIN, 0); } OpenShock::Serialization::Local::SetGPIOResultCode result() const { return static_cast(GetField(VT_RESULT, 0)); } bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && - VerifyField(verifier, VT_GPIO_PIN, 1) && + VerifyField(verifier, VT_GPIO_PIN, 1) && VerifyField(verifier, VT_RESULT, 1) && verifier.EndTable(); } @@ -817,8 +817,8 @@ struct SetEstopPinCommandResultBuilder { typedef SetEstopPinCommandResult Table; ::flatbuffers::FlatBufferBuilder &fbb_; ::flatbuffers::uoffset_t start_; - void add_gpio_pin(uint8_t gpio_pin) { - fbb_.AddElement(SetEstopPinCommandResult::VT_GPIO_PIN, gpio_pin, 0); + void add_gpio_pin(int8_t gpio_pin) { + fbb_.AddElement(SetEstopPinCommandResult::VT_GPIO_PIN, gpio_pin, 0); } void add_result(OpenShock::Serialization::Local::SetGPIOResultCode result) { fbb_.AddElement(SetEstopPinCommandResult::VT_RESULT, static_cast(result), 0); @@ -836,7 +836,7 @@ struct SetEstopPinCommandResultBuilder { inline ::flatbuffers::Offset CreateSetEstopPinCommandResult( ::flatbuffers::FlatBufferBuilder &_fbb, - uint8_t gpio_pin = 0, + int8_t gpio_pin = 0, OpenShock::Serialization::Local::SetGPIOResultCode result = OpenShock::Serialization::Local::SetGPIOResultCode::Success) { SetEstopPinCommandResultBuilder builder_(_fbb); builder_.add_result(result); diff --git a/include/serialization/_fbs/LocalToHubMessage_generated.h b/include/serialization/_fbs/LocalToHubMessage_generated.h index 1794a8d7..329b3a28 100644 --- a/include/serialization/_fbs/LocalToHubMessage_generated.h +++ b/include/serialization/_fbs/LocalToHubMessage_generated.h @@ -1173,12 +1173,12 @@ struct SetRfTxPinCommand FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_PIN = 4 }; - uint8_t pin() const { - return GetField(VT_PIN, 0); + int8_t pin() const { + return GetField(VT_PIN, 0); } bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && - VerifyField(verifier, VT_PIN, 1) && + VerifyField(verifier, VT_PIN, 1) && verifier.EndTable(); } }; @@ -1187,8 +1187,8 @@ struct SetRfTxPinCommandBuilder { typedef SetRfTxPinCommand Table; ::flatbuffers::FlatBufferBuilder &fbb_; ::flatbuffers::uoffset_t start_; - void add_pin(uint8_t pin) { - fbb_.AddElement(SetRfTxPinCommand::VT_PIN, pin, 0); + void add_pin(int8_t pin) { + fbb_.AddElement(SetRfTxPinCommand::VT_PIN, pin, 0); } explicit SetRfTxPinCommandBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { @@ -1203,7 +1203,7 @@ struct SetRfTxPinCommandBuilder { inline ::flatbuffers::Offset CreateSetRfTxPinCommand( ::flatbuffers::FlatBufferBuilder &_fbb, - uint8_t pin = 0) { + int8_t pin = 0) { SetRfTxPinCommandBuilder builder_(_fbb); builder_.add_pin(pin); return builder_.Finish(); @@ -1273,12 +1273,12 @@ struct SetEstopPinCommand FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_PIN = 4 }; - uint8_t pin() const { - return GetField(VT_PIN, 0); + int8_t pin() const { + return GetField(VT_PIN, 0); } bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && - VerifyField(verifier, VT_PIN, 1) && + VerifyField(verifier, VT_PIN, 1) && verifier.EndTable(); } }; @@ -1287,8 +1287,8 @@ struct SetEstopPinCommandBuilder { typedef SetEstopPinCommand Table; ::flatbuffers::FlatBufferBuilder &fbb_; ::flatbuffers::uoffset_t start_; - void add_pin(uint8_t pin) { - fbb_.AddElement(SetEstopPinCommand::VT_PIN, pin, 0); + void add_pin(int8_t pin) { + fbb_.AddElement(SetEstopPinCommand::VT_PIN, pin, 0); } explicit SetEstopPinCommandBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { @@ -1303,7 +1303,7 @@ struct SetEstopPinCommandBuilder { inline ::flatbuffers::Offset CreateSetEstopPinCommand( ::flatbuffers::FlatBufferBuilder &_fbb, - uint8_t pin = 0) { + int8_t pin = 0) { SetEstopPinCommandBuilder builder_(_fbb); builder_.add_pin(pin); return builder_.Finish(); diff --git a/schemas b/schemas index 5a234f36..9a4dd475 160000 --- a/schemas +++ b/schemas @@ -1 +1 @@ -Subproject commit 5a234f361c962d42ef215b0a927a117502ba910b +Subproject commit 9a4dd4754d45990ec8ccbfb3d5c429f676760e82 diff --git a/src/CommandHandler.cpp b/src/CommandHandler.cpp index a449793a..7575f91a 100644 --- a/src/CommandHandler.cpp +++ b/src/CommandHandler.cpp @@ -25,7 +25,8 @@ const uint16_t KEEP_ALIVE_DURATION = 300; using namespace OpenShock; -uint32_t calculateEepyTime(int64_t timeToKeepAlive) { +uint32_t calculateEepyTime(int64_t timeToKeepAlive) +{ int64_t now = OpenShock::millis(); return static_cast(std::clamp(timeToKeepAlive - now, 0LL, KEEP_ALIVE_INTERVAL)); } @@ -46,7 +47,8 @@ static ReadWriteMutex s_keepAliveMutex = {}; static QueueHandle_t s_keepAliveQueue = nullptr; static TaskHandle_t s_keepAliveTaskHandle = nullptr; -void _keepAliveTask(void* arg) { +void _keepAliveTask(void* arg) +{ (void)arg; int64_t timeToKeepAlive = KEEP_ALIVE_INTERVAL; @@ -101,7 +103,8 @@ void _keepAliveTask(void* arg) { } } -bool _internalSetKeepAliveEnabled(bool enabled) { +bool _internalSetKeepAliveEnabled(bool enabled) +{ bool wasEnabled = s_keepAliveQueue != nullptr && s_keepAliveTaskHandle != nullptr; if (enabled == wasEnabled) { @@ -148,7 +151,8 @@ bool _internalSetKeepAliveEnabled(bool enabled) { return true; } -bool CommandHandler::Init() { +bool CommandHandler::Init() +{ static bool initialized = false; if (initialized) { OS_LOGW(TAG, "RF Transmitter and EStopManager are already initialized?"); @@ -162,17 +166,17 @@ bool CommandHandler::Init() { return false; } - uint8_t txPin = rfConfig.txPin; + gpio_num_t txPin = rfConfig.txPin; if (!OpenShock::IsValidOutputPin(txPin)) { if (!OpenShock::IsValidOutputPin(OPENSHOCK_RF_TX_GPIO)) { - OS_LOGE(TAG, "Configured RF TX pin (%u) is invalid, and default pin (%u) is invalid. Unable to initialize RF transmitter", txPin, OPENSHOCK_RF_TX_GPIO); + OS_LOGE(TAG, "Configured RF TX pin (%hhi) is invalid, and default pin (%hhi) is invalid. Unable to initialize RF transmitter", txPin, OPENSHOCK_RF_TX_GPIO); OS_LOGD(TAG, "Setting RF TX pin to GPIO_INVALID"); - return Config::SetRFConfigTxPin(OPENSHOCK_GPIO_INVALID); // This is not a error yet, unless we are unable to save the RF TX Pin as invalid + return Config::SetRFConfigTxPin(static_cast(OPENSHOCK_GPIO_INVALID)); // This is not a error yet, unless we are unable to save the RF TX Pin as invalid } - OS_LOGW(TAG, "Configured RF TX pin (%u) is invalid, using default pin (%u)", txPin, OPENSHOCK_RF_TX_GPIO); - txPin = OPENSHOCK_RF_TX_GPIO; + OS_LOGW(TAG, "Configured RF TX pin (%hhi) is invalid, using default pin (%hhi)", txPin, OPENSHOCK_RF_TX_GPIO); + txPin = static_cast(OPENSHOCK_RF_TX_GPIO); if (!Config::SetRFConfigTxPin(txPin)) { OS_LOGE(TAG, "Failed to set RF TX pin in config"); return false; @@ -201,11 +205,13 @@ bool CommandHandler::Init() { return true; } -bool CommandHandler::Ok() { +bool CommandHandler::Ok() +{ return s_rfTransmitter != nullptr; } -SetGPIOResultCode CommandHandler::SetRfTxPin(uint8_t txPin) { +SetGPIOResultCode CommandHandler::SetRfTxPin(gpio_num_t txPin) +{ if (!OpenShock::IsValidOutputPin(txPin)) { return SetGPIOResultCode::InvalidPin; } @@ -234,18 +240,19 @@ SetGPIOResultCode CommandHandler::SetRfTxPin(uint8_t txPin) { return SetGPIOResultCode::Success; } -SetGPIOResultCode CommandHandler::SetEstopPin(uint8_t estopPin) { - if (OpenShock::IsValidInputPin(estopPin)) { +SetGPIOResultCode CommandHandler::SetEstopPin(gpio_num_t estopPin) +{ + if (OpenShock::IsValidInputPin(static_cast(estopPin))) { xSemaphoreTake(s_estopManagerMutex, portMAX_DELAY); - if (!EStopManager::SetEStopPin(static_cast(estopPin))) { + if (!EStopManager::SetEStopPin(estopPin)) { OS_LOGE(TAG, "Failed to set EStop pin"); xSemaphoreGive(s_estopManagerMutex); return SetGPIOResultCode::InternalError; } - if (!Config::SetEStopGpioPin(static_cast(estopPin))) { + if (!Config::SetEStopGpioPin(estopPin)) { OS_LOGE(TAG, "Failed to set EStop pin in config"); xSemaphoreGive(s_estopManagerMutex); @@ -259,7 +266,8 @@ SetGPIOResultCode CommandHandler::SetEstopPin(uint8_t estopPin) { } } -bool CommandHandler::SetKeepAliveEnabled(bool enabled) { +bool CommandHandler::SetKeepAliveEnabled(bool enabled) +{ if (!_internalSetKeepAliveEnabled(enabled)) { return false; } @@ -272,7 +280,8 @@ bool CommandHandler::SetKeepAliveEnabled(bool enabled) { return true; } -bool CommandHandler::SetKeepAlivePaused(bool paused) { +bool CommandHandler::SetKeepAlivePaused(bool paused) +{ bool keepAliveEnabled = false; if (!Config::GetRFConfigKeepAliveEnabled(keepAliveEnabled)) { OS_LOGE(TAG, "Failed to get keep-alive enabled from config"); @@ -290,17 +299,23 @@ bool CommandHandler::SetKeepAlivePaused(bool paused) { return true; } -uint8_t CommandHandler::GetRfTxPin() { - uint8_t txPin; +gpio_num_t CommandHandler::GetRfTxPin() +{ + if (s_rfTransmitter != nullptr) { + return s_rfTransmitter->GetTxPin(); + } + + gpio_num_t txPin; if (!Config::GetRFConfigTxPin(txPin)) { OS_LOGE(TAG, "Failed to get RF TX pin from config"); - txPin = OPENSHOCK_GPIO_INVALID; + txPin = static_cast(OPENSHOCK_GPIO_INVALID); } return txPin; } -bool CommandHandler::HandleCommand(ShockerModelType model, uint16_t shockerId, ShockerCommandType type, uint8_t intensity, uint16_t durationMs) { +bool CommandHandler::HandleCommand(ShockerModelType model, uint16_t shockerId, ShockerCommandType type, uint8_t intensity, uint16_t durationMs) +{ ScopedReadLock rftxLock(&s_rfTransmitterMutex); if (s_rfTransmitter == nullptr) { diff --git a/src/Convert.cpp b/src/Convert.cpp index 95f08ee1..c21b5ff6 100644 --- a/src/Convert.cpp +++ b/src/Convert.cpp @@ -9,7 +9,8 @@ using namespace std::string_view_literals; template -constexpr unsigned int NumDigits() { +constexpr unsigned int NumDigits() +{ static_assert(std::is_integral::value); uint64_t num = std::numeric_limits::max(); @@ -24,7 +25,8 @@ constexpr unsigned int NumDigits() { // Base converter template -constexpr bool spanToT(std::string_view str, T& val) { +constexpr bool spanToT(std::string_view str, T& val) +{ static_assert(std::is_integral::value); const T Threshold = std::numeric_limits::max() / 10; @@ -59,7 +61,8 @@ constexpr bool spanToT(std::string_view str, T& val) { // Unsigned converter template -constexpr bool spanToUT(std::string_view str, T& val) { +constexpr bool spanToUT(std::string_view str, T& val) +{ static_assert(std::is_unsigned::value); if (str.empty() || str.length() > NumDigits()) { @@ -71,7 +74,8 @@ constexpr bool spanToUT(std::string_view str, T& val) { // Signed converter template -constexpr bool spanToST(std::string_view str, T& val) { +constexpr bool spanToST(std::string_view str, T& val) +{ static_assert(std::is_signed::value); if (str.empty() || str.length() > NumDigits()) { @@ -103,31 +107,40 @@ constexpr bool spanToST(std::string_view str, T& val) { using namespace OpenShock; // Specific converters -bool Convert::ToInt8(std::string_view str, int8_t& val) { +bool Convert::ToInt8(std::string_view str, int8_t& val) +{ return spanToST(str, val); } -bool Convert::ToUint8(std::string_view str, uint8_t& val) { +bool Convert::ToUint8(std::string_view str, uint8_t& val) +{ return spanToUT(str, val); } -bool Convert::ToInt16(std::string_view str, int16_t& val) { +bool Convert::ToInt16(std::string_view str, int16_t& val) +{ return spanToST(str, val); } -bool Convert::ToUint16(std::string_view str, uint16_t& val) { +bool Convert::ToUint16(std::string_view str, uint16_t& val) +{ return spanToUT(str, val); } -bool Convert::ToInt32(std::string_view str, int32_t& val) { +bool Convert::ToInt32(std::string_view str, int32_t& val) +{ return spanToST(str, val); } -bool Convert::ToUint32(std::string_view str, uint32_t& val) { +bool Convert::ToUint32(std::string_view str, uint32_t& val) +{ return spanToUT(str, val); } -bool Convert::ToInt64(std::string_view str, int64_t& val) { +bool Convert::ToInt64(std::string_view str, int64_t& val) +{ return spanToST(str, val); } -bool Convert::ToUint64(std::string_view str, uint64_t& val) { +bool Convert::ToUint64(std::string_view str, uint64_t& val) +{ return spanToUT(str, val); } -bool Convert::ToBool(std::string_view str, bool& val) { +bool Convert::ToBool(std::string_view str, bool& val) +{ if (str.length() > 5) { return false; } @@ -145,6 +158,22 @@ bool Convert::ToBool(std::string_view str, bool& val) { return false; } +bool Convert::ToGpioNum(std::string_view str, gpio_num_t& val) +{ + int8_t i8 = 0; + if (!spanToST(str, i8)) { + return false; + } + + if (i8 < GPIO_NUM_NC || i8 >= GPIO_NUM_MAX) { + return false; + } + + val = static_cast(i8); + + return true; +} + static_assert(NumDigits() == 3, "NumDigits test for uint8_t failed"); static_assert(NumDigits() == 5, "NumDigits test for uint16_t failed"); static_assert(NumDigits() == 10, "NumDigits test for uint32_t failed"); @@ -155,189 +184,216 @@ static_assert(NumDigits() == 6, "NumDigits test for int16_t failed"); static_assert(NumDigits() == 11, "NumDigits test for int32_t failed"); static_assert(NumDigits() == 20, "NumDigits test for int64_t failed"); -constexpr bool test_spanToUT8() { +constexpr bool test_spanToUT8() +{ uint8_t u8 = 0; return spanToUT("255"sv, u8) && u8 == 255; } static_assert(test_spanToUT8(), "test_spanToUT8 failed"); -constexpr bool test_spanToUT16() { +constexpr bool test_spanToUT16() +{ uint16_t u16 = 0; return spanToUT("65535"sv, u16) && u16 == 65'535; } static_assert(test_spanToUT16(), "test_spanToUT16 failed"); -constexpr bool test_spanToUT32() { +constexpr bool test_spanToUT32() +{ uint32_t u32 = 0; return spanToUT("4294967295"sv, u32) && u32 == 4'294'967'295U; } static_assert(test_spanToUT32(), "test_spanToUT32 failed"); -constexpr bool test_spanToUT64() { +constexpr bool test_spanToUT64() +{ uint64_t u64 = 0; return spanToUT("18446744073709551615"sv, u64) && u64 == 18'446'744'073'709'551'615ULL; } static_assert(test_spanToUT64(), "test_spanToUT64 failed"); -constexpr bool test_spanToUT8Overflow() { +constexpr bool test_spanToUT8Overflow() +{ uint8_t u8 = 0; return !spanToUT("256"sv, u8); // Overflow } static_assert(test_spanToUT8Overflow(), "test_spanToUT8Overflow failed"); -constexpr bool test_spanToUT16Overflow() { +constexpr bool test_spanToUT16Overflow() +{ uint16_t u16 = 0; return !spanToUT("70000"sv, u16); // Overflow } static_assert(test_spanToUT16Overflow(), "test_spanToUT16Overflow failed"); -constexpr bool test_spanToUT32Overflow() { +constexpr bool test_spanToUT32Overflow() +{ uint32_t u32 = 0; return !spanToUT("4294967296"sv, u32); // Overflow } static_assert(test_spanToUT32Overflow(), "test_spanToUT32Overflow failed"); -constexpr bool test_spanToUT64Overflow() { +constexpr bool test_spanToUT64Overflow() +{ uint64_t u64 = 0; return !spanToUT("18446744073709551616"sv, u64); // Overflow } static_assert(test_spanToUT64Overflow(), "test_spanToUT64Overflow failed"); -constexpr bool test_spanToST8() { +constexpr bool test_spanToST8() +{ int8_t i8 = 0; return spanToST("-127"sv, i8) && i8 == -127; } static_assert(test_spanToST8(), "test_spanToST8 failed"); -constexpr bool test_spanToST16() { +constexpr bool test_spanToST16() +{ int16_t i16 = 0; return spanToST("32767"sv, i16) && i16 == 32'767; } static_assert(test_spanToST16(), "test_spanToST16 failed"); -constexpr bool test_spanToST32() { +constexpr bool test_spanToST32() +{ int32_t i32 = 0; return spanToST("-2147483647"sv, i32) && i32 == -2'147'483'647; } static_assert(test_spanToST32(), "test_spanToST32 failed"); -constexpr bool test_spanToST64() { +constexpr bool test_spanToST64() +{ int64_t i64 = 0; return spanToST("9223372036854775807"sv, i64) && i64 == 9'223'372'036'854'775'807LL; } static_assert(test_spanToST64(), "test_spanToST64 failed"); -constexpr bool test_spanToST8Underflow() { +constexpr bool test_spanToST8Underflow() +{ int8_t i8 = 0; return !spanToST("-128"sv, i8); // Underflow } static_assert(test_spanToST8Underflow(), "test_spanToST8Underflow failed"); -constexpr bool test_spanToST8Overflow() { +constexpr bool test_spanToST8Overflow() +{ int8_t i8 = 0; return !spanToST("128"sv, i8); // Overflow } static_assert(test_spanToST8Overflow(), "test_spanToST8Overflow failed"); -constexpr bool test_spanToST16Underflow() { +constexpr bool test_spanToST16Underflow() +{ int16_t i16 = 0; return !spanToST("-32769"sv, i16); // Underflow } static_assert(test_spanToST16Underflow(), "test_spanToST16Underflow failed"); -constexpr bool test_spanToST16Overflow() { +constexpr bool test_spanToST16Overflow() +{ int16_t i16 = 0; return !spanToST("32768"sv, i16); // Overflow } static_assert(test_spanToST16Overflow(), "test_spanToST16Overflow failed"); -constexpr bool test_spanToST32Underflow() { +constexpr bool test_spanToST32Underflow() +{ int32_t i32 = 0; return !spanToST("-2147483649"sv, i32); // Underflow } static_assert(test_spanToST32Underflow(), "test_spanToST32Underflow failed"); -constexpr bool test_spanToST32Overflow() { +constexpr bool test_spanToST32Overflow() +{ int32_t i32 = 0; return !spanToST("2147483648"sv, i32); // Overflow } static_assert(test_spanToST32Overflow(), "test_spanToST32Overflow failed"); -constexpr bool test_spanToST64Underflow() { +constexpr bool test_spanToST64Underflow() +{ int64_t i64 = 0; return !spanToST("-9223372036854775809"sv, i64); // Underflow } static_assert(test_spanToST64Underflow(), "test_spanToST64Underflow failed"); -constexpr bool test_spanToST64Overflow() { +constexpr bool test_spanToST64Overflow() +{ int64_t i64 = 0; return !spanToST("9223372036854775808"sv, i64); // Overflow } static_assert(test_spanToST64Overflow(), "test_spanToST64Overflow failed"); -constexpr bool test_spanToSTEmptyString() { +constexpr bool test_spanToSTEmptyString() +{ int8_t i8 = 0; return !spanToST(""sv, i8); // Empty string } static_assert(test_spanToSTEmptyString(), "test_spanToSTEmptyString failed"); -constexpr bool test_spanToSTJustNegativeSign() { +constexpr bool test_spanToSTJustNegativeSign() +{ int16_t i16 = 0; return !spanToST("-"sv, i16); // Just a negative sign } static_assert(test_spanToSTJustNegativeSign(), "test_spanToSTJustNegativeSign failed"); -constexpr bool test_spanToSTNegativeZero() { +constexpr bool test_spanToSTNegativeZero() +{ int32_t i32 = 0; return !spanToST("-0"sv, i32); // Negative zero } static_assert(test_spanToSTNegativeZero(), "test_spanToSTNegativeZero failed"); -constexpr bool test_spanToSTInvalidCharacter() { +constexpr bool test_spanToSTInvalidCharacter() +{ int32_t i32 = 0; return !spanToST("+123"sv, i32); // Invalid character } static_assert(test_spanToSTInvalidCharacter(), "test_spanToSTInvalidCharacter failed"); -constexpr bool test_spanToSTLeadingSpace() { +constexpr bool test_spanToSTLeadingSpace() +{ int64_t i64 = 0; return !spanToST(" 123"sv, i64); // Leading space } static_assert(test_spanToSTLeadingSpace(), "test_spanToSTLeadingSpace failed"); -constexpr bool test_spanToSTTrailingSpace() { +constexpr bool test_spanToSTTrailingSpace() +{ int64_t i64 = 0; return !spanToST("123 "sv, i64); // Trailing space } static_assert(test_spanToSTTrailingSpace(), "test_spanToSTTrailingSpace failed"); -constexpr bool test_spanToSTLeadingZero() { +constexpr bool test_spanToSTLeadingZero() +{ int64_t i64 = 0; return !spanToST("0123"sv, i64); // Leading zero } diff --git a/src/PinPatternManager.cpp b/src/PinPatternManager.cpp index 37d1c11e..c6d8dd90 100644 --- a/src/PinPatternManager.cpp +++ b/src/PinPatternManager.cpp @@ -9,14 +9,19 @@ const char* const TAG = "PinPatternManager"; using namespace OpenShock; -PinPatternManager::PinPatternManager(gpio_num_t gpioPin) : m_gpioPin(GPIO_NUM_NC), m_pattern(), m_taskHandle(nullptr), m_taskMutex(xSemaphoreCreateMutex()) { +PinPatternManager::PinPatternManager(gpio_num_t gpioPin) + : m_gpioPin(GPIO_NUM_NC) + , m_pattern() + , m_taskHandle(nullptr) + , m_taskMutex(xSemaphoreCreateMutex()) +{ if (gpioPin == GPIO_NUM_NC) { OS_LOGE(TAG, "Pin is not set"); return; } if (!IsValidOutputPin(gpioPin)) { - OS_LOGE(TAG, "Pin %d is not a valid output pin", gpioPin); + OS_LOGE(TAG, "Pin %hhi is not a valid output pin", gpioPin); return; } @@ -27,14 +32,15 @@ PinPatternManager::PinPatternManager(gpio_num_t gpioPin) : m_gpioPin(GPIO_NUM_NC config.pull_down_en = GPIO_PULLDOWN_DISABLE; config.intr_type = GPIO_INTR_DISABLE; if (gpio_config(&config) != ESP_OK) { - OS_LOGE(TAG, "Failed to configure pin %d", gpioPin); + OS_LOGE(TAG, "Failed to configure pin %hhi", gpioPin); return; } m_gpioPin = gpioPin; } -PinPatternManager::~PinPatternManager() { +PinPatternManager::~PinPatternManager() +{ ClearPattern(); vSemaphoreDelete(m_taskMutex); @@ -44,7 +50,8 @@ PinPatternManager::~PinPatternManager() { } } -void PinPatternManager::SetPattern(const State* pattern, std::size_t patternLength) { +void PinPatternManager::SetPattern(const State* pattern, std::size_t patternLength) +{ ClearPatternInternal(); // Set new values @@ -52,12 +59,12 @@ void PinPatternManager::SetPattern(const State* pattern, std::size_t patternLeng std::copy(pattern, pattern + patternLength, m_pattern.begin()); char name[32]; - snprintf(name, sizeof(name), "PinPatternManager-%d", m_gpioPin); + snprintf(name, sizeof(name), "PinPatternManager-%hhi", m_gpioPin); // Start the task BaseType_t result = xTaskCreate(RunPattern, name, 1024, this, 1, &m_taskHandle); // PROFILED: 0.5KB stack usage if (result != pdPASS) { - OS_LOGE(TAG, "[pin-%u] Failed to create task: %d", m_gpioPin, result); + OS_LOGE(TAG, "[pin-%hhi] Failed to create task: %d", m_gpioPin, result); m_taskHandle = nullptr; m_pattern.clear(); @@ -67,12 +74,14 @@ void PinPatternManager::SetPattern(const State* pattern, std::size_t patternLeng xSemaphoreGive(m_taskMutex); } -void PinPatternManager::ClearPattern() { +void PinPatternManager::ClearPattern() +{ ClearPatternInternal(); xSemaphoreGive(m_taskMutex); } -void PinPatternManager::ClearPatternInternal() { +void PinPatternManager::ClearPatternInternal() +{ xSemaphoreTake(m_taskMutex, portMAX_DELAY); if (m_taskHandle != nullptr) { @@ -83,7 +92,8 @@ void PinPatternManager::ClearPatternInternal() { m_pattern.clear(); } -void PinPatternManager::RunPattern(void* arg) { +void PinPatternManager::RunPattern(void* arg) +{ PinPatternManager* thisPtr = reinterpret_cast(arg); gpio_num_t pin = thisPtr->m_gpioPin; diff --git a/src/RGBPatternManager.cpp b/src/RGBPatternManager.cpp index 6527ee55..7fdae6b3 100644 --- a/src/RGBPatternManager.cpp +++ b/src/RGBPatternManager.cpp @@ -16,38 +16,47 @@ using namespace OpenShock; // TODO: Support multiple LEDs ? // TODO: Support other LED types ? -RGBPatternManager::RGBPatternManager(gpio_num_t gpioPin) : m_gpioPin(GPIO_NUM_NC), m_brightness(255), m_pattern(), m_rmtHandle(nullptr), m_taskHandle(nullptr), m_taskMutex(xSemaphoreCreateMutex()) { +RGBPatternManager::RGBPatternManager(gpio_num_t gpioPin) + : m_gpioPin(GPIO_NUM_NC) + , m_brightness(255) + , m_pattern() + , m_rmtHandle(nullptr) + , m_taskHandle(nullptr) + , m_taskMutex(xSemaphoreCreateMutex()) +{ if (gpioPin == GPIO_NUM_NC) { OS_LOGE(TAG, "Pin is not set"); return; } if (!OpenShock::IsValidOutputPin(gpioPin)) { - OS_LOGE(TAG, "Pin %d is not a valid output pin", gpioPin); + OS_LOGE(TAG, "Pin %hhi is not a valid output pin", gpioPin); return; } m_rmtHandle = rmtInit(gpioPin, RMT_TX_MODE, RMT_MEM_64); if (m_rmtHandle == NULL) { - OS_LOGE(TAG, "Failed to initialize RMT for pin %d", gpioPin); + OS_LOGE(TAG, "Failed to initialize RMT for pin %hhi", gpioPin); return; } float realTick = rmtSetTick(m_rmtHandle, 100.F); - OS_LOGD(TAG, "RMT tick is %f ns for pin %d", realTick, gpioPin); + OS_LOGD(TAG, "RMT tick is %f ns for pin %hhi", realTick, gpioPin); SetBrightness(20); m_gpioPin = gpioPin; } -RGBPatternManager::~RGBPatternManager() { +RGBPatternManager::~RGBPatternManager() +{ ClearPattern(); vSemaphoreDelete(m_taskMutex); } -void RGBPatternManager::SetPattern(const RGBState* pattern, std::size_t patternLength) { +void RGBPatternManager::SetPattern(const RGBState* pattern, std::size_t patternLength) +{ ClearPatternInternal(); // Set new values @@ -57,7 +66,7 @@ void RGBPatternManager::SetPattern(const RGBState* pattern, std::size_t patternL // Start the task BaseType_t result = TaskUtils::TaskCreateExpensive(RunPattern, TAG, 4096, this, 1, &m_taskHandle); // PROFILED: 1.7KB stack usage if (result != pdPASS) { - OS_LOGE(TAG, "[pin-%u] Failed to create task: %d", m_gpioPin, result); + OS_LOGE(TAG, "[pin-%hhi] Failed to create task: %d", m_gpioPin, result); m_taskHandle = nullptr; m_pattern.clear(); @@ -67,12 +76,14 @@ void RGBPatternManager::SetPattern(const RGBState* pattern, std::size_t patternL xSemaphoreGive(m_taskMutex); } -void RGBPatternManager::ClearPattern() { +void RGBPatternManager::ClearPattern() +{ ClearPatternInternal(); xSemaphoreGive(m_taskMutex); } -void RGBPatternManager::ClearPatternInternal() { +void RGBPatternManager::ClearPatternInternal() +{ xSemaphoreTake(m_taskMutex, portMAX_DELAY); if (m_taskHandle != nullptr) { @@ -84,15 +95,17 @@ void RGBPatternManager::ClearPatternInternal() { } // Range: 0-255 -void RGBPatternManager::SetBrightness(uint8_t brightness) { +void RGBPatternManager::SetBrightness(uint8_t brightness) +{ m_brightness = brightness; } -void RGBPatternManager::RunPattern(void* arg) { +void RGBPatternManager::RunPattern(void* arg) +{ RGBPatternManager* thisPtr = reinterpret_cast(arg); rmt_obj_t* rmtHandle = thisPtr->m_rmtHandle; - uint8_t brightness = thisPtr->m_brightness; + uint8_t brightness = thisPtr->m_brightness; std::vector& pattern = thisPtr->m_pattern; std::array led_data; // 24 bits per LED (8 bits per color * 3 colors) diff --git a/src/config/Config.cpp b/src/config/Config.cpp index 7f30298a..18c7f496 100644 --- a/src/config/Config.cpp +++ b/src/config/Config.cpp @@ -42,7 +42,8 @@ static ReadWriteMutex _configMutex; #define CONFIG_LOCK_READ(retval) CONFIG_LOCK_READ_ACTION(retval, {}) #define CONFIG_LOCK_WRITE(retval) CONFIG_LOCK_WRITE_ACTION(retval, {}) -bool _tryDeserializeConfig(const uint8_t* buffer, std::size_t bufferLen, OpenShock::Config::RootConfig& config) { +bool _tryDeserializeConfig(const uint8_t* buffer, std::size_t bufferLen, OpenShock::Config::RootConfig& config) +{ if (buffer == nullptr || bufferLen == 0) { OS_LOGE(TAG, "Buffer is null or empty"); return false; @@ -73,7 +74,8 @@ bool _tryDeserializeConfig(const uint8_t* buffer, std::size_t bufferLen, OpenSho return true; } -bool _tryLoadConfig(std::vector& buffer) { +bool _tryLoadConfig(std::vector& buffer) +{ File file = _configFS.open("/config", "rb"); if (!file) { OS_LOGE(TAG, "Failed to open config file for reading"); @@ -96,7 +98,8 @@ bool _tryLoadConfig(std::vector& buffer) { return true; } -bool _tryLoadConfig() { +bool _tryLoadConfig() +{ std::vector buffer; if (!_tryLoadConfig(buffer)) { return false; @@ -104,7 +107,8 @@ bool _tryLoadConfig() { return _tryDeserializeConfig(buffer.data(), buffer.size(), _configData); } -bool _trySaveConfig(const uint8_t* data, std::size_t dataLen) { +bool _trySaveConfig(const uint8_t* data, std::size_t dataLen) +{ File file = _configFS.open("/config", "wb"); if (!file) { OS_LOGE(TAG, "Failed to open config file for writing"); @@ -121,7 +125,8 @@ bool _trySaveConfig(const uint8_t* data, std::size_t dataLen) { return true; } -bool _trySaveConfig() { +bool _trySaveConfig() +{ flatbuffers::FlatBufferBuilder builder; auto fbsConfig = _configData.ToFlatbuffers(builder, true); @@ -131,7 +136,8 @@ bool _trySaveConfig() { return _trySaveConfig(builder.GetBufferPointer(), builder.GetSize()); } -void Config::Init() { +void Config::Init() +{ CONFIG_LOCK_WRITE(); if (!_configFS.begin(true, "/config", 3, "config")) { @@ -151,13 +157,15 @@ void Config::Init() { } } -cJSON* _getAsCJSON(bool withSensitiveData) { +cJSON* _getAsCJSON(bool withSensitiveData) +{ CONFIG_LOCK_READ(nullptr); return _configData.ToJSON(withSensitiveData); } -std::string Config::GetAsJSON(bool withSensitiveData) { +std::string Config::GetAsJSON(bool withSensitiveData) +{ cJSON* root = _getAsCJSON(withSensitiveData); char* json = cJSON_PrintUnformatted(root); @@ -170,7 +178,8 @@ std::string Config::GetAsJSON(bool withSensitiveData) { return result; } -bool Config::SaveFromJSON(std::string_view json) { +bool Config::SaveFromJSON(std::string_view json) +{ cJSON* root = cJSON_ParseWithLength(json.data(), json.size()); if (root == nullptr) { OS_LOGE(TAG, "Failed to parse JSON: %s", cJSON_GetErrorPtr()); @@ -191,13 +200,15 @@ bool Config::SaveFromJSON(std::string_view json) { return _trySaveConfig(); } -flatbuffers::Offset Config::GetAsFlatBuffer(flatbuffers::FlatBufferBuilder& builder, bool withSensitiveData) { +flatbuffers::Offset Config::GetAsFlatBuffer(flatbuffers::FlatBufferBuilder& builder, bool withSensitiveData) +{ CONFIG_LOCK_READ(0); return _configData.ToFlatbuffers(builder, withSensitiveData); } -bool Config::SaveFromFlatBuffer(const Serialization::Configuration::HubConfig* config) { +bool Config::SaveFromFlatBuffer(const Serialization::Configuration::HubConfig* config) +{ CONFIG_LOCK_WRITE(false); if (!_configData.FromFlatbuffers(config)) { @@ -208,13 +219,15 @@ bool Config::SaveFromFlatBuffer(const Serialization::Configuration::HubConfig* c return _trySaveConfig(); } -bool Config::GetRaw(std::vector& buffer) { +bool Config::GetRaw(std::vector& buffer) +{ CONFIG_LOCK_READ(false); return _tryLoadConfig(buffer); } -bool Config::SetRaw(const uint8_t* buffer, std::size_t size) { +bool Config::SetRaw(const uint8_t* buffer, std::size_t size) +{ CONFIG_LOCK_WRITE(false); OpenShock::Config::RootConfig config; @@ -226,7 +239,8 @@ bool Config::SetRaw(const uint8_t* buffer, std::size_t size) { return _trySaveConfig(buffer, size); } -void Config::FactoryReset() { +void Config::FactoryReset() +{ CONFIG_LOCK_WRITE(); _configData.ToDefault(); @@ -242,7 +256,8 @@ void Config::FactoryReset() { OS_LOGI(TAG, "Factory reset complete"); } -bool Config::GetRFConfig(Config::RFConfig& out) { +bool Config::GetRFConfig(Config::RFConfig& out) +{ CONFIG_LOCK_READ(false); out = _configData.rf; @@ -250,7 +265,8 @@ bool Config::GetRFConfig(Config::RFConfig& out) { return true; } -bool Config::GetWiFiConfig(Config::WiFiConfig& out) { +bool Config::GetWiFiConfig(Config::WiFiConfig& out) +{ CONFIG_LOCK_READ(false); out = _configData.wifi; @@ -258,7 +274,8 @@ bool Config::GetWiFiConfig(Config::WiFiConfig& out) { return true; } -bool Config::GetCaptivePortalConfig(Config::CaptivePortalConfig& out) { +bool Config::GetCaptivePortalConfig(Config::CaptivePortalConfig& out) +{ CONFIG_LOCK_READ(false); out = _configData.captivePortal; @@ -266,7 +283,8 @@ bool Config::GetCaptivePortalConfig(Config::CaptivePortalConfig& out) { return true; } -bool Config::GetBackendConfig(Config::BackendConfig& out) { +bool Config::GetBackendConfig(Config::BackendConfig& out) +{ CONFIG_LOCK_READ(false); out = _configData.backend; @@ -274,7 +292,8 @@ bool Config::GetBackendConfig(Config::BackendConfig& out) { return true; } -bool Config::GetSerialInputConfig(Config::SerialInputConfig& out) { +bool Config::GetSerialInputConfig(Config::SerialInputConfig& out) +{ CONFIG_LOCK_READ(false); out = _configData.serialInput; @@ -282,7 +301,8 @@ bool Config::GetSerialInputConfig(Config::SerialInputConfig& out) { return true; } -bool Config::GetOtaUpdateConfig(Config::OtaUpdateConfig& out) { +bool Config::GetOtaUpdateConfig(Config::OtaUpdateConfig& out) +{ CONFIG_LOCK_READ(false); out = _configData.otaUpdate; @@ -290,7 +310,8 @@ bool Config::GetOtaUpdateConfig(Config::OtaUpdateConfig& out) { return true; } -bool Config::GetEStop(Config::EStopConfig& out) { +bool Config::GetEStop(Config::EStopConfig& out) +{ CONFIG_LOCK_READ(false); out = _configData.estop; @@ -298,56 +319,64 @@ bool Config::GetEStop(Config::EStopConfig& out) { return true; } -bool Config::SetRFConfig(const Config::RFConfig& config) { +bool Config::SetRFConfig(const Config::RFConfig& config) +{ CONFIG_LOCK_WRITE(false); _configData.rf = config; return _trySaveConfig(); } -bool Config::SetWiFiConfig(const Config::WiFiConfig& config) { +bool Config::SetWiFiConfig(const Config::WiFiConfig& config) +{ CONFIG_LOCK_WRITE(false); _configData.wifi = config; return _trySaveConfig(); } -bool Config::SetCaptivePortalConfig(const Config::CaptivePortalConfig& config) { +bool Config::SetCaptivePortalConfig(const Config::CaptivePortalConfig& config) +{ CONFIG_LOCK_WRITE(false); _configData.captivePortal = config; return _trySaveConfig(); } -bool Config::SetBackendConfig(const Config::BackendConfig& config) { +bool Config::SetBackendConfig(const Config::BackendConfig& config) +{ CONFIG_LOCK_WRITE(false); _configData.backend = config; return _trySaveConfig(); } -bool Config::SetSerialInputConfig(const Config::SerialInputConfig& config) { +bool Config::SetSerialInputConfig(const Config::SerialInputConfig& config) +{ CONFIG_LOCK_WRITE(false); _configData.serialInput = config; return _trySaveConfig(); } -bool Config::SetOtaUpdateConfig(const Config::OtaUpdateConfig& config) { +bool Config::SetOtaUpdateConfig(const Config::OtaUpdateConfig& config) +{ CONFIG_LOCK_WRITE(false); _configData.otaUpdate = config; return _trySaveConfig(); } -bool Config::SetEStop(const Config::EStopConfig& config) { +bool Config::SetEStop(const Config::EStopConfig& config) +{ CONFIG_LOCK_WRITE(false); _configData.estop = config; return _trySaveConfig(); } -bool Config::GetWiFiCredentials(std::vector& out) { +bool Config::GetWiFiCredentials(std::vector& out) +{ CONFIG_LOCK_READ(false); out = _configData.wifi.credentialsList; @@ -355,7 +384,8 @@ bool Config::GetWiFiCredentials(std::vector& out) { return true; } -bool Config::GetWiFiCredentials(cJSON* array, bool withSensitiveData) { +bool Config::GetWiFiCredentials(cJSON* array, bool withSensitiveData) +{ CONFIG_LOCK_READ(false); for (auto& creds : _configData.wifi.credentialsList) { @@ -367,7 +397,8 @@ bool Config::GetWiFiCredentials(cJSON* array, bool withSensitiveData) { return true; } -bool Config::SetWiFiCredentials(const std::vector& credentials) { +bool Config::SetWiFiCredentials(const std::vector& credentials) +{ bool foundZeroId = std::any_of(credentials.begin(), credentials.end(), [](const Config::WiFiCredentials& creds) { return creds.id == 0; }); if (foundZeroId) { OS_LOGE(TAG, "Cannot set WiFi credentials: credential ID cannot be 0"); @@ -380,7 +411,8 @@ bool Config::SetWiFiCredentials(const std::vector& cred return _trySaveConfig(); } -bool Config::GetRFConfigTxPin(uint8_t& out) { +bool Config::GetRFConfigTxPin(gpio_num_t& out) +{ CONFIG_LOCK_READ(false); out = _configData.rf.txPin; @@ -388,14 +420,16 @@ bool Config::GetRFConfigTxPin(uint8_t& out) { return true; } -bool Config::SetRFConfigTxPin(uint8_t txPin) { +bool Config::SetRFConfigTxPin(gpio_num_t txPin) +{ CONFIG_LOCK_WRITE(false); _configData.rf.txPin = txPin; return _trySaveConfig(); } -bool Config::GetRFConfigKeepAliveEnabled(bool& out) { +bool Config::GetRFConfigKeepAliveEnabled(bool& out) +{ CONFIG_LOCK_READ(false); out = _configData.rf.keepAliveEnabled; @@ -403,14 +437,16 @@ bool Config::GetRFConfigKeepAliveEnabled(bool& out) { return true; } -bool Config::SetRFConfigKeepAliveEnabled(bool enabled) { +bool Config::SetRFConfigKeepAliveEnabled(bool enabled) +{ CONFIG_LOCK_WRITE(false); _configData.rf.keepAliveEnabled = enabled; return _trySaveConfig(); } -bool Config::AnyWiFiCredentials(std::function predicate) { +bool Config::AnyWiFiCredentials(std::function predicate) +{ CONFIG_LOCK_READ(false); auto& creds = _configData.wifi.credentialsList; @@ -418,7 +454,8 @@ bool Config::AnyWiFiCredentials(std::function(OPENSHOCK_ESTOP_PIN); } -bool EStopConfig::FromFlatbuffers(const Serialization::Configuration::EStopConfig* config) { +bool EStopConfig::FromFlatbuffers(const Serialization::Configuration::EStopConfig* config) +{ if (config == nullptr) { - ToDefault(); // Set to default if config is null + ToDefault(); // Set to default if config is null return true; } gpioPin = static_cast(config->gpio_pin()); - if (OpenShock::IsValidInputPin(static_cast(gpioPin))) { + if (OpenShock::IsValidInputPin(static_cast(gpioPin))) { enabled = config->enabled(); } else { enabled = false; @@ -43,13 +45,15 @@ bool EStopConfig::FromFlatbuffers(const Serialization::Configuration::EStopConfi return true; } -flatbuffers::Offset EStopConfig::ToFlatbuffers(flatbuffers::FlatBufferBuilder& builder, bool withSensitiveData) const { +flatbuffers::Offset EStopConfig::ToFlatbuffers(flatbuffers::FlatBufferBuilder& builder, bool withSensitiveData) const +{ return Serialization::Configuration::CreateEStopConfig(builder, gpioPin); } -bool EStopConfig::FromJSON(const cJSON* json) { +bool EStopConfig::FromJSON(const cJSON* json) +{ if (json == nullptr) { - ToDefault(); // Set to default if config is null + ToDefault(); // Set to default if config is null return true; } @@ -58,13 +62,9 @@ bool EStopConfig::FromJSON(const cJSON* json) { return false; } - uint8_t val; - if (!Internal::Utils::FromJsonU8(val, json, "gpioPin", OPENSHOCK_ESTOP_PIN)) { - OS_LOGE(TAG, "Failed to parse gpioPin"); - return false; - } + Internal::Utils::FromJsonGpioNum(gpioPin, json, "gpioPin", static_cast(OPENSHOCK_ESTOP_PIN)); - if (!Internal::Utils::FromJsonBool(enabled, json, "enabled", OpenShock::IsValidInputPin(val))) { + if (!Internal::Utils::FromJsonBool(enabled, json, "enabled", OpenShock::IsValidInputPin(gpioPin))) { OS_LOGE(TAG, "Failed to parse enabled"); return false; } @@ -72,7 +72,8 @@ bool EStopConfig::FromJSON(const cJSON* json) { return true; } -cJSON* EStopConfig::ToJSON(bool withSensitiveData) const { +cJSON* EStopConfig::ToJSON(bool withSensitiveData) const +{ cJSON* root = cJSON_CreateObject(); cJSON_AddBoolToObject(root, "enabled", enabled); diff --git a/src/config/RFConfig.cpp b/src/config/RFConfig.cpp index dd5e5bbf..81d5080b 100644 --- a/src/config/RFConfig.cpp +++ b/src/config/RFConfig.cpp @@ -9,40 +9,44 @@ const char* const TAG = "Config::RFConfig"; using namespace OpenShock::Config; RFConfig::RFConfig() - : txPin(OPENSHOCK_RF_TX_GPIO) + : txPin(static_cast(OPENSHOCK_RF_TX_GPIO)) , keepAliveEnabled(true) { } -RFConfig::RFConfig(uint8_t txPin, bool keepAliveEnabled) +RFConfig::RFConfig(gpio_num_t txPin, bool keepAliveEnabled) : txPin(txPin) , keepAliveEnabled(keepAliveEnabled) { } -void RFConfig::ToDefault() { - txPin = OPENSHOCK_RF_TX_GPIO; +void RFConfig::ToDefault() +{ + txPin = static_cast(OPENSHOCK_RF_TX_GPIO); keepAliveEnabled = true; } -bool RFConfig::FromFlatbuffers(const Serialization::Configuration::RFConfig* config) { +bool RFConfig::FromFlatbuffers(const Serialization::Configuration::RFConfig* config) +{ if (config == nullptr) { OS_LOGW(TAG, "Config is null, setting to default"); ToDefault(); return true; } - txPin = config->tx_pin(); + Internal::Utils::FromU8GpioNum(txPin, config->tx_pin(), static_cast(OPENSHOCK_RF_TX_GPIO)); keepAliveEnabled = config->keepalive_enabled(); return true; } -flatbuffers::Offset RFConfig::ToFlatbuffers(flatbuffers::FlatBufferBuilder& builder, bool withSensitiveData) const { +flatbuffers::Offset RFConfig::ToFlatbuffers(flatbuffers::FlatBufferBuilder& builder, bool withSensitiveData) const +{ return Serialization::Configuration::CreateRFConfig(builder, txPin, keepAliveEnabled); } -bool RFConfig::FromJSON(const cJSON* json) { +bool RFConfig::FromJSON(const cJSON* json) +{ if (json == nullptr) { OS_LOGW(TAG, "Config is null, setting to default"); ToDefault(); @@ -54,16 +58,17 @@ bool RFConfig::FromJSON(const cJSON* json) { return false; } - Internal::Utils::FromJsonU8(txPin, json, "txPin", OPENSHOCK_RF_TX_GPIO); + Internal::Utils::FromJsonGpioNum(txPin, json, "txPin", static_cast(OPENSHOCK_RF_TX_GPIO)); Internal::Utils::FromJsonBool(keepAliveEnabled, json, "keepAliveEnabled", true); return true; } -cJSON* RFConfig::ToJSON(bool withSensitiveData) const { +cJSON* RFConfig::ToJSON(bool withSensitiveData) const +{ cJSON* root = cJSON_CreateObject(); - cJSON_AddNumberToObject(root, "txPin", txPin); //-V2564 + cJSON_AddNumberToObject(root, "txPin", static_cast(txPin)); //-V2564 cJSON_AddBoolToObject(root, "keepAliveEnabled", keepAliveEnabled); return root; diff --git a/src/config/internal/utils.cpp b/src/config/internal/utils.cpp index bfde56e2..8ccb1ad9 100644 --- a/src/config/internal/utils.cpp +++ b/src/config/internal/utils.cpp @@ -2,19 +2,20 @@ const char* const TAG = "Config::Internal::Utils"; +#include "Chipset.h" #include "Logging.h" #include "util/IPAddressUtils.h" using namespace OpenShock; template -bool _utilFromJsonInt(T& val, const cJSON* json, const char* name, T defaultVal, int minVal, int maxVal) { +bool _utilFromJsonInt(T& val, const cJSON* json, const char* name, T defaultVal, int minVal, int maxVal) +{ static_assert(std::is_integral::value, "T must be an integral type"); const cJSON* jsonVal = cJSON_GetObjectItemCaseSensitive(json, name); if (jsonVal == nullptr) { - val = defaultVal; - return true; + return false; } if (cJSON_IsNumber(jsonVal) == 0) { @@ -39,7 +40,27 @@ bool _utilFromJsonInt(T& val, const cJSON* json, const char* name, T defaultVal, return true; } -void Config::Internal::Utils::FromFbsStr(std::string& str, const flatbuffers::String* fbsStr, const char* defaultStr) { +bool Config::Internal::Utils::FromU8GpioNum(gpio_num_t& val, uint8_t u8Val) +{ + if (u8Val >= GPIO_NUM_MAX || !GPIO_IS_VALID_GPIO(u8Val)) { + OS_LOGE(TAG, "invalid GPIO number"); + return false; + } + + val = static_cast(u8Val); + + return true; +} + +void Config::Internal::Utils::FromU8GpioNum(gpio_num_t& val, uint8_t u8Val, gpio_num_t defaultVal) +{ + if (!FromU8GpioNum(val, u8Val)) { + val = defaultVal; + } +} + +void Config::Internal::Utils::FromFbsStr(std::string& str, const flatbuffers::String* fbsStr, const char* defaultStr) +{ if (fbsStr != nullptr) { str = fbsStr->c_str(); } else { @@ -47,10 +68,11 @@ void Config::Internal::Utils::FromFbsStr(std::string& str, const flatbuffers::St } } -bool Config::Internal::Utils::FromFbsIPAddress(IPAddress& ip, const flatbuffers::String* fbsIP, const IPAddress& defaultIP) { +bool Config::Internal::Utils::FromFbsIPAddress(IPAddress& ip, const flatbuffers::String* fbsIP, const IPAddress& defaultIP) +{ if (fbsIP == nullptr) { - OS_LOGE(TAG, "IP address is null"); - return false; + ip = defaultIP; + return true; } std::string_view view(*fbsIP); @@ -63,7 +85,8 @@ bool Config::Internal::Utils::FromFbsIPAddress(IPAddress& ip, const flatbuffers: return true; } -bool Config::Internal::Utils::FromJsonBool(bool& val, const cJSON* json, const char* name, bool defaultVal) { +bool Config::Internal::Utils::FromJsonBool(bool& val, const cJSON* json, const char* name, bool defaultVal) +{ const cJSON* jsonVal = cJSON_GetObjectItemCaseSensitive(json, name); if (jsonVal == nullptr) { val = defaultVal; @@ -80,23 +103,27 @@ bool Config::Internal::Utils::FromJsonBool(bool& val, const cJSON* json, const c return true; } -bool Config::Internal::Utils::FromJsonU8(uint8_t& val, const cJSON* json, const char* name, uint8_t defaultVal) { +bool Config::Internal::Utils::FromJsonU8(uint8_t& val, const cJSON* json, const char* name, uint8_t defaultVal) +{ return _utilFromJsonInt(val, json, name, defaultVal, 0, UINT8_MAX); } -bool Config::Internal::Utils::FromJsonU16(uint16_t& val, const cJSON* json, const char* name, uint16_t defaultVal) { +bool Config::Internal::Utils::FromJsonU16(uint16_t& val, const cJSON* json, const char* name, uint16_t defaultVal) +{ return _utilFromJsonInt(val, json, name, defaultVal, 0, UINT16_MAX); } -bool Config::Internal::Utils::FromJsonI32(int32_t& val, const cJSON* json, const char* name, int32_t defaultVal) { +bool Config::Internal::Utils::FromJsonI32(int32_t& val, const cJSON* json, const char* name, int32_t defaultVal) +{ return _utilFromJsonInt(val, json, name, defaultVal, INT32_MIN, INT32_MAX); } -bool Config::Internal::Utils::FromJsonStr(std::string& str, const cJSON* json, const char* name, const char* defaultStr) { +bool Config::Internal::Utils::FromJsonStr(std::string& str, const cJSON* json, const char* name) +{ const cJSON* jsonVal = cJSON_GetObjectItemCaseSensitive(json, name); if (jsonVal == nullptr) { - str = defaultStr; - return true; + OS_LOGE(TAG, "value at '%s' is null", name); + return false; } if (cJSON_IsString(jsonVal) == 0) { @@ -109,7 +136,15 @@ bool Config::Internal::Utils::FromJsonStr(std::string& str, const cJSON* json, c return true; } -bool Config::Internal::Utils::FromJsonIPAddress(IPAddress& ip, const cJSON* json, const char* name, const IPAddress& defaultIP) { +void Config::Internal::Utils::FromJsonStr(std::string& str, const cJSON* json, const char* name, const char* defaultStr) +{ + if (!FromJsonStr(str, json, name)) { + str = defaultStr; + } +} + +bool Config::Internal::Utils::FromJsonIPAddress(IPAddress& ip, const cJSON* json, const char* name) +{ const cJSON* jsonVal = cJSON_GetObjectItemCaseSensitive(json, name); if (jsonVal == nullptr) { OS_LOGE(TAG, "value at '%s' is null", name); @@ -130,3 +165,27 @@ bool Config::Internal::Utils::FromJsonIPAddress(IPAddress& ip, const cJSON* json return true; } + +void Config::Internal::Utils::FromJsonIPAddress(IPAddress& ip, const cJSON* json, const char* name, const IPAddress& defaultIP) +{ + if (!FromJsonIPAddress(ip, json, name)) { + ip = defaultIP; + } +} + +bool Config::Internal::Utils::FromJsonGpioNum(gpio_num_t& val, const cJSON* json, const char* name) +{ + uint8_t u8Val; + if (!FromJsonU8(u8Val, json, name, 0)) { + return false; + } + + return FromU8GpioNum(val, u8Val); +} + +void Config::Internal::Utils::FromJsonGpioNum(gpio_num_t& val, const cJSON* json, const char* name, gpio_num_t defaultVal) +{ + if (!FromJsonGpioNum(val, json, name)) { + val = defaultVal; + } +} diff --git a/src/event_handlers/websocket/local/SetEStopPinCommand.cpp b/src/event_handlers/websocket/local/SetEStopPinCommand.cpp index 2535a9fe..1ffebba2 100644 --- a/src/event_handlers/websocket/local/SetEStopPinCommand.cpp +++ b/src/event_handlers/websocket/local/SetEStopPinCommand.cpp @@ -9,10 +9,11 @@ const char* const TAG = "LocalMessageHandlers"; -void serializeSetEStopPinResult(uint8_t socketId, uint8_t pin, OpenShock::Serialization::Local::SetGPIOResultCode result) { +void serializeSetEStopPinResult(uint8_t socketId, gpio_num_t pin, OpenShock::Serialization::Local::SetGPIOResultCode result) +{ flatbuffers::FlatBufferBuilder builder(1024); - auto responseOffset = OpenShock::Serialization::Local::CreateSetEstopPinCommandResult(builder, pin, result); + auto responseOffset = OpenShock::Serialization::Local::CreateSetEstopPinCommandResult(builder, static_cast(pin), result); auto msg = OpenShock::Serialization::Local::CreateHubToLocalMessage(builder, OpenShock::Serialization::Local::HubToLocalMessagePayload::SetEstopPinCommandResult, responseOffset.Union()); @@ -26,7 +27,8 @@ void serializeSetEStopPinResult(uint8_t socketId, uint8_t pin, OpenShock::Serial using namespace OpenShock::MessageHandlers::Local; -void _Private::HandleSetEstopPinCommand(uint8_t socketId, const OpenShock::Serialization::Local::LocalToHubMessage* root) { +void _Private::HandleSetEstopPinCommand(uint8_t socketId, const OpenShock::Serialization::Local::LocalToHubMessage* root) +{ auto msg = root->payload_as_SetEstopPinCommand(); if (msg == nullptr) { OS_LOGE(TAG, "Payload cannot be parsed as SetEstopPinCommand"); @@ -35,7 +37,7 @@ void _Private::HandleSetEstopPinCommand(uint8_t socketId, const OpenShock::Seria auto pin = msg->pin(); - auto result = OpenShock::CommandHandler::SetEstopPin(pin); + auto result = OpenShock::CommandHandler::SetEstopPin(static_cast(pin)); - serializeSetEStopPinResult(socketId, pin, result); + serializeSetEStopPinResult(socketId, static_cast(pin), result); } diff --git a/src/event_handlers/websocket/local/SetRfTxPinCommand.cpp b/src/event_handlers/websocket/local/SetRfTxPinCommand.cpp index db7fb08d..490215f9 100644 --- a/src/event_handlers/websocket/local/SetRfTxPinCommand.cpp +++ b/src/event_handlers/websocket/local/SetRfTxPinCommand.cpp @@ -9,10 +9,11 @@ const char* const TAG = "LocalMessageHandlers"; #include -void serializeSetRfTxPinResult(uint8_t socketId, uint8_t pin, OpenShock::Serialization::Local::SetGPIOResultCode result) { +void serializeSetRfTxPinResult(uint8_t socketId, gpio_num_t pin, OpenShock::Serialization::Local::SetGPIOResultCode result) +{ flatbuffers::FlatBufferBuilder builder(1024); - auto responseOffset = OpenShock::Serialization::Local::CreateSetRfTxPinCommandResult(builder, pin, result); + auto responseOffset = OpenShock::Serialization::Local::CreateSetRfTxPinCommandResult(builder, static_cast(pin), result); auto msg = OpenShock::Serialization::Local::CreateHubToLocalMessage(builder, OpenShock::Serialization::Local::HubToLocalMessagePayload::SetRfTxPinCommandResult, responseOffset.Union()); @@ -26,7 +27,8 @@ void serializeSetRfTxPinResult(uint8_t socketId, uint8_t pin, OpenShock::Seriali using namespace OpenShock::MessageHandlers::Local; -void _Private::HandleSetRfTxPinCommand(uint8_t socketId, const OpenShock::Serialization::Local::LocalToHubMessage* root) { +void _Private::HandleSetRfTxPinCommand(uint8_t socketId, const OpenShock::Serialization::Local::LocalToHubMessage* root) +{ auto msg = root->payload_as_SetRfTxPinCommand(); if (msg == nullptr) { OS_LOGE(TAG, "Payload cannot be parsed as SetRfTxPinCommand"); @@ -35,7 +37,7 @@ void _Private::HandleSetRfTxPinCommand(uint8_t socketId, const OpenShock::Serial auto pin = msg->pin(); - auto result = OpenShock::CommandHandler::SetRfTxPin(pin); + auto result = OpenShock::CommandHandler::SetRfTxPin(static_cast(pin)); - serializeSetRfTxPinResult(socketId, pin, result); + serializeSetRfTxPinResult(socketId, static_cast(pin), result); } diff --git a/src/radio/RFTransmitter.cpp b/src/radio/RFTransmitter.cpp index 64121436..2391c6e1 100644 --- a/src/radio/RFTransmitter.cpp +++ b/src/radio/RFTransmitter.cpp @@ -14,10 +14,10 @@ const char* const TAG = "RFTransmitter"; #include -const UBaseType_t RFTRANSMITTER_QUEUE_SIZE = 64; -const BaseType_t RFTRANSMITTER_TASK_PRIORITY = 1; +const UBaseType_t RFTRANSMITTER_QUEUE_SIZE = 64; +const BaseType_t RFTRANSMITTER_TASK_PRIORITY = 1; const uint32_t RFTRANSMITTER_TASK_STACK_SIZE = 4096; // PROFILED: 1.4KB stack usage -const float RFTRANSMITTER_TICKRATE_NS = 1000; +const float RFTRANSMITTER_TICKRATE_NS = 1000; const int64_t TRANSMIT_END_DURATION = 300; using namespace OpenShock; @@ -30,22 +30,27 @@ struct command_t { bool overwrite; }; -RFTransmitter::RFTransmitter(uint8_t gpioPin) : m_txPin(gpioPin), m_rmtHandle(nullptr), m_queueHandle(nullptr), m_taskHandle(nullptr) { - OS_LOGD(TAG, "[pin-%u] Creating RFTransmitter", m_txPin); +RFTransmitter::RFTransmitter(gpio_num_t gpioPin) + : m_txPin(gpioPin) + , m_rmtHandle(nullptr) + , m_queueHandle(nullptr) + , m_taskHandle(nullptr) +{ + OS_LOGD(TAG, "[pin-%hhi] Creating RFTransmitter", m_txPin); - m_rmtHandle = rmtInit(gpioPin, RMT_TX_MODE, RMT_MEM_64); + m_rmtHandle = rmtInit(static_cast(m_txPin), RMT_TX_MODE, RMT_MEM_64); if (m_rmtHandle == nullptr) { - OS_LOGE(TAG, "[pin-%u] Failed to create rmt object", m_txPin); + OS_LOGE(TAG, "[pin-%hhi] Failed to create rmt object", m_txPin); destroy(); return; } float realTick = rmtSetTick(m_rmtHandle, RFTRANSMITTER_TICKRATE_NS); - OS_LOGD(TAG, "[pin-%u] real tick set to: %fns", m_txPin, realTick); + OS_LOGD(TAG, "[pin-%hhi] real tick set to: %fns", m_txPin, realTick); m_queueHandle = xQueueCreate(RFTRANSMITTER_QUEUE_SIZE, sizeof(command_t*)); if (m_queueHandle == nullptr) { - OS_LOGE(TAG, "[pin-%u] Failed to create queue", m_txPin); + OS_LOGE(TAG, "[pin-%hhi] Failed to create queue", m_txPin); destroy(); return; } @@ -54,19 +59,21 @@ RFTransmitter::RFTransmitter(uint8_t gpioPin) : m_txPin(gpioPin), m_rmtHandle(nu snprintf(name, sizeof(name), "RFTransmitter-%u", m_txPin); if (TaskUtils::TaskCreateExpensive(&Util::FnProxy<&RFTransmitter::TransmitTask>, name, RFTRANSMITTER_TASK_STACK_SIZE, this, RFTRANSMITTER_TASK_PRIORITY, &m_taskHandle) != pdPASS) { - OS_LOGE(TAG, "[pin-%u] Failed to create task", m_txPin); + OS_LOGE(TAG, "[pin-%hhi] Failed to create task", m_txPin); destroy(); return; } } -RFTransmitter::~RFTransmitter() { +RFTransmitter::~RFTransmitter() +{ destroy(); } -bool RFTransmitter::SendCommand(ShockerModelType model, uint16_t shockerId, ShockerCommandType type, uint8_t intensity, uint16_t durationMs, bool overwriteExisting) { +bool RFTransmitter::SendCommand(ShockerModelType model, uint16_t shockerId, ShockerCommandType type, uint8_t intensity, uint16_t durationMs, bool overwriteExisting) +{ if (m_queueHandle == nullptr) { - OS_LOGE(TAG, "[pin-%u] Queue is null", m_txPin); + OS_LOGE(TAG, "[pin-%hhi] Queue is null", m_txPin); return false; } @@ -74,13 +81,13 @@ bool RFTransmitter::SendCommand(ShockerModelType model, uint16_t shockerId, Shoc // We will use nullptr commands to end the task, if we got a nullptr here, we are out of memory... :( if (cmd == nullptr) { - OS_LOGE(TAG, "[pin-%u] Failed to allocate command", m_txPin); + OS_LOGE(TAG, "[pin-%hhi] Failed to allocate command", m_txPin); return false; } // Add the command to the queue, wait max 10 ms (Adjust this) if (xQueueSend(m_queueHandle, &cmd, pdMS_TO_TICKS(10)) != pdTRUE) { - OS_LOGE(TAG, "[pin-%u] Failed to send command to queue", m_txPin); + OS_LOGE(TAG, "[pin-%hhi] Failed to send command to queue", m_txPin); delete cmd; return false; } @@ -88,12 +95,13 @@ bool RFTransmitter::SendCommand(ShockerModelType model, uint16_t shockerId, Shoc return true; } -void RFTransmitter::ClearPendingCommands() { +void RFTransmitter::ClearPendingCommands() +{ if (m_queueHandle == nullptr) { return; } - OS_LOGI(TAG, "[pin-%u] Clearing pending commands", m_txPin); + OS_LOGI(TAG, "[pin-%hhi] Clearing pending commands", m_txPin); command_t* command; while (xQueueReceive(m_queueHandle, &command, 0) == pdPASS) { @@ -101,9 +109,10 @@ void RFTransmitter::ClearPendingCommands() { } } -void RFTransmitter::destroy() { +void RFTransmitter::destroy() +{ if (m_taskHandle != nullptr) { - OS_LOGD(TAG, "[pin-%u] Stopping task", m_txPin); + OS_LOGD(TAG, "[pin-%hhi] Stopping task", m_txPin); // Wait for the task to stop command_t* cmd = nullptr; @@ -114,7 +123,7 @@ void RFTransmitter::destroy() { xQueueSend(m_queueHandle, &cmd, pdMS_TO_TICKS(10)); } - OS_LOGD(TAG, "[pin-%u] Task stopped", m_txPin); + OS_LOGD(TAG, "[pin-%hhi] Task stopped", m_txPin); // Clear the queue ClearPendingCommands(); @@ -131,8 +140,9 @@ void RFTransmitter::destroy() { } } -void RFTransmitter::TransmitTask() { - OS_LOGD(TAG, "[pin-%u] RMT loop running on core %d", m_txPin, xPortGetCoreID()); +void RFTransmitter::TransmitTask() +{ + OS_LOGD(TAG, "[pin-%hhi] RMT loop running on core %d", m_txPin, xPortGetCoreID()); std::vector commands; while (true) { @@ -140,13 +150,13 @@ void RFTransmitter::TransmitTask() { command_t* cmd = nullptr; while (xQueueReceive(m_queueHandle, &cmd, commands.empty() ? portMAX_DELAY : 0) == pdTRUE) { if (cmd == nullptr) { - OS_LOGD(TAG, "[pin-%u] Received nullptr (stop command), cleaning up...", m_txPin); + OS_LOGD(TAG, "[pin-%hhi] Received nullptr (stop command), cleaning up...", m_txPin); for (auto it = commands.begin(); it != commands.end(); ++it) { delete *it; } - OS_LOGD(TAG, "[pin-%u] Cleanup done, stopping task", m_txPin); + OS_LOGD(TAG, "[pin-%hhi] Cleanup done, stopping task", m_txPin); vTaskDelete(nullptr); return; diff --git a/src/serial/command_handlers/estoppin.cpp b/src/serial/command_handlers/estoppin.cpp index 237a9e1f..c0fe1e78 100644 --- a/src/serial/command_handlers/estoppin.cpp +++ b/src/serial/command_handlers/estoppin.cpp @@ -4,9 +4,10 @@ #include "Convert.h" #include "EStopManager.h" -void _handleEStopPinCommand(std::string_view arg, bool isAutomated) { +void _handleEStopPinCommand(std::string_view arg, bool isAutomated) +{ + gpio_num_t estopPin; if (arg.empty()) { - gpio_num_t estopPin; if (!OpenShock::Config::GetEStopGpioPin(estopPin)) { SERPR_ERROR("Failed to get EStop pin from config"); return; @@ -17,14 +18,11 @@ void _handleEStopPinCommand(std::string_view arg, bool isAutomated) { return; } - uint8_t pin; - if (!OpenShock::Convert::ToUint8(arg, pin)) { + if (!OpenShock::Convert::ToGpioNum(arg, estopPin)) { SERPR_ERROR("Invalid argument (number invalid or out of range)"); return; } - gpio_num_t estopPin = static_cast(pin); - if (!OpenShock::EStopManager::SetEStopPin(estopPin)) { SERPR_ERROR("Failed to set EStop pin"); return; @@ -38,7 +36,8 @@ void _handleEStopPinCommand(std::string_view arg, bool isAutomated) { SERPR_SUCCESS("Saved config"); } -OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::ESStopPinHandler() { +OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::ESStopPinHandler() +{ auto group = OpenShock::Serial::CommandGroup("estoppin"sv); auto& getCommand = group.addCommand("Get the GPIO pin used for the E-Stop."sv, _handleEStopPinCommand); diff --git a/src/serial/command_handlers/rftxpin.cpp b/src/serial/command_handlers/rftxpin.cpp index 356fc097..0155f3ea 100644 --- a/src/serial/command_handlers/rftxpin.cpp +++ b/src/serial/command_handlers/rftxpin.cpp @@ -5,25 +5,26 @@ #include "Convert.h" #include "SetGPIOResultCode.h" -void _handleRfTxPinCommand(std::string_view arg, bool isAutomated) { +void _handleRfTxPinCommand(std::string_view arg, bool isAutomated) +{ + gpio_num_t txPin; + if (arg.empty()) { - uint8_t txPin; if (!OpenShock::Config::GetRFConfigTxPin(txPin)) { SERPR_ERROR("Failed to get RF TX pin from config"); return; } // Get rmt pin - SERPR_RESPONSE("RmtPin|%u", txPin); + SERPR_RESPONSE("RmtPin|%hhi", static_cast(txPin)); return; } - uint8_t pin; - if (!OpenShock::Convert::ToUint8(arg, pin)) { + if (!OpenShock::Convert::ToGpioNum(arg, txPin)) { SERPR_ERROR("Invalid argument (number invalid or out of range)"); } - OpenShock::SetGPIOResultCode result = OpenShock::CommandHandler::SetRfTxPin(static_cast(pin)); + OpenShock::SetGPIOResultCode result = OpenShock::CommandHandler::SetRfTxPin(txPin); switch (result) { case OpenShock::SetGPIOResultCode::InvalidPin: @@ -44,7 +45,8 @@ void _handleRfTxPinCommand(std::string_view arg, bool isAutomated) { } } -OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::RfTxPinHandler() { +OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::RfTxPinHandler() +{ auto group = OpenShock::Serial::CommandGroup("rftxpin"sv); auto& getCommand = group.addCommand("Get the GPIO pin used for the radio transmitter"sv, _handleRfTxPinCommand); From 2682b937b37ac23a9b513b14637e07777e372ef2 Mon Sep 17 00:00:00 2001 From: hhvrc Date: Mon, 7 Oct 2024 23:44:23 +0200 Subject: [PATCH 2/7] Bump schema submodule --- .gitmodules | 1 - schemas | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index e4a756bb..dafad5fb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,3 @@ [submodule "schemas"] path = schemas url = https://github.com/OpenShock/flatbuffers-schemas - branch = feature/better-gpio-pin-handling diff --git a/schemas b/schemas index 9a4dd475..58c0c64c 160000 --- a/schemas +++ b/schemas @@ -1 +1 @@ -Subproject commit 9a4dd4754d45990ec8ccbfb3d5c429f676760e82 +Subproject commit 58c0c64cc3f9755323d5438c76778c8abe8153c6 From 0d11ed70225d18569eeb0bfc7f55303cc767e2b8 Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Tue, 8 Oct 2024 00:51:58 +0200 Subject: [PATCH 3/7] Rationalize 998DR logic (#296) * Try to make sense of 998DR encoder --- src/radio/rmt/Petrainer998DREncoder.cpp | 61 ++++++++++++------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/src/radio/rmt/Petrainer998DREncoder.cpp b/src/radio/rmt/Petrainer998DREncoder.cpp index 77c75def..d8fa3950 100644 --- a/src/radio/rmt/Petrainer998DREncoder.cpp +++ b/src/radio/rmt/Petrainer998DREncoder.cpp @@ -5,55 +5,52 @@ const rmt_data_t kRmtPreamble = {1500, 1, 750, 0}; const rmt_data_t kRmtOne = {750, 1, 250, 0}; const rmt_data_t kRmtZero = {250, 1, 750, 0}; -const rmt_data_t kRmtPostamble = {1500, 0, 1500, 0}; // Some subvariants expect a quiet period between commands +const rmt_data_t kRmtPostamble = {1500, 0, 1500, 0}; // Some subvariants expect a quiet period between commands using namespace OpenShock; -std::vector Rmt::Petrainer998DREncoder::GetSequence(uint16_t shockerId, ShockerCommandType type, uint8_t intensity) { +std::vector Rmt::Petrainer998DREncoder::GetSequence(uint16_t shockerId, ShockerCommandType type, uint8_t intensity) +{ // Intensity must be between 0 and 100 intensity = std::min(intensity, static_cast(100)); - uint8_t typeVal = 0; - // typeInvert has the value of typeVal but bits are reversed and inverted - uint8_t typeInvert = 0; + int typeShift = 0; switch (type) { - case ShockerCommandType::Shock: - typeVal = 0b0001; - typeInvert = 0b0111; - break; - case ShockerCommandType::Vibrate: - typeVal = 0b0010; - typeInvert = 0b1011; - break; - case ShockerCommandType::Sound: - typeVal = 0b0100; - typeInvert = 0b1101; - break; - // case ShockerCommandType::Light: - // typeVal = 0b1000; - // typeInvert = 0b1110; - // break; - default: - return {}; // Invalid type + case ShockerCommandType::Shock: + typeShift = 0; + break; + case ShockerCommandType::Vibrate: + typeShift = 1; + break; + case ShockerCommandType::Sound: + typeShift = 2; + break; + // case ShockerCommandType::Light: + // nShift = 3; + // break; + default: + return {}; // Invalid type } + uint8_t typeVal = 0b0001 << typeShift; + uint8_t typeInvert = ~(0b1000 >> typeShift); + // TODO: Channel argument? - // Can be [000] or [111], 3 bits wide - uint8_t channel = 0b000; - uint8_t channelInvert = 0b111; + uint8_t channel = 0b1000; // Can be [1000] or [1111], 4 bits wide + uint8_t channelInvert = 0b1110; // Can be [1110] or [0000], 4 bits wide - // Payload layout: [channel:3][typeVal:4][shockerID:17][intensity:7][typeInvert:4][channelInvert:3] - uint64_t data = (static_cast(channel & 0b111) << 35 | static_cast(typeVal & 0b1111) << 31 | static_cast(shockerId & 0x1FFFF) << 14 | static_cast(intensity & 0x7F) << 7 | static_cast(typeInvert & 0b1111) << 3 | static_cast(channelInvert & 0b111)); + // TODO: Below ShockerID is 17 bits wide, and intensity is 7 bits wide. This is weird as ShockerID has 1 more bit and intensity has 1 less bit than 8 bit alignment. This needs investigation. + // Payload layout: [channel:4][typeVal:4][shockerID:17][intensity:7][typeInvert:4][channelInvert:4] (40 bits) + uint64_t data + = (static_cast(channel & 0b1111) << 36 | static_cast(typeVal & 0b1111) << 32 | static_cast(shockerId & 0x1FFFF) << 15 | static_cast(intensity & 0x7F) << 8 | static_cast(typeInvert & 0b1111) << 4 | static_cast(channelInvert & 0b1111)); std::vector pulses; pulses.reserve(43); // Generate the sequence pulses.push_back(kRmtPreamble); - pulses.push_back(kRmtOne); - Internal::EncodeBits<38>(pulses, data, kRmtOne, kRmtZero); - pulses.push_back(kRmtZero); - pulses.push_back(kRmtZero); + Internal::EncodeBits<40>(pulses, data, kRmtOne, kRmtZero); + pulses.push_back(kRmtZero); // Idk why this is here, the decoded protocol has it pulses.push_back(kRmtPostamble); return pulses; From 197051c504b785b1d6e685c9084c6af08a4f74bb Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Tue, 8 Oct 2024 02:31:57 +0200 Subject: [PATCH 4/7] Report available GPIO pins to frontend (#277) * Report available GPIO pins to frontend * Improve GPIO Availability checker methods * Move GpioPinSelector into a component * Improve GpioPinSelector * Improve pin safety on frontend * Bump schema submodule --- frontend/src/lib/MessageHandlers/index.ts | 10 +++ .../serialization/local/ready-message.ts | 74 ++++++++++++++++++- .../src/lib/components/GpioPinSelector.svelte | 46 ++++++++++++ frontend/src/lib/stores/DeviceStateStore.ts | 2 + frontend/src/lib/stores/UsedPinsStore.ts | 21 ++++++ frontend/src/lib/types/DeviceState.ts | 2 + frontend/src/routes/+page.svelte | 42 +---------- include/Chipset.h | 58 +++++++++++++-- .../_fbs/HubToLocalMessage_generated.h | 46 +++++++++++- schemas | 2 +- src/serialization/WSLocal.cpp | 9 ++- 11 files changed, 263 insertions(+), 49 deletions(-) create mode 100644 frontend/src/lib/components/GpioPinSelector.svelte create mode 100644 frontend/src/lib/stores/UsedPinsStore.ts diff --git a/frontend/src/lib/MessageHandlers/index.ts b/frontend/src/lib/MessageHandlers/index.ts index 999c3cd3..098d1530 100644 --- a/frontend/src/lib/MessageHandlers/index.ts +++ b/frontend/src/lib/MessageHandlers/index.ts @@ -38,6 +38,16 @@ PayloadHandlers[HubToLocalMessagePayload.ReadyMessage] = (cli, msg) => { store.accountLinked = payload.accountLinked(); store.config = mapConfig(payload.config()); + const gpioValidInputs = payload.gpioValidInputsArray(); + if (gpioValidInputs) { + store.gpioValidInputs = gpioValidInputs; + } + + const gpioValidOutputs = payload.gpioValidOutputsArray(); + if (gpioValidOutputs) { + store.gpioValidOutputs = gpioValidOutputs; + } + console.log('[WS] Updated device state store: ', store); return store; diff --git a/frontend/src/lib/_fbs/open-shock/serialization/local/ready-message.ts b/frontend/src/lib/_fbs/open-shock/serialization/local/ready-message.ts index aee82d90..4175feab 100644 --- a/frontend/src/lib/_fbs/open-shock/serialization/local/ready-message.ts +++ b/frontend/src/lib/_fbs/open-shock/serialization/local/ready-message.ts @@ -46,8 +46,38 @@ config(obj?:HubConfig):HubConfig|null { return offset ? (obj || new HubConfig()).__init(this.bb!.__indirect(this.bb_pos + offset), this.bb!) : null; } +gpioValidInputs(index: number):number|null { + const offset = this.bb!.__offset(this.bb_pos, 12); + return offset ? this.bb!.readInt8(this.bb!.__vector(this.bb_pos + offset) + index) : 0; +} + +gpioValidInputsLength():number { + const offset = this.bb!.__offset(this.bb_pos, 12); + return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0; +} + +gpioValidInputsArray():Int8Array|null { + const offset = this.bb!.__offset(this.bb_pos, 12); + return offset ? new Int8Array(this.bb!.bytes().buffer, this.bb!.bytes().byteOffset + this.bb!.__vector(this.bb_pos + offset), this.bb!.__vector_len(this.bb_pos + offset)) : null; +} + +gpioValidOutputs(index: number):number|null { + const offset = this.bb!.__offset(this.bb_pos, 14); + return offset ? this.bb!.readInt8(this.bb!.__vector(this.bb_pos + offset) + index) : 0; +} + +gpioValidOutputsLength():number { + const offset = this.bb!.__offset(this.bb_pos, 14); + return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0; +} + +gpioValidOutputsArray():Int8Array|null { + const offset = this.bb!.__offset(this.bb_pos, 14); + return offset ? new Int8Array(this.bb!.bytes().buffer, this.bb!.bytes().byteOffset + this.bb!.__vector(this.bb_pos + offset), this.bb!.__vector_len(this.bb_pos + offset)) : null; +} + static startReadyMessage(builder:flatbuffers.Builder) { - builder.startObject(4); + builder.startObject(6); } static addPoggies(builder:flatbuffers.Builder, poggies:boolean) { @@ -66,6 +96,48 @@ static addConfig(builder:flatbuffers.Builder, configOffset:flatbuffers.Offset) { builder.addFieldOffset(3, configOffset, 0); } +static addGpioValidInputs(builder:flatbuffers.Builder, gpioValidInputsOffset:flatbuffers.Offset) { + builder.addFieldOffset(4, gpioValidInputsOffset, 0); +} + +static createGpioValidInputsVector(builder:flatbuffers.Builder, data:number[]|Int8Array):flatbuffers.Offset; +/** + * @deprecated This Uint8Array overload will be removed in the future. + */ +static createGpioValidInputsVector(builder:flatbuffers.Builder, data:number[]|Uint8Array):flatbuffers.Offset; +static createGpioValidInputsVector(builder:flatbuffers.Builder, data:number[]|Int8Array|Uint8Array):flatbuffers.Offset { + builder.startVector(1, data.length, 1); + for (let i = data.length - 1; i >= 0; i--) { + builder.addInt8(data[i]!); + } + return builder.endVector(); +} + +static startGpioValidInputsVector(builder:flatbuffers.Builder, numElems:number) { + builder.startVector(1, numElems, 1); +} + +static addGpioValidOutputs(builder:flatbuffers.Builder, gpioValidOutputsOffset:flatbuffers.Offset) { + builder.addFieldOffset(5, gpioValidOutputsOffset, 0); +} + +static createGpioValidOutputsVector(builder:flatbuffers.Builder, data:number[]|Int8Array):flatbuffers.Offset; +/** + * @deprecated This Uint8Array overload will be removed in the future. + */ +static createGpioValidOutputsVector(builder:flatbuffers.Builder, data:number[]|Uint8Array):flatbuffers.Offset; +static createGpioValidOutputsVector(builder:flatbuffers.Builder, data:number[]|Int8Array|Uint8Array):flatbuffers.Offset { + builder.startVector(1, data.length, 1); + for (let i = data.length - 1; i >= 0; i--) { + builder.addInt8(data[i]!); + } + return builder.endVector(); +} + +static startGpioValidOutputsVector(builder:flatbuffers.Builder, numElems:number) { + builder.startVector(1, numElems, 1); +} + static endReadyMessage(builder:flatbuffers.Builder):flatbuffers.Offset { const offset = builder.endObject(); return offset; diff --git a/frontend/src/lib/components/GpioPinSelector.svelte b/frontend/src/lib/components/GpioPinSelector.svelte new file mode 100644 index 00000000..6c5d1766 --- /dev/null +++ b/frontend/src/lib/components/GpioPinSelector.svelte @@ -0,0 +1,46 @@ + + +
+
+

{name}

+ {statusText} +
+
+ + +
+
diff --git a/frontend/src/lib/stores/DeviceStateStore.ts b/frontend/src/lib/stores/DeviceStateStore.ts index c73cfcce..ff804183 100644 --- a/frontend/src/lib/stores/DeviceStateStore.ts +++ b/frontend/src/lib/stores/DeviceStateStore.ts @@ -10,6 +10,8 @@ const { subscribe, update } = writable({ wifiNetworkGroups: new Map(), accountLinked: false, config: null, + gpioValidInputs: new Int8Array(), + gpioValidOutputs: new Int8Array(), }); function insertSorted(array: T[], value: T, compare: (a: T, b: T) => number) { diff --git a/frontend/src/lib/stores/UsedPinsStore.ts b/frontend/src/lib/stores/UsedPinsStore.ts new file mode 100644 index 00000000..af5fdd20 --- /dev/null +++ b/frontend/src/lib/stores/UsedPinsStore.ts @@ -0,0 +1,21 @@ +import { writable } from "svelte/store"; + +const { subscribe, update } = writable>(new Map()); + +export const UsedPinsStore = { + subscribe, + markPinUsed(pin: number, name: string) { + update((store) => { + // Remove any existing entries with the same pin or name + for (const [key, value] of store) { + if (key === pin || value === name) { + store.delete(key); + } + } + + store.set(pin, name); + + return store; + }); + } +}; \ No newline at end of file diff --git a/frontend/src/lib/types/DeviceState.ts b/frontend/src/lib/types/DeviceState.ts index 6a698394..4898a1ad 100644 --- a/frontend/src/lib/types/DeviceState.ts +++ b/frontend/src/lib/types/DeviceState.ts @@ -9,4 +9,6 @@ export type DeviceState = { wifiNetworkGroups: Map; accountLinked: boolean; config: Config | null; + gpioValidInputs: Int8Array; + gpioValidOutputs: Int8Array; }; diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index 5523a811..bbfed90e 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -1,9 +1,9 @@
@@ -57,28 +39,10 @@
-
-
-

RF TX Pin

- (Currently {$DeviceStateStore.config?.rf == null ? ' unavailable' : $DeviceStateStore.config.rf.txPin}) -
-
- - -
-
+ -
-
-

EStop GPIO Pin

- (Currently {$DeviceStateStore.config?.estop == null ? ' unavailable' : $DeviceStateStore.config.estop.gpioPin}) -
-
- - -
-
+ diff --git a/include/Chipset.h b/include/Chipset.h index c05b49ec..8e548cc8 100644 --- a/include/Chipset.h +++ b/include/Chipset.h @@ -5,6 +5,8 @@ #include #include +#include +#include // The following chipsets are supported by the OpenShock firmware. // To find documentation for a specific chipset, see the docs link. @@ -253,13 +255,37 @@ namespace OpenShock { return true; } - const std::size_t GPIOPinSetSize = GPIO_NUM_MAX; - typedef std::bitset GPIOPinSet; + static_assert(GPIO_NUM_MAX < std::numeric_limits::max(), "GPIO_NUM_MAX is too large for int8_t."); + + constexpr uint8_t GetValidInputPinsCount() + { + uint8_t count = 0; + for (int8_t i = GPIO_NUM_NC; i < GPIO_NUM_MAX; i++) { + if (IsValidInputPin(i)) { + count++; + } + } + return count; + } + constexpr uint8_t GetValidOutputPinsCount() + { + uint8_t count = 0; + for (int8_t i = GPIO_NUM_NC; i < GPIO_NUM_MAX; i++) { + if (IsValidOutputPin(i)) { + count++; + } + } + return count; + } + + const uint8_t ValidInputPinsCount = GetValidInputPinsCount(); + const uint8_t ValidOutputPinsCount = GetValidOutputPinsCount(); + typedef std::bitset GPIOPinSet; constexpr GPIOPinSet GetValidGPIOPins() { GPIOPinSet pins; - for (std::size_t i = 0; i < GPIOPinSetSize; i++) { + for (uint8_t i = 0; i < GPIO_NUM_MAX; i++) { if (IsValidGPIOPin(i)) { pins.set(i); } @@ -269,7 +295,7 @@ namespace OpenShock { constexpr GPIOPinSet GetValidInputPins() { GPIOPinSet pins; - for (std::size_t i = 0; i < GPIOPinSetSize; i++) { + for (uint8_t i = 0; i < GPIO_NUM_MAX; i++) { if (IsValidInputPin(i)) { pins.set(i); } @@ -279,11 +305,33 @@ namespace OpenShock { constexpr GPIOPinSet GetValidOutputPins() { GPIOPinSet pins; - for (std::size_t i = 0; i < GPIOPinSetSize; i++) { + for (uint8_t i = 0; i < GPIO_NUM_MAX; i++) { if (IsValidOutputPin(i)) { pins.set(i); } } return pins; } + inline std::vector GetValidInputPinsVector() + { + std::vector pins; + pins.reserve(ValidInputPinsCount); + for (int8_t i = GPIO_NUM_NC; i < GPIO_NUM_MAX; i++) { + if (IsValidInputPin(i)) { + pins.push_back(i); + } + } + return pins; + } + inline std::vector GetValidOutputPinsVector() + { + std::vector pins; + pins.reserve(ValidOutputPinsCount); + for (int8_t i = GPIO_NUM_NC; i < GPIO_NUM_MAX; i++) { + if (IsValidOutputPin(i)) { + pins.push_back(i); + } + } + return pins; + } } // namespace OpenShock diff --git a/include/serialization/_fbs/HubToLocalMessage_generated.h b/include/serialization/_fbs/HubToLocalMessage_generated.h index dfba7811..a6b68fdb 100644 --- a/include/serialization/_fbs/HubToLocalMessage_generated.h +++ b/include/serialization/_fbs/HubToLocalMessage_generated.h @@ -244,7 +244,9 @@ struct ReadyMessage FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { VT_POGGIES = 4, VT_CONNECTED_WIFI = 6, VT_ACCOUNT_LINKED = 8, - VT_CONFIG = 10 + VT_CONFIG = 10, + VT_GPIO_VALID_INPUTS = 12, + VT_GPIO_VALID_OUTPUTS = 14 }; bool poggies() const { return GetField(VT_POGGIES, 0) != 0; @@ -258,6 +260,12 @@ struct ReadyMessage FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { const OpenShock::Serialization::Configuration::HubConfig *config() const { return GetPointer(VT_CONFIG); } + const ::flatbuffers::Vector *gpio_valid_inputs() const { + return GetPointer *>(VT_GPIO_VALID_INPUTS); + } + const ::flatbuffers::Vector *gpio_valid_outputs() const { + return GetPointer *>(VT_GPIO_VALID_OUTPUTS); + } bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_POGGIES, 1) && @@ -266,6 +274,10 @@ struct ReadyMessage FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { VerifyField(verifier, VT_ACCOUNT_LINKED, 1) && VerifyOffset(verifier, VT_CONFIG) && verifier.VerifyTable(config()) && + VerifyOffset(verifier, VT_GPIO_VALID_INPUTS) && + verifier.VerifyVector(gpio_valid_inputs()) && + VerifyOffset(verifier, VT_GPIO_VALID_OUTPUTS) && + verifier.VerifyVector(gpio_valid_outputs()) && verifier.EndTable(); } }; @@ -286,6 +298,12 @@ struct ReadyMessageBuilder { void add_config(::flatbuffers::Offset config) { fbb_.AddOffset(ReadyMessage::VT_CONFIG, config); } + void add_gpio_valid_inputs(::flatbuffers::Offset<::flatbuffers::Vector> gpio_valid_inputs) { + fbb_.AddOffset(ReadyMessage::VT_GPIO_VALID_INPUTS, gpio_valid_inputs); + } + void add_gpio_valid_outputs(::flatbuffers::Offset<::flatbuffers::Vector> gpio_valid_outputs) { + fbb_.AddOffset(ReadyMessage::VT_GPIO_VALID_OUTPUTS, gpio_valid_outputs); + } explicit ReadyMessageBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); @@ -302,8 +320,12 @@ inline ::flatbuffers::Offset CreateReadyMessage( bool poggies = false, ::flatbuffers::Offset connected_wifi = 0, bool account_linked = false, - ::flatbuffers::Offset config = 0) { + ::flatbuffers::Offset config = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> gpio_valid_inputs = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> gpio_valid_outputs = 0) { ReadyMessageBuilder builder_(_fbb); + builder_.add_gpio_valid_outputs(gpio_valid_outputs); + builder_.add_gpio_valid_inputs(gpio_valid_inputs); builder_.add_config(config); builder_.add_connected_wifi(connected_wifi); builder_.add_account_linked(account_linked); @@ -316,6 +338,26 @@ struct ReadyMessage::Traits { static auto constexpr Create = CreateReadyMessage; }; +inline ::flatbuffers::Offset CreateReadyMessageDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + bool poggies = false, + ::flatbuffers::Offset connected_wifi = 0, + bool account_linked = false, + ::flatbuffers::Offset config = 0, + const std::vector *gpio_valid_inputs = nullptr, + const std::vector *gpio_valid_outputs = nullptr) { + auto gpio_valid_inputs__ = gpio_valid_inputs ? _fbb.CreateVector(*gpio_valid_inputs) : 0; + auto gpio_valid_outputs__ = gpio_valid_outputs ? _fbb.CreateVector(*gpio_valid_outputs) : 0; + return OpenShock::Serialization::Local::CreateReadyMessage( + _fbb, + poggies, + connected_wifi, + account_linked, + config, + gpio_valid_inputs__, + gpio_valid_outputs__); +} + struct ErrorMessage FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef ErrorMessageBuilder Builder; struct Traits; diff --git a/schemas b/schemas index 58c0c64c..b82468a6 160000 --- a/schemas +++ b/schemas @@ -1 +1 @@ -Subproject commit 58c0c64cc3f9755323d5438c76778c8abe8153c6 +Subproject commit b82468a6672860dd506f26724265d9bd7dd2d2fb diff --git a/src/serialization/WSLocal.cpp b/src/serialization/WSLocal.cpp index ac67a131..887dc4f5 100644 --- a/src/serialization/WSLocal.cpp +++ b/src/serialization/WSLocal.cpp @@ -2,6 +2,7 @@ const char* const TAG = "WSLocal"; +#include "Chipset.h" #include "config/Config.h" #include "Logging.h" #include "util/HexUtils.h" @@ -78,7 +79,13 @@ bool Local::SerializeReadyMessage(const WiFiNetwork* connectedNetwork, bool acco return false; } - auto readyMessageOffset = Serialization::Local::CreateReadyMessage(builder, true, fbsNetwork, accountLinked, configOffset); + auto inputPins = OpenShock::GetValidInputPinsVector(); + auto inputPinsOffset = builder.CreateVector(inputPins); + + auto outputPins = OpenShock::GetValidOutputPinsVector(); + auto outputPinsOffset = builder.CreateVector(outputPins); + + auto readyMessageOffset = Serialization::Local::CreateReadyMessage(builder, true, fbsNetwork, accountLinked, configOffset, inputPinsOffset, outputPinsOffset); auto msg = Serialization::Local::CreateHubToLocalMessage(builder, Serialization::Local::HubToLocalMessagePayload::ReadyMessage, readyMessageOffset.Union()); From b852e2044313bb4f34fe1b661ac9da1d15db3288 Mon Sep 17 00:00:00 2001 From: hhvrc Date: Tue, 8 Oct 2024 02:32:41 +0200 Subject: [PATCH 5/7] Fix null mutex exception --- src/CommandHandler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CommandHandler.cpp b/src/CommandHandler.cpp index 7575f91a..1d6e3adc 100644 --- a/src/CommandHandler.cpp +++ b/src/CommandHandler.cpp @@ -160,6 +160,8 @@ bool CommandHandler::Init() } initialized = true; + s_estopManagerMutex = xSemaphoreCreateMutex(); + Config::RFConfig rfConfig; if (!Config::GetRFConfig(rfConfig)) { OS_LOGE(TAG, "Failed to get RF config"); From 1e77dd861ba872efee84aad937309e0222207f86 Mon Sep 17 00:00:00 2001 From: hhvrc Date: Tue, 8 Oct 2024 02:43:46 +0200 Subject: [PATCH 6/7] Remove debug log --- src/serial/SerialInputHandler.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/serial/SerialInputHandler.cpp b/src/serial/SerialInputHandler.cpp index f523a5a7..3b7c3e56 100644 --- a/src/serial/SerialInputHandler.cpp +++ b/src/serial/SerialInputHandler.cpp @@ -244,8 +244,6 @@ void _printCommandHelp(Serial::CommandGroup& group) { } buffer.push_back('\n'); - OS_LOGI(TAG, "Buffer size: %zu", buffer.size()); - ::Serial.print(buffer.data()); } From 3d1acb7d792cadb181735bd5812b7c263f1c2c50 Mon Sep 17 00:00:00 2001 From: hhvrc Date: Tue, 8 Oct 2024 02:44:08 +0200 Subject: [PATCH 7/7] Format SerialInputHandler.cpp --- src/serial/SerialInputHandler.cpp | 68 +++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/src/serial/SerialInputHandler.cpp b/src/serial/SerialInputHandler.cpp index 3b7c3e56..504990b0 100644 --- a/src/serial/SerialInputHandler.cpp +++ b/src/serial/SerialInputHandler.cpp @@ -32,7 +32,8 @@ const char* const TAG = "SerialInputHandler"; namespace std { struct hash_ci { - std::size_t operator()(std::string_view str) const { + std::size_t operator()(std::string_view str) const + { std::size_t hash = 7; for (int i = 0; i < str.size(); ++i) { @@ -64,7 +65,8 @@ static bool s_echoEnabled = true; static std::vector s_commandGroups; static std::unordered_map s_commandHandlers; -void _printCompleteHelp() { +void _printCompleteHelp() +{ std::size_t commandCount = 0; std::size_t longestCommand = 0; std::size_t longestArgument = 0; @@ -122,7 +124,8 @@ void _printCompleteHelp() { ::Serial.print(buffer.data()); } -void _printCommandHelp(Serial::CommandGroup& group) { +void _printCommandHelp(Serial::CommandGroup& group) +{ std::size_t size = 0; for (const auto& command : group.commands()) { size++; // +1 for newline @@ -247,7 +250,8 @@ void _printCommandHelp(Serial::CommandGroup& group) { ::Serial.print(buffer.data()); } -void _handleHelpCommand(std::string_view arg, bool isAutomated) { +void _handleHelpCommand(std::string_view arg, bool isAutomated) +{ arg = OpenShock::StringTrim(arg); if (arg.empty()) { _printCompleteHelp(); @@ -264,7 +268,8 @@ void _handleHelpCommand(std::string_view arg, bool isAutomated) { SERPR_ERROR("Command \"%.*s\" not found", arg.length(), arg.data()); } -void RegisterCommandHandler(const OpenShock::Serial::CommandGroup& handler) { +void RegisterCommandHandler(const OpenShock::Serial::CommandGroup& handler) +{ s_commandHandlers[handler.name()] = handler; } @@ -278,11 +283,15 @@ class SerialBuffer { constexpr SerialBuffer() : m_data(nullptr) , m_size(0) - , m_capacity(0) { } + , m_capacity(0) + { + } inline SerialBuffer(std::size_t capacity) : m_data(new char[capacity]) , m_size(0) - , m_capacity(capacity) { } + , m_capacity(capacity) + { + } inline ~SerialBuffer() { delete[] m_data; } constexpr char* data() { return m_data; } @@ -291,14 +300,16 @@ class SerialBuffer { constexpr bool empty() const { return m_size == 0; } constexpr void clear() { m_size = 0; } - inline void destroy() { + inline void destroy() + { delete[] m_data; m_data = nullptr; m_size = 0; m_capacity = 0; } - inline void reserve(std::size_t size) { + inline void reserve(std::size_t size) + { size = (size + 31) & ~31; // Align to 32 bytes if (size <= m_capacity) { @@ -315,7 +326,8 @@ class SerialBuffer { m_capacity = size; } - inline void push_back(char c) { + inline void push_back(char c) + { if (m_size >= m_capacity) { reserve(m_capacity + 16); } @@ -323,7 +335,8 @@ class SerialBuffer { m_data[m_size++] = c; } - constexpr void pop_back() { + constexpr void pop_back() + { if (m_size > 0) { --m_size; } @@ -344,7 +357,8 @@ enum class SerialReadResult { AutoCompleteRequest, }; -SerialReadResult _tryReadSerialLine(SerialBuffer& buffer) { +SerialReadResult _tryReadSerialLine(SerialBuffer& buffer) +{ // Check if there's any data available int available = ::Serial.available(); if (available <= 0) { @@ -390,7 +404,8 @@ SerialReadResult _tryReadSerialLine(SerialBuffer& buffer) { return SerialReadResult::Data; } -void _skipSerialWhitespaces(SerialBuffer& buffer) { +void _skipSerialWhitespaces(SerialBuffer& buffer) +{ int available = ::Serial.available(); while (available-- > 0) { @@ -403,11 +418,13 @@ void _skipSerialWhitespaces(SerialBuffer& buffer) { } } -void _echoBuffer(std::string_view buffer) { +void _echoBuffer(std::string_view buffer) +{ ::Serial.printf(CLEAR_LINE "> %.*s", buffer.size(), buffer.data()); } -void _echoHandleSerialInput(std::string_view buffer, bool hasData) { +void _echoHandleSerialInput(std::string_view buffer, bool hasData) +{ static int64_t lastActivity = 0; static bool hasChanges = false; @@ -435,7 +452,8 @@ void _echoHandleSerialInput(std::string_view buffer, bool hasData) { } } -void _processSerialLine(std::string_view line) { +void _processSerialLine(std::string_view line) +{ line = OpenShock::StringTrim(line); if (line.empty()) { return; @@ -475,7 +493,8 @@ void _processSerialLine(std::string_view line) { } } -bool SerialInputHandler::Init() { +bool SerialInputHandler::Init() +{ static bool s_initialized = false; if (s_initialized) { OS_LOGW(TAG, "Serial input handler already initialized"); @@ -502,7 +521,8 @@ bool SerialInputHandler::Init() { return true; } -void SerialInputHandler::Update() { +void SerialInputHandler::Update() +{ static SerialBuffer buffer(32); switch (_tryReadSerialLine(buffer)) { @@ -531,15 +551,18 @@ void SerialInputHandler::Update() { } } -bool SerialInputHandler::SerialEchoEnabled() { +bool SerialInputHandler::SerialEchoEnabled() +{ return s_echoEnabled; } -void SerialInputHandler::SetSerialEchoEnabled(bool enabled) { +void SerialInputHandler::SetSerialEchoEnabled(bool enabled) +{ s_echoEnabled = enabled; } -void SerialInputHandler::PrintWelcomeHeader() { +void SerialInputHandler::PrintWelcomeHeader() +{ ::Serial.print(R"( ============== OPENSHOCK ============== Contribute @ github.com/OpenShock @@ -549,7 +572,8 @@ void SerialInputHandler::PrintWelcomeHeader() { )"); } -void SerialInputHandler::PrintVersionInfo() { +void SerialInputHandler::PrintVersionInfo() +{ ::Serial.print("\ Version: " OPENSHOCK_FW_VERSION "\n\ Build: " OPENSHOCK_FW_MODE "\n\