From a9d5aa83dcb0e9bbc4aa3bed4888502849a84852 Mon Sep 17 00:00:00 2001 From: Patedam Date: Sat, 14 Mar 2026 17:27:24 -0400 Subject: [PATCH] added udp logger to log over network and not uart Added modem sleep for wif --- Provider/AGENTS.md | 5 +++ Provider/GEMINI.md | 8 ++++ Provider/main/CMakeLists.txt | 2 +- Provider/main/Kconfig.projbuild | 7 ++++ Provider/main/connect.cpp | 6 +++ Provider/main/led_status.cpp | 1 + Provider/main/main.cpp | 73 +++++++++++++++++++++++++++++++-- Provider/main/udp_logger.cpp | 71 ++++++++++++++++++++++++++++++++ Provider/sdkconfig.defaults | 2 + 9 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 Provider/main/udp_logger.cpp diff --git a/Provider/AGENTS.md b/Provider/AGENTS.md index 0be2a03..365bc59 100644 --- a/Provider/AGENTS.md +++ b/Provider/AGENTS.md @@ -17,6 +17,11 @@ npm run dev # Dev server — calls real ESP32 API (set VITE_API_BASE npm run build # Production build → dist/index.html npm run build:esp32 # Build + gzip → dist/index.html.gz npm run ota:deploy # OTA deploy frontend to device +### Logging on Ethernet +When the device is connected via Ethernet, logs are broadcast over UDP. Run `ncat -ul 514` on your PC to view the live `ESP_LOG` output. +*(If `ncat` throws a `WSAEMSGSIZE` error on Windows, use this Python command instead:)* +```powershell +python -c "import socket; s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM); s.bind(('', 514)); [print(m[0].decode(errors='ignore'), end='') for m in iter(lambda:s.recvfrom(4096), None)]" ``` There are no automated tests. Verify by building and inspecting on-device. diff --git a/Provider/GEMINI.md b/Provider/GEMINI.md index cfa4e58..01da8d6 100644 --- a/Provider/GEMINI.md +++ b/Provider/GEMINI.md @@ -103,3 +103,11 @@ npm run ota:package # Package as versioned .bin npm run ota:bundle # Package FW + frontend as .bundle npm run ota:deploy # Deploy frontend OTA to device ``` + +## Logging on Ethernet + +When the device is connected to Ethernet, logs are broadcast over UDP to port 514. Use `ncat -ul 514` on your PC to view them live. +*(If `ncat` throws a `WSAEMSGSIZE` error on Windows, use this Python command instead:)* +```powershell +python -c "import socket; s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM); s.bind(('', 514)); [print(m[0].decode(errors='ignore'), end='') for m in iter(lambda:s.recvfrom(4096), None)]" +``` diff --git a/Provider/main/CMakeLists.txt b/Provider/main/CMakeLists.txt index c928c94..4387faf 100644 --- a/Provider/main/CMakeLists.txt +++ b/Provider/main/CMakeLists.txt @@ -2,7 +2,7 @@ idf_component_register(SRCS "main.cpp" # Needed as we use minimal build PRIV_REQUIRES esp_http_server esp_eth esp_wifi nvs_flash esp_netif vfs - json app_update esp_timer esp_psram mdns + json app_update esp_timer esp_psram mdns driver INCLUDE_DIRS ".") if(CONFIG_CALENDINK_DEPLOY_WEB_PAGES) diff --git a/Provider/main/Kconfig.projbuild b/Provider/main/Kconfig.projbuild index 31ecbc1..0221b43 100644 --- a/Provider/main/Kconfig.projbuild +++ b/Provider/main/Kconfig.projbuild @@ -38,6 +38,13 @@ menu "CalendarInk Network Configuration" The hostname to use for mDNS. The device will be accessible at .local. (e.g., calendink.local) + config CALENDINK_UDP_LOG_TARGET_IP + string "UDP Logger Target IP Address" + default "" + help + The IP address to send UDP logs to via port 514. + If left blank, logs will be broadcast to 255.255.255.255. + endmenu menu "Calendink Web Server" diff --git a/Provider/main/connect.cpp b/Provider/main/connect.cpp index ce6939e..613eabf 100644 --- a/Provider/main/connect.cpp +++ b/Provider/main/connect.cpp @@ -3,11 +3,13 @@ #include // SDK +#include "driver/gpio.h" #include "esp_err.h" #include "esp_eth.h" #include "esp_eth_driver.h" #include "esp_event.h" #include "esp_log.h" +#include "esp_sleep.h" #include "esp_wifi.h" #include "ethernet_init.h" #include "freertos/FreeRTOS.h" @@ -340,6 +342,10 @@ internal esp_err_t connect_wifi(const char *ssid, const char *password, ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); + + // Reverting to MIN_MODEM because MAX_MODEM causes disconnects on many routers + ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_MAX_MODEM)); + ESP_ERROR_CHECK(esp_wifi_connect()); ESP_ERROR_CHECK(esp_register_shutdown_handler(&teardown_wifi)); diff --git a/Provider/main/led_status.cpp b/Provider/main/led_status.cpp index 119afc3..ef1128e 100644 --- a/Provider/main/led_status.cpp +++ b/Provider/main/led_status.cpp @@ -58,6 +58,7 @@ internal void set_led_status(led_status status) } led_strip_refresh(led_strip); } + #if CONFIG_CALENDINK_BLINK_IP internal void led_blink_number(int n, uint8_t r, uint8_t g, uint8_t b) { diff --git a/Provider/main/main.cpp b/Provider/main/main.cpp index 51b3d15..3b8588e 100644 --- a/Provider/main/main.cpp +++ b/Provider/main/main.cpp @@ -5,6 +5,7 @@ // SDK #include "esp_log.h" #include "esp_ota_ops.h" +#include "esp_pm.h" #include "esp_psram.h" #include "esp_system.h" #include "freertos/FreeRTOS.h" @@ -24,6 +25,7 @@ #include "connect.cpp" #include "http_server.cpp" #include "mdns_service.cpp" +#include "udp_logger.cpp" // clang-format on internal const char *kTagMain = "MAIN"; @@ -35,11 +37,24 @@ uint8_t g_Active_WWW_Partition = 0; constexpr bool kBlockUntilEthernetEstablished = false; +internal void my_timer_callback(void *arg) +{ + ESP_LOGI(kTagMain, "Timer finished! Turning Led Off..."); + destroy_led(); +} + extern "C" void app_main() { ESP_LOGI(kTagMain, "Hello, Calendink OTA! [V0.1.1]"); ESP_LOGI(kTagMain, "PSRAM size: %d bytes", esp_psram_get_size()); +#if CONFIG_PM_ENABLE + esp_pm_config_t pm_config = { + .max_freq_mhz = 240, .min_freq_mhz = 40, .light_sleep_enable = true}; + esp_pm_configure(&pm_config); + ESP_LOGI(kTagMain, "Dynamic Power Management initialized (Tickless Idle)."); +#endif + httpd_handle_t web_server = NULL; esp_err_t err = nvs_flash_init(); @@ -252,6 +267,10 @@ extern "C" void app_main() ESP_LOGI(kTagMain, "Connected! IP acquired."); +#if !defined(NDEBUG) + start_udp_logging(514); +#endif + // Start the webserver web_server = start_webserver(); @@ -269,10 +288,58 @@ extern "C" void app_main() } } - // Keep the main task alive indefinitely - while (true) { - vTaskDelay(pdMS_TO_TICKS(1000)); + const esp_timer_create_args_t timer_args = {.callback = &my_timer_callback, + .arg = nullptr, + .dispatch_method = + ESP_TIMER_TASK, + .name = "Led Turn Off", + .skip_unhandled_events = true}; + + // Create and start timer if needed, or this was just stub code? + esp_timer_handle_t timer_handle; + if (esp_timer_create(&timer_args, &timer_handle) == ESP_OK) + { + esp_timer_start_once(timer_handle, 5'000'000); // 5 sec cooldown + } + } + + // Keep the main task alive indefinitely + { +#if CONFIG_PM_PROFILING + int pm_dump_counter = 0; +#endif + while (true) + { + vTaskDelay(pdMS_TO_TICKS(100)); + +#if CONFIG_PM_PROFILING + if (++pm_dump_counter >= 50) + { // Every 5 seconds + pm_dump_counter = 0; + ESP_LOGI(kTagMain, "--- PM Profiling Dump ---"); + + char *ptr = nullptr; + size_t size = 0; + FILE *f = open_memstream(&ptr, &size); + if (f != nullptr) + { + esp_pm_dump_locks(f); + fclose(f); + if (ptr != nullptr) + { + char *saveptr; + char *line = strtok_r(ptr, "\n", &saveptr); + while (line != nullptr) { + ESP_LOGI(kTagMain, "%s", line); + line = strtok_r(nullptr, "\n", &saveptr); + } + free(ptr); + } + } + } +#endif + } } shutdown: diff --git a/Provider/main/udp_logger.cpp b/Provider/main/udp_logger.cpp new file mode 100644 index 0000000..4f48336 --- /dev/null +++ b/Provider/main/udp_logger.cpp @@ -0,0 +1,71 @@ +#include + +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "lwip/sockets.h" + +#include "types.hpp" + +internal constexpr char kTagUdpLogger[] = "UDP_LOG"; + +internal int s_udp_log_socket = -1; +internal struct sockaddr_in s_udp_dest_addr; +internal vprintf_like_t s_original_vprintf = NULL; + +internal int udp_logging_vprintf(const char *fmt, va_list ap) +{ + char buf[512]; + va_list ap_copy; + va_copy(ap_copy, ap); + int len = vsnprintf(buf, sizeof(buf) - 1, fmt, ap_copy); + va_end(ap_copy); + + if (len > 0) + { + if (len >= sizeof(buf)) + { + len = sizeof(buf) - 1; + } + buf[len] = '\0'; + if (s_udp_log_socket >= 0) + { + sendto(s_udp_log_socket, buf, len, 0, (struct sockaddr *)&s_udp_dest_addr, + sizeof(s_udp_dest_addr)); + } + } + return s_original_vprintf(fmt, ap); +} + +internal void start_udp_logging(int port) +{ + s_udp_log_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (s_udp_log_socket < 0) + { + ESP_LOGE(kTagUdpLogger, "Unable to create socket: errno %d", errno); + return; + } + + int opt_val = 1; + setsockopt(s_udp_log_socket, SOL_SOCKET, SO_BROADCAST, &opt_val, + sizeof(opt_val)); + + s_udp_dest_addr.sin_family = AF_INET; + s_udp_dest_addr.sin_port = htons(port); + +#ifdef CONFIG_CALENDINK_UDP_LOG_TARGET_IP + if (strlen(CONFIG_CALENDINK_UDP_LOG_TARGET_IP) > 1) + { + s_udp_dest_addr.sin_addr.s_addr = + inet_addr(CONFIG_CALENDINK_UDP_LOG_TARGET_IP); + } + else + { + s_udp_dest_addr.sin_addr.s_addr = inet_addr("255.255.255.255"); + } +#else + s_udp_dest_addr.sin_addr.s_addr = inet_addr("255.255.255.255"); +#endif + + s_original_vprintf = esp_log_set_vprintf(&udp_logging_vprintf); + ESP_LOGI(kTagUdpLogger, "UDP logging broadcast started on port %d", port); +} diff --git a/Provider/sdkconfig.defaults b/Provider/sdkconfig.defaults index 38f92f5..d27e960 100644 --- a/Provider/sdkconfig.defaults +++ b/Provider/sdkconfig.defaults @@ -2,3 +2,5 @@ CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" CONFIG_LWIP_MAX_SOCKETS=32 +CONFIG_PM_ENABLE=y +CONFIG_FREERTOS_USE_TICKLESS_IDLE=y