From cfe7332899fd4377f15403354041d1d2d29581b3 Mon Sep 17 00:00:00 2001 From: Patedam Date: Tue, 3 Mar 2026 14:11:55 -0500 Subject: [PATCH] Clang format changes for more readability --- .clang-format | 274 ++++++++++++++++++++++++++++ Provider/frontend/.env.development | 4 +- Provider/main/api/system/info.cpp | 25 ++- Provider/main/api/system/reboot.cpp | 3 +- Provider/main/connect.cpp | 189 +++++++++++++------ Provider/main/http_server.cpp | 100 +++++++--- Provider/main/led_status.cpp | 24 ++- Provider/main/main.cpp | 52 ++++-- 8 files changed, 554 insertions(+), 117 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b717d1f --- /dev/null +++ b/.clang-format @@ -0,0 +1,274 @@ +--- +Language: Cpp +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseArrows: false + AlignCaseColons: false +AlignConsecutiveTableGenBreakingDAGArgColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenCondOperatorColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenDefinitionColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Never +AllowShortCaseExpressionOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakAfterReturnType: None +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Allman +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakFunctionDefinitionParameters: false +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +BreakTemplateDeclarations: MultiLine +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLines: + AtEndOfFile: false + AtStartOfBlock: true + AtStartOfFile: true +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MainIncludeChar: Quote +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +PPIndentWidth: -1 +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + ExceptDoubleParentheses: false + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TableGenBreakInsideDAGArg: DontBreak +TabWidth: 8 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +... + diff --git a/Provider/frontend/.env.development b/Provider/frontend/.env.development index 4053c9e..f8b2791 100644 --- a/Provider/frontend/.env.development +++ b/Provider/frontend/.env.development @@ -1,3 +1 @@ -# Set this to your ESP32's IP address for local development -# Example: VITE_API_BASE=http://192.168.1.100 -VITE_API_BASE=http://192.168.50.216 +VITE_API_BASE=http://192.168.50.43 diff --git a/Provider/main/api/system/info.cpp b/Provider/main/api/system/info.cpp index a637204..4100c59 100644 --- a/Provider/main/api/system/info.cpp +++ b/Provider/main/api/system/info.cpp @@ -11,7 +11,8 @@ #include "appstate.hpp" #include "types.hpp" -internal esp_err_t api_system_info_handler(httpd_req_t *req) { +internal esp_err_t api_system_info_handler(httpd_req_t *req) +{ httpd_resp_set_type(req, "application/json"); httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); @@ -21,13 +22,20 @@ internal esp_err_t api_system_info_handler(httpd_req_t *req) { esp_chip_info(&chip_info); const char *model = "ESP32-S3"; - if (chip_info.model == CHIP_ESP32) { + if (chip_info.model == CHIP_ESP32) + { model = "ESP32"; - } else if (chip_info.model == CHIP_ESP32S2) { + } + else if (chip_info.model == CHIP_ESP32S2) + { model = "ESP32-S2"; - } else if (chip_info.model == CHIP_ESP32S3) { + } + else if (chip_info.model == CHIP_ESP32S3) + { model = "ESP32-S3"; - } else if (chip_info.model == CHIP_ESP32C3) { + } + else if (chip_info.model == CHIP_ESP32C3) + { model = "ESP32-C3"; } @@ -43,9 +51,12 @@ internal esp_err_t api_system_info_handler(httpd_req_t *req) { cJSON_AddStringToObject(root, "firmware", app_desc->version); const char *conn_type = "offline"; - if (g_Ethernet_Initialized) { + if (g_Ethernet_Initialized) + { conn_type = "ethernet"; - } else if (g_Wifi_Initialized) { + } + else if (g_Wifi_Initialized) + { conn_type = "wifi"; } cJSON_AddStringToObject(root, "connection", conn_type); diff --git a/Provider/main/api/system/reboot.cpp b/Provider/main/api/system/reboot.cpp index c96e6d8..040d174 100644 --- a/Provider/main/api/system/reboot.cpp +++ b/Provider/main/api/system/reboot.cpp @@ -7,7 +7,8 @@ internal void restart_timer_callback(void *arg) { esp_restart(); } -internal esp_err_t api_system_reboot_handler(httpd_req_t *req) { +internal esp_err_t api_system_reboot_handler(httpd_req_t *req) +{ httpd_resp_set_type(req, "application/json"); httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); diff --git a/Provider/main/connect.cpp b/Provider/main/connect.cpp index 20a4b3e..d0aaa3e 100644 --- a/Provider/main/connect.cpp +++ b/Provider/main/connect.cpp @@ -8,10 +8,11 @@ #include "esp_eth_driver.h" #include "esp_event.h" #include "esp_log.h" -#include "esp_netif.h" -#include "esp_system.h" #include "esp_wifi.h" #include "ethernet_init.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/timers.h" // Project includes #include "types.hpp" @@ -41,10 +42,13 @@ internal bool s_ethernet_connected = false; #endif void ethernet_event_handler(void *arg, esp_event_base_t event_base, - int32_t event_id, void *event_data) { - if (event_base == IP_EVENT && event_id == IP_EVENT_ETH_GOT_IP) { + int32_t event_id, void *event_data) +{ + if (event_base == IP_EVENT && event_id == IP_EVENT_ETH_GOT_IP) + { ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; - if (event->esp_netif != s_eth_netif) { + if (event->esp_netif != s_eth_netif) + { return; } ESP_LOGI(kLogEthernet, "Got IPv4 event: Interface \"%s\" address: " IPSTR, @@ -52,31 +56,39 @@ void ethernet_event_handler(void *arg, esp_event_base_t event_base, s_current_ip_info = event->ip_info; xSemaphoreGive(s_semph_get_ip_addrs); - } else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_CONNECTED) { + } + else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_CONNECTED) + { ESP_LOGI(kLogEthernet, "Ethernet Link Up"); s_eth_link_up = true; - if (s_semph_eth_link) { + if (s_semph_eth_link) + { xSemaphoreGive(s_semph_eth_link); } - } else if (event_base == ETH_EVENT && - event_id == ETHERNET_EVENT_DISCONNECTED) { + } + else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_DISCONNECTED) + { ESP_LOGI(kLogEthernet, "Ethernet Link Down"); s_eth_link_up = false; } } -void teardown_ethernet() { - if (s_semph_eth_link != nullptr) { +void teardown_ethernet() +{ + if (s_semph_eth_link != nullptr) + { vSemaphoreDelete(s_semph_eth_link); s_semph_eth_link = nullptr; } - if (s_semph_get_ip_addrs != nullptr) { + if (s_semph_get_ip_addrs != nullptr) + { vSemaphoreDelete(s_semph_get_ip_addrs); s_semph_get_ip_addrs = nullptr; } - if (s_eth_netif) { + if (s_eth_netif) + { ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, ðernet_event_handler)); @@ -96,7 +108,8 @@ void teardown_ethernet() { esp_netif_deinit(); } -internal esp_err_t connect_ethernet(bool blockUntilIPAcquired) { +internal esp_err_t connect_ethernet(bool blockUntilIPAcquired) +{ #ifndef NDEBUG assert(!s_ethernet_connected && "Ethernet connect called but already connected!"); @@ -105,7 +118,8 @@ internal esp_err_t connect_ethernet(bool blockUntilIPAcquired) { s_semph_get_ip_addrs = xSemaphoreCreateBinary(); s_semph_eth_link = xSemaphoreCreateBinary(); - if (s_semph_get_ip_addrs == NULL || s_semph_eth_link == NULL) { + if (s_semph_get_ip_addrs == NULL || s_semph_eth_link == NULL) + { return ESP_ERR_NO_MEM; } // Connection is split in two steps. First we open the connection and ask for @@ -139,7 +153,8 @@ internal esp_err_t connect_ethernet(bool blockUntilIPAcquired) { // Will teardown connection properly on shutdown ESP_ERROR_CHECK(esp_register_shutdown_handler(&teardown_ethernet)); - if (blockUntilIPAcquired) { + if (blockUntilIPAcquired) + { ESP_LOGI(kLogEthernet, "Waiting for IP address."); xSemaphoreTake(s_semph_get_ip_addrs, portMAX_DELAY); blink_last_ip_octet(); @@ -148,7 +163,8 @@ internal esp_err_t connect_ethernet(bool blockUntilIPAcquired) { return ESP_OK; } -void disconnect_ethernet() { +void disconnect_ethernet() +{ #ifndef NDEBUG assert(s_ethernet_connected && "Ethernet disconnect called but not connected!"); @@ -159,10 +175,13 @@ void disconnect_ethernet() { ESP_ERROR_CHECK(esp_unregister_shutdown_handler(&teardown_ethernet)); } -internal esp_err_t check_ethernet_connection(uint32_t timeoutSeconds) { +internal esp_err_t check_ethernet_connection(uint32_t timeoutSeconds) +{ // Wait up to 5000ms for the physical link to negotiate - if (!s_eth_link_up) { - if (!xSemaphoreTake(s_semph_eth_link, pdMS_TO_TICKS(5000))) { + if (!s_eth_link_up) + { + if (!xSemaphoreTake(s_semph_eth_link, pdMS_TO_TICKS(5000))) + { ESP_LOGE(kLogEthernet, "No physical Ethernet link detected. Skipping DHCP wait."); return ESP_ERR_INVALID_STATE; // Special error to skip retries @@ -172,10 +191,13 @@ internal esp_err_t check_ethernet_connection(uint32_t timeoutSeconds) { ESP_LOGI(kLogEthernet, "Waiting for IP address for %d seconds.", timeoutSeconds); if (xSemaphoreTake(s_semph_get_ip_addrs, - pdMS_TO_TICKS(timeoutSeconds * 1000))) { + pdMS_TO_TICKS(timeoutSeconds * 1000))) + { blink_last_ip_octet(); return ESP_OK; - } else { + } + else + { return ESP_ERR_TIMEOUT; } } @@ -187,25 +209,41 @@ internal esp_netif_t *s_wifi_netif = nullptr; internal SemaphoreHandle_t s_semph_get_wifi_ip_addrs = nullptr; internal SemaphoreHandle_t s_semph_wifi_link = nullptr; internal volatile bool s_wifi_link_up = false; +internal TimerHandle_t s_wifi_reconnect_timer = nullptr; + #ifndef NDEBUG internal bool s_wifi_connected = false; #endif +// Forward declaration for timer callback +internal void wifi_reconnect_timer_cb(TimerHandle_t xTimer); + void wifi_event_handler(void *arg, esp_event_base_t event_base, - int32_t event_id, void *event_data) { - if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) { + int32_t event_id, void *event_data) +{ + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) + { ESP_LOGI(kLogWifi, "WiFi associated with AP."); s_wifi_link_up = true; - if (s_semph_wifi_link) { + if (s_semph_wifi_link) + { xSemaphoreGive(s_semph_wifi_link); } - } else if (event_base == WIFI_EVENT && - event_id == WIFI_EVENT_STA_DISCONNECTED) { - ESP_LOGI(kLogWifi, "WiFi disconnected."); + } + else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) + { + ESP_LOGI(kLogWifi, "WiFi disconnected. Scheduling reconnect..."); s_wifi_link_up = false; - } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + if (s_wifi_reconnect_timer != nullptr) + { + xTimerStart(s_wifi_reconnect_timer, 0); + } + } + else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) + { ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; - if (event->esp_netif != s_wifi_netif) { + if (event->esp_netif != s_wifi_netif) + { return; } ESP_LOGI(kLogWifi, "Got IPv4 event: Interface \"%s\" address: " IPSTR, @@ -216,13 +254,29 @@ void wifi_event_handler(void *arg, esp_event_base_t event_base, } } -void teardown_wifi() { - if (s_semph_wifi_link != nullptr) { +internal void wifi_reconnect_timer_cb(TimerHandle_t xTimer) +{ + ESP_LOGI(kLogWifi, "Timer expired. Executing esp_wifi_connect()..."); + esp_wifi_connect(); +} + +void teardown_wifi() +{ + if (s_wifi_reconnect_timer != nullptr) + { + xTimerStop(s_wifi_reconnect_timer, 0); + xTimerDelete(s_wifi_reconnect_timer, 0); + s_wifi_reconnect_timer = nullptr; + } + + if (s_semph_wifi_link != nullptr) + { vSemaphoreDelete(s_semph_wifi_link); s_semph_wifi_link = nullptr; } - if (s_semph_get_wifi_ip_addrs != nullptr) { + if (s_semph_get_wifi_ip_addrs != nullptr) + { vSemaphoreDelete(s_semph_get_wifi_ip_addrs); s_semph_get_wifi_ip_addrs = nullptr; } @@ -234,7 +288,8 @@ void teardown_wifi() { esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler); - if (s_wifi_netif) { + if (s_wifi_netif) + { esp_wifi_disconnect(); esp_wifi_stop(); esp_wifi_deinit(); @@ -244,7 +299,8 @@ void teardown_wifi() { } internal esp_err_t connect_wifi(const char *ssid, const char *password, - bool blockUntilIPAcquired) { + bool blockUntilIPAcquired) +{ #ifndef NDEBUG assert(!s_wifi_connected && "WiFi connect called but already connected!"); s_wifi_connected = true; @@ -252,10 +308,16 @@ internal esp_err_t connect_wifi(const char *ssid, const char *password, s_semph_get_wifi_ip_addrs = xSemaphoreCreateBinary(); s_semph_wifi_link = xSemaphoreCreateBinary(); - if (s_semph_get_wifi_ip_addrs == NULL || s_semph_wifi_link == NULL) { + if (s_semph_get_wifi_ip_addrs == NULL || s_semph_wifi_link == NULL) + { return ESP_ERR_NO_MEM; } + // Create a 5-second timer to avoid spamming connect requests + s_wifi_reconnect_timer = + xTimerCreate("wifi_recon", pdMS_TO_TICKS(5000), pdFALSE, (void *)0, + wifi_reconnect_timer_cb); + // esp_netif_init() is already called by Ethernet, but safe to call multiple // times or we can assume it's initialized. s_wifi_netif = esp_netif_create_default_wifi_sta(); @@ -285,7 +347,8 @@ internal esp_err_t connect_wifi(const char *ssid, const char *password, ESP_ERROR_CHECK(esp_register_shutdown_handler(&teardown_wifi)); - if (blockUntilIPAcquired) { + if (blockUntilIPAcquired) + { ESP_LOGI(kLogWifi, "Waiting for IP address."); xSemaphoreTake(s_semph_get_wifi_ip_addrs, portMAX_DELAY); blink_last_ip_octet(); @@ -294,7 +357,8 @@ internal esp_err_t connect_wifi(const char *ssid, const char *password, return ESP_OK; } -void disconnect_wifi() { +void disconnect_wifi() +{ #ifndef NDEBUG assert(s_wifi_connected && "WiFi disconnect called but not connected!"); s_wifi_connected = false; @@ -304,36 +368,57 @@ void disconnect_wifi() { ESP_ERROR_CHECK(esp_unregister_shutdown_handler(&teardown_wifi)); } -internal esp_err_t check_wifi_connection(uint32_t timeoutSeconds) { +internal esp_err_t check_wifi_connection(uint32_t timeoutSeconds) +{ // Wait up to 10000ms for the physical link to associate with the AP - if (!s_wifi_link_up) { - if (!xSemaphoreTake(s_semph_wifi_link, pdMS_TO_TICKS(10000))) { - ESP_LOGE(kLogWifi, - "Failed to associate with WiFi AP. Skipping DHCP wait."); - return ESP_ERR_INVALID_STATE; // Special error to skip retries + if (!s_wifi_link_up) + { + // If the timer isn't already running, kickstart a connection attempt now + if (s_wifi_reconnect_timer != nullptr && + xTimerIsTimerActive(s_wifi_reconnect_timer) == pdFALSE) + { + ESP_LOGI(kLogWifi, + "Physical link is down. Requesting immediate AP association..."); + esp_err_t err = esp_wifi_connect(); + if (err != ESP_OK && err != ESP_ERR_WIFI_CONN) + { + ESP_LOGE(kLogWifi, "esp_wifi_connect failed: %s", esp_err_to_name(err)); + } + } + + if (!xSemaphoreTake(s_semph_wifi_link, pdMS_TO_TICKS(10000))) + { + ESP_LOGE(kLogWifi, "Failed to associate with WiFi AP."); + return ESP_ERR_TIMEOUT; // Return timeout so main.cpp triggers a retry } } ESP_LOGI(kLogWifi, "Waiting for IP address for %d seconds.", timeoutSeconds); if (xSemaphoreTake(s_semph_get_wifi_ip_addrs, - pdMS_TO_TICKS(timeoutSeconds * 1000))) { + pdMS_TO_TICKS(timeoutSeconds * 1000))) + { blink_last_ip_octet(); return ESP_OK; - } else { + } + else + { return ESP_ERR_TIMEOUT; } } -internal esp_err_t get_ip_info(esp_netif_ip_info_t *ip_info) { +internal esp_err_t get_ip_info(esp_netif_ip_info_t *ip_info) +{ *ip_info = s_current_ip_info; return ESP_OK; } internal void led_blink_number(int n, uint8_t r, uint8_t g, uint8_t b); -internal void blink_last_ip_octet() { +internal void blink_last_ip_octet() +{ esp_netif_ip_info_t ip_info; - if (get_ip_info(&ip_info) == ESP_OK) { + if (get_ip_info(&ip_info) == ESP_OK) + { uint8_t last_octet = (ip_info.ip.addr >> 24) & 0xFF; printf("IP Address: " IPSTR "\n", IP2STR(&ip_info.ip)); @@ -342,10 +427,12 @@ internal void blink_last_ip_octet() { int t = (last_octet / 10) % 10; int u = last_octet % 10; - if (h > 0) { + if (h > 0) + { led_blink_number(h, 255, 255, 255); } - if (h > 0 || t > 0) { + if (h > 0 || t > 0) + { led_blink_number(t, 255, 255, 255); } led_blink_number(u, 255, 255, 255); diff --git a/Provider/main/http_server.cpp b/Provider/main/http_server.cpp index 8602cfb..9f1e414 100644 --- a/Provider/main/http_server.cpp +++ b/Provider/main/http_server.cpp @@ -19,78 +19,107 @@ constexpr uint8 kGZ_Extension_Length = sizeof(".gz") - 1; #define FILE_PATH_MAX (ESP_VFS_PATH_MAX + 128) #define SCRATCH_BUFSIZE 4096 -typedef struct { +typedef struct +{ char scratch[SCRATCH_BUFSIZE]; } http_server_data_t; #ifdef CONFIG_CALENDINK_DEPLOY_WEB_PAGES // Set HTTP response content type according to file extension internal esp_err_t set_content_type_from_file(httpd_req_t *req, - const char *filepath) { + const char *filepath) +{ const char *type = "text/plain"; - if (strstr(filepath, ".html")) { + if (strstr(filepath, ".html")) + { type = "text/html"; - } else if (strstr(filepath, ".js")) { + } + else if (strstr(filepath, ".js")) + { type = "application/javascript"; - } else if (strstr(filepath, ".css")) { + } + else if (strstr(filepath, ".css")) + { type = "text/css"; - } else if (strstr(filepath, ".png")) { + } + else if (strstr(filepath, ".png")) + { type = "image/png"; - } else if (strstr(filepath, ".ico")) { + } + else if (strstr(filepath, ".ico")) + { type = "image/x-icon"; - } else if (strstr(filepath, ".svg")) { + } + else if (strstr(filepath, ".svg")) + { type = "text/xml"; } return httpd_resp_set_type(req, type); } // Handler to serve static files from LittleFS -internal esp_err_t static_file_handler(httpd_req_t *req) { +internal esp_err_t static_file_handler(httpd_req_t *req) +{ char filepath[FILE_PATH_MAX]; // Construct real file path strlcpy(filepath, "/www", sizeof(filepath)); - if (req->uri[strlen(req->uri) - 1] == '/') { + if (req->uri[strlen(req->uri) - 1] == '/') + { strlcat(filepath, "/index.html", sizeof(filepath)); - } else { + } + else + { strlcat(filepath, req->uri, sizeof(filepath)); } // Default to index.html if file doesn't exist (SPA routing support) struct stat file_stat; - if (stat(filepath, &file_stat) == -1) { + if (stat(filepath, &file_stat) == -1) + { // Try gzipped first, then fallback to index.html char filepath_gz[FILE_PATH_MAX + kGZ_Extension_Length]; snprintf(filepath_gz, sizeof(filepath_gz), "%s.gz", filepath); - if (stat(filepath_gz, &file_stat) == 0) { + if (stat(filepath_gz, &file_stat) == 0) + { strlcpy(filepath, filepath_gz, sizeof(filepath)); httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); - } else { + } + else + { ESP_LOGW(TAG, "File not found: %s, falling back to index.html", filepath); snprintf(filepath, sizeof(filepath), "%s/index.html", "/www"); - if (stat(filepath, &file_stat) == -1) { + if (stat(filepath, &file_stat) == -1) + { // If index.html doesn't exist, try index.html.gz snprintf(filepath_gz, sizeof(filepath_gz), "%s/index.html.gz", "/www"); - if (stat(filepath_gz, &file_stat) == 0) { + if (stat(filepath_gz, &file_stat) == 0) + { strlcpy(filepath, filepath_gz, sizeof(filepath)); httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); - } else { + } + else + { ESP_LOGE(TAG, "index.html not found too."); httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File not found"); return ESP_FAIL; } } } - } else { + } + else + { // Since ESP32 handles .gz transparently if we tell it to via headers // If we requested explicitly a .gz file, set the header - if (strstr(filepath, ".gz")) { + if (strstr(filepath, ".gz")) + { httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); } } int fd = open(filepath, O_RDONLY, 0); - if (fd == -1) { + if (fd == -1) + { ESP_LOGE(TAG, "Failed to open file: %s", filepath); httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file"); @@ -103,12 +132,17 @@ internal esp_err_t static_file_handler(httpd_req_t *req) { char *chunk = rest_context->scratch; ssize_t read_bytes; - do { + do + { read_bytes = read(fd, chunk, SCRATCH_BUFSIZE); - if (read_bytes == -1) { + if (read_bytes == -1) + { ESP_LOGE(TAG, "Failed to read file: %s", filepath); - } else if (read_bytes > 0) { - if (httpd_resp_send_chunk(req, chunk, read_bytes) != ESP_OK) { + } + else if (read_bytes > 0) + { + if (httpd_resp_send_chunk(req, chunk, read_bytes) != ESP_OK) + { close(fd); ESP_LOGE(TAG, "File sending failed!"); httpd_resp_sendstr_chunk(req, NULL); // Abort sending @@ -125,7 +159,8 @@ internal esp_err_t static_file_handler(httpd_req_t *req) { #endif // Handler for CORS Preflight OPTIONS requests -internal esp_err_t cors_options_handler(httpd_req_t *req) { +internal esp_err_t cors_options_handler(httpd_req_t *req) +{ httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); httpd_resp_set_hdr(req, "Access-Control-Allow-Methods", "GET, POST, OPTIONS"); httpd_resp_set_hdr(req, "Access-Control-Allow-Headers", "Content-Type"); @@ -134,10 +169,12 @@ internal esp_err_t cors_options_handler(httpd_req_t *req) { return ESP_OK; } -internal httpd_handle_t start_webserver(void) { +internal httpd_handle_t start_webserver(void) +{ http_server_data_t *rest_context = (http_server_data_t *)calloc(1, sizeof(http_server_data_t)); - if (rest_context == NULL) { + if (rest_context == NULL) + { ESP_LOGE(TAG, "No memory for rest context"); return NULL; } @@ -149,7 +186,8 @@ internal httpd_handle_t start_webserver(void) { httpd_handle_t server = NULL; ESP_LOGI(TAG, "Starting HTTP Server on port: '%d'", config.server_port); - if (httpd_start(&server, &config) == ESP_OK) { + if (httpd_start(&server, &config) == ESP_OK) + { // Register CORS OPTIONS handler for API routes httpd_uri_t cors_options_uri = {.uri = "/api/*", .method = HTTP_OPTIONS, @@ -178,8 +216,10 @@ internal httpd_handle_t start_webserver(void) { return NULL; } -internal void stop_webserver(httpd_handle_t server) { - if (server) { +internal void stop_webserver(httpd_handle_t server) +{ + if (server) + { httpd_stop(server); } } diff --git a/Provider/main/led_status.cpp b/Provider/main/led_status.cpp index 8f56a35..f9110e2 100644 --- a/Provider/main/led_status.cpp +++ b/Provider/main/led_status.cpp @@ -7,7 +7,8 @@ // Could be a config but its the GPIO on my ESP32-S3-ETH #define LED_GPIO GPIO_NUM_21 -enum class led_status : uint8 { +enum class led_status : uint8 +{ ConnectingEthernet, ConnectingWifi, ReadyEthernet, @@ -17,7 +18,8 @@ enum class led_status : uint8 { internal led_strip_handle_t led_strip; -internal void setup_led(void) { +internal void setup_led(void) +{ /* LED strip initialization with the GPIO and pixels number*/ led_strip_config_t strip_config = {}; strip_config.strip_gpio_num = LED_GPIO; @@ -34,8 +36,10 @@ internal void setup_led(void) { internal void destroy_led(void) { led_strip_clear(led_strip); } -internal void set_led_status(led_status status) { - switch (status) { +internal void set_led_status(led_status status) +{ + switch (status) + { case led_status::ConnectingEthernet: led_strip_set_pixel(led_strip, 0, 255, 165, 0); break; @@ -54,9 +58,12 @@ internal void set_led_status(led_status status) { } led_strip_refresh(led_strip); } -internal void led_blink_number(int n, uint8_t r, uint8_t g, uint8_t b) { - if (n <= 0) { - for (int i = 0; i < 2; i++) { +internal void led_blink_number(int n, uint8_t r, uint8_t g, uint8_t b) +{ + if (n <= 0) + { + for (int i = 0; i < 2; i++) + { led_strip_set_pixel(led_strip, 0, r, g, b); led_strip_refresh(led_strip); vTaskDelay(pdMS_TO_TICKS(50)); @@ -67,7 +74,8 @@ internal void led_blink_number(int n, uint8_t r, uint8_t g, uint8_t b) { vTaskDelay(pdMS_TO_TICKS(1000)); return; } - for (int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) + { led_strip_set_pixel(led_strip, 0, r, g, b); led_strip_refresh(led_strip); vTaskDelay(pdMS_TO_TICKS(300)); diff --git a/Provider/main/main.cpp b/Provider/main/main.cpp index 7366c17..7e24118 100644 --- a/Provider/main/main.cpp +++ b/Provider/main/main.cpp @@ -22,7 +22,8 @@ internal constexpr bool kBlockUntilEthernetEstablished = false; -extern "C" void app_main() { +extern "C" void app_main() +{ printf("Hello, Worldi!\n"); httpd_handle_t web_server = NULL; @@ -35,25 +36,30 @@ extern "C" void app_main() { set_led_status(led_status::ConnectingEthernet); g_Ethernet_Initialized = true; esp_err_t result = connect_ethernet(kBlockUntilEthernetEstablished); - if (result != ESP_OK) { + if (result != ESP_OK) + { set_led_status(led_status::Failed); vTaskDelay(pdMS_TO_TICKS(1000)); goto shutdown; } // Check for ethernet connection until its made - if (!kBlockUntilEthernetEstablished) { + if (!kBlockUntilEthernetEstablished) + { uint8 retries = 1; - do { + do + { set_led_status(led_status::ConnectingEthernet); result = check_ethernet_connection(retries); - if (result == ESP_ERR_INVALID_STATE) { + if (result == ESP_ERR_INVALID_STATE) + { printf("Ethernet cable not plugged in, skipping retries.\n"); break; } - if (result != ESP_OK) { + if (result != ESP_OK) + { set_led_status(led_status::Failed); vTaskDelay(pdMS_TO_TICKS(1000)); } @@ -63,7 +69,8 @@ extern "C" void app_main() { retries <= CONFIG_CALENDINK_ETH_RETRIES); } - if (result != ESP_OK) { + if (result != ESP_OK) + { printf("Ethernet failed, trying wifi\n"); disconnect_ethernet(); g_Ethernet_Initialized = false; @@ -73,19 +80,23 @@ extern "C" void app_main() { result = connect_wifi(CONFIG_CALENDINK_WIFI_SSID, CONFIG_CALENDINK_WIFI_PASSWORD, kBlockUntilEthernetEstablished); - if (result != ESP_OK) { + if (result != ESP_OK) + { set_led_status(led_status::Failed); vTaskDelay(pdMS_TO_TICKS(1000)); goto shutdown; } - if (!kBlockUntilEthernetEstablished) { + if (!kBlockUntilEthernetEstablished) + { uint8 retries = 1; - do { + do + { set_led_status(led_status::ConnectingWifi); result = check_wifi_connection(retries); - if (result != ESP_OK) { + if (result != ESP_OK) + { set_led_status(led_status::Failed); vTaskDelay(pdMS_TO_TICKS(1000)); } @@ -95,14 +106,17 @@ extern "C" void app_main() { retries <= CONFIG_CALENDINK_WIFI_RETRIES); } - if (result != ESP_OK) { + if (result != ESP_OK) + { printf("Wifi failed.\n"); goto shutdown; } set_led_status(led_status::ReadyWifi); printf("Will use Wifi!\n"); - } else { + } + else + { set_led_status(led_status::ReadyEthernet); printf("Will use Ethernet!\n"); } @@ -113,23 +127,27 @@ extern "C" void app_main() { web_server = start_webserver(); // Keep the main task alive indefinitely - while (true) { + while (true) + { vTaskDelay(pdMS_TO_TICKS(1000)); } shutdown: printf("Shutting down.\n"); - if (web_server) { + if (web_server) + { stop_webserver(web_server); web_server = NULL; } - if (g_Ethernet_Initialized) { + if (g_Ethernet_Initialized) + { disconnect_ethernet(); g_Ethernet_Initialized = false; } - if (g_Wifi_Initialized) { + if (g_Wifi_Initialized) + { disconnect_wifi(); g_Wifi_Initialized = false; }