added udp logger to log over network and not uart

Added modem sleep for wif
This commit is contained in:
2026-03-14 17:27:24 -04:00
parent b702839f8e
commit a9d5aa83dc
9 changed files with 171 additions and 4 deletions

View File

@@ -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 # Production build → dist/index.html
npm run build:esp32 # Build + gzip → dist/index.html.gz npm run build:esp32 # Build + gzip → dist/index.html.gz
npm run ota:deploy # OTA deploy frontend to device 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. There are no automated tests. Verify by building and inspecting on-device.

View File

@@ -103,3 +103,11 @@ npm run ota:package # Package as versioned .bin
npm run ota:bundle # Package FW + frontend as .bundle npm run ota:bundle # Package FW + frontend as .bundle
npm run ota:deploy # Deploy frontend OTA to device 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)]"
```

View File

@@ -2,7 +2,7 @@ idf_component_register(SRCS "main.cpp"
# Needed as we use minimal build # Needed as we use minimal build
PRIV_REQUIRES esp_http_server esp_eth PRIV_REQUIRES esp_http_server esp_eth
esp_wifi nvs_flash esp_netif vfs 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 ".") INCLUDE_DIRS ".")
if(CONFIG_CALENDINK_DEPLOY_WEB_PAGES) if(CONFIG_CALENDINK_DEPLOY_WEB_PAGES)

View File

@@ -38,6 +38,13 @@ menu "CalendarInk Network Configuration"
The hostname to use for mDNS. The device will be accessible The hostname to use for mDNS. The device will be accessible
at <hostname>.local. (e.g., calendink.local) at <hostname>.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 endmenu
menu "Calendink Web Server" menu "Calendink Web Server"

View File

@@ -3,11 +3,13 @@
#include <string.h> #include <string.h>
// SDK // SDK
#include "driver/gpio.h"
#include "esp_err.h" #include "esp_err.h"
#include "esp_eth.h" #include "esp_eth.h"
#include "esp_eth_driver.h" #include "esp_eth_driver.h"
#include "esp_event.h" #include "esp_event.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_sleep.h"
#include "esp_wifi.h" #include "esp_wifi.h"
#include "ethernet_init.h" #include "ethernet_init.h"
#include "freertos/FreeRTOS.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_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start()); 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_wifi_connect());
ESP_ERROR_CHECK(esp_register_shutdown_handler(&teardown_wifi)); ESP_ERROR_CHECK(esp_register_shutdown_handler(&teardown_wifi));

View File

@@ -58,6 +58,7 @@ internal void set_led_status(led_status status)
} }
led_strip_refresh(led_strip); led_strip_refresh(led_strip);
} }
#if CONFIG_CALENDINK_BLINK_IP #if CONFIG_CALENDINK_BLINK_IP
internal void led_blink_number(int n, uint8_t r, uint8_t g, uint8_t b) internal void led_blink_number(int n, uint8_t r, uint8_t g, uint8_t b)
{ {

View File

@@ -5,6 +5,7 @@
// SDK // SDK
#include "esp_log.h" #include "esp_log.h"
#include "esp_ota_ops.h" #include "esp_ota_ops.h"
#include "esp_pm.h"
#include "esp_psram.h" #include "esp_psram.h"
#include "esp_system.h" #include "esp_system.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
@@ -24,6 +25,7 @@
#include "connect.cpp" #include "connect.cpp"
#include "http_server.cpp" #include "http_server.cpp"
#include "mdns_service.cpp" #include "mdns_service.cpp"
#include "udp_logger.cpp"
// clang-format on // clang-format on
internal const char *kTagMain = "MAIN"; internal const char *kTagMain = "MAIN";
@@ -35,11 +37,24 @@ uint8_t g_Active_WWW_Partition = 0;
constexpr bool kBlockUntilEthernetEstablished = false; 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() extern "C" void app_main()
{ {
ESP_LOGI(kTagMain, "Hello, Calendink OTA! [V0.1.1]"); ESP_LOGI(kTagMain, "Hello, Calendink OTA! [V0.1.1]");
ESP_LOGI(kTagMain, "PSRAM size: %d bytes", esp_psram_get_size()); 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; httpd_handle_t web_server = NULL;
esp_err_t err = nvs_flash_init(); esp_err_t err = nvs_flash_init();
@@ -252,6 +267,10 @@ extern "C" void app_main()
ESP_LOGI(kTagMain, "Connected! IP acquired."); ESP_LOGI(kTagMain, "Connected! IP acquired.");
#if !defined(NDEBUG)
start_udp_logging(514);
#endif
// Start the webserver // Start the webserver
web_server = start_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: shutdown:

View File

@@ -0,0 +1,71 @@
#include <string.h>
#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);
}

View File

@@ -2,3 +2,5 @@ CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_LWIP_MAX_SOCKETS=32 CONFIG_LWIP_MAX_SOCKETS=32
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y