diff --git a/CMakeLists.txt b/CMakeLists.txt index acd0ea6..bacf4e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ if (TARGET tinyusb_host) command_processor.cpp epaper_manager.cpp wifi_manager.cpp + tcp_debug.cpp ) add_subdirectory(pico-ssd1306 commands) diff --git a/epaper_manager.cpp b/epaper_manager.cpp index fa1ea11..c6ef839 100644 --- a/epaper_manager.cpp +++ b/epaper_manager.cpp @@ -1,5 +1,6 @@ #include "epaper_manager.h" #include "wifi_manager.h" +#include "tcp_debug.h" #include "pico/stdlib.h" #include "pico/multicore.h" #include "pico/flash.h" @@ -94,6 +95,9 @@ static void init_epaper_display() { bool connected = wifi_try_auto_connect(); printf("[Core 1] Connected: %d\r\n", connected); + if (connected) { + tcp_debug_init(); + } Paint_SelectImage(g_epd_red); printf("[Core 1] Drawing header\r\n"); diff --git a/hello_usb.cpp b/hello_usb.cpp index 1cd4893..58a2f1b 100644 --- a/hello_usb.cpp +++ b/hello_usb.cpp @@ -22,8 +22,13 @@ // Holds last echoed line for display static char g_last_echo[128] = ""; -// Global DisplayManager pointer -static DisplayManager* g_display_manager = nullptr; +#include "command_processor.h" +#include "epaper_manager.h" +#include "wifi_manager.h" +#include "tcp_debug.h" + +// Global display manager instance +DisplayManager* g_display_manager = nullptr; // Keyboard buffer #define MAX_INPUT_LEN 64 @@ -75,6 +80,7 @@ static bool execute_command(CommandAction action, const char* input) { epaper_send_update(err_msg, true); } else { epaper_send_update("Connected!", true); + tcp_debug_init(); } } else { if (g_display_manager) g_display_manager->refresh("Connection Failed", nullptr); @@ -148,7 +154,7 @@ static void process_kbd_report(hid_keyboard_report_t const *report) { g_buffer_index = 0; g_input_buffer[0] = '\0'; } else if (event.is_printable) { - printf("%c", event.ascii); + // printf("%c", event.ascii); if (g_buffer_index < MAX_INPUT_LEN - 1) { g_input_buffer[g_buffer_index++] = event.ascii; g_input_buffer[g_buffer_index] = '\0'; diff --git a/tcp_debug.cpp b/tcp_debug.cpp new file mode 100644 index 0000000..c8c1d74 --- /dev/null +++ b/tcp_debug.cpp @@ -0,0 +1,137 @@ +#include "tcp_debug.h" +#include "pico/cyw43_arch.h" +#include "lwip/tcp.h" +#include "pico/stdio/driver.h" +#include +#include + +#define TCP_PORT 4242 +#define DEBUG_BUF_SIZE 2048 + +// Circular buffer for debug output +static char debug_buf[DEBUG_BUF_SIZE]; +static volatile int write_idx = 0; +static volatile int read_idx = 0; +static struct tcp_pcb *server_pcb = NULL; +static struct tcp_pcb *client_pcb = NULL; + +// Helper to write to buffer +static void buffer_write(const char *data, int len) { + for (int i = 0; i < len; i++) { + int next_write = (write_idx + 1) % DEBUG_BUF_SIZE; + if (next_write != read_idx) { // Only write if not full + debug_buf[write_idx] = data[i]; + write_idx = next_write; + } + } +} + +// stdio driver callback +static void tcp_out_chars(const char *buf, int len) { + // Handle CRLF conversion manually if needed, or just write raw + // The issue is likely that \n is sent without \r, causing "staircase" effect in raw terminals + for (int i = 0; i < len; i++) { + if (buf[i] == '\n') { + char cr = '\r'; + buffer_write(&cr, 1); + } + buffer_write(&buf[i], 1); + } +} + +static stdio_driver_t tcp_driver = { + .out_chars = tcp_out_chars, + .out_flush = NULL, + .in_chars = NULL, +#if PICO_STDIO_ENABLE_CRLF_SUPPORT + .crlf_enabled = false +#endif +}; + +// TCP callbacks +static err_t tcp_server_poll(void *arg, struct tcp_pcb *tpcb) { + if (tpcb != client_pcb) return ERR_OK; + + // Check if there is data in the ring buffer to send + if (write_idx != read_idx) { + int len_to_send = 0; + char send_buf[256]; // Temporary buffer for one TCP write chunk + + // Copy from ring buffer to linear buffer + while (write_idx != read_idx && len_to_send < sizeof(send_buf)) { + send_buf[len_to_send++] = debug_buf[read_idx]; + read_idx = (read_idx + 1) % DEBUG_BUF_SIZE; + } + + if (len_to_send > 0) { + err_t err = tcp_write(tpcb, send_buf, len_to_send, TCP_WRITE_FLAG_COPY); + if (err == ERR_OK) { + tcp_output(tpcb); + } else { + // If write failed (e.g. buffer full), rewind read_idx (simple retry logic) + // Note: This is a simplification. In a real robust system we might drop data or handle partial writes. + // For now, we just lose the data if TCP is full to avoid blocking the system. + } + } + } + return ERR_OK; +} + +static err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { + if (!p) { + tcp_close(tpcb); + client_pcb = NULL; + printf("TCP debug client disconnected\n"); + return ERR_OK; + } + // We don't expect input, just discard it + tcp_recved(tpcb, p->tot_len); + pbuf_free(p); + return ERR_OK; +} + +static err_t tcp_server_accept(void *arg, struct tcp_pcb *newpcb, err_t err) { + if (client_pcb) { + // Already have a client, reject or close old? + // Let's close the old one and accept the new one (last one wins) + tcp_abort(client_pcb); + } + + client_pcb = newpcb; + tcp_recv(newpcb, tcp_server_recv); + tcp_poll(newpcb, tcp_server_poll, 1); // Poll every 500ms (coarse grain) -> actually arg is interval in 500ms steps? + // Wait, poll interval is passed to tcp_poll. 1 means every 500ms. + // We might want faster polling for debug output. + // But we can't poll faster than the lwIP timer. + // However, we can also trigger writes from the main loop if we wanted, but polling is safer for threading. + + printf("TCP debug client connected\n"); + return ERR_OK; +} + +bool tcp_debug_init(void) { + if (server_pcb) return true; + + // Register stdio driver + stdio_set_driver_enabled(&tcp_driver, true); + + cyw43_arch_lwip_begin(); + server_pcb = tcp_new_ip_type(IPADDR_TYPE_ANY); + if (!server_pcb) { + cyw43_arch_lwip_end(); + return false; + } + + err_t err = tcp_bind(server_pcb, IP_ANY_TYPE, TCP_PORT); + if (err != ERR_OK) { + cyw43_arch_lwip_end(); + return false; + } + + server_pcb = tcp_listen(server_pcb); + tcp_accept(server_pcb, tcp_server_accept); + cyw43_arch_lwip_end(); + + printf("TCP debug server listening on port %d\n", TCP_PORT); + return true; +} diff --git a/tcp_debug.h b/tcp_debug.h new file mode 100644 index 0000000..2a5746b --- /dev/null +++ b/tcp_debug.h @@ -0,0 +1,13 @@ +#ifndef TCP_DEBUG_H +#define TCP_DEBUG_H + +#include + +/** + * Initializes the TCP debug server on port 4242. + * Registers a stdio driver that redirects printf output to connected TCP clients. + * Returns true on success. + */ +bool tcp_debug_init(void); + +#endif