From 65650a7b57fafda2d2f51671e7af3e8715fff1a1 Mon Sep 17 00:00:00 2001 From: Adolfo Reyna Date: Wed, 10 Dec 2025 08:09:38 -0500 Subject: [PATCH] wifi working tested on hardware --- CMakeLists.txt | 3 +- command_processor.cpp | 14 ++--- command_processor.h | 4 +- epaper_manager.cpp | 4 ++ hello_usb.cpp | 83 +++++++++++++++++++++++----- lwipopts.h | 53 ++++++++++++++++++ wifi_manager.cpp | 125 ++++++++++++++++++++++++++++++++++++++++++ wifi_manager.h | 44 +++++++++++++++ 8 files changed, 305 insertions(+), 25 deletions(-) create mode 100644 lwipopts.h create mode 100644 wifi_manager.cpp create mode 100644 wifi_manager.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a194035..aaf8579 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ if (TARGET tinyusb_host) keyboard_input.cpp command_processor.cpp epaper_manager.cpp + wifi_manager.cpp ) add_subdirectory(pico-ssd1306 commands) @@ -61,7 +62,7 @@ if (TARGET tinyusb_host) ) # pull in common dependencies - target_link_libraries(hello_usb pico_stdlib pico_ssd1306 hardware_i2c Config ePaper GUI Fonts hardware_spi pico_multicore tinyusb_host tinyusb_board) + target_link_libraries(hello_usb pico_stdlib pico_ssd1306 hardware_i2c Config ePaper GUI Fonts hardware_spi pico_multicore tinyusb_host tinyusb_board pico_cyw43_arch_lwip_threadsafe_background) # enable usb output, disable uart output pico_enable_stdio_usb(hello_usb 0) diff --git a/command_processor.cpp b/command_processor.cpp index 80104cf..590f9c1 100644 --- a/command_processor.cpp +++ b/command_processor.cpp @@ -7,13 +7,11 @@ CommandAction parse_command(const char* input) { return CMD_NONE; } - if (strcmp(input, "/refresh") == 0) { - return CMD_REFRESH; - } else if (strcmp(input, "/clear") == 0) { - return CMD_CLEAR; - } else if (strcmp(input, "/wifisetup") == 0) { - return CMD_WIFI; - } - + if (strcmp(input, "/refresh") == 0) return CMD_REFRESH; + if (strcmp(input, "/clear") == 0) return CMD_CLEAR; + if (strcmp(input, "/scan") == 0) return CMD_SCAN; + if (strncmp(input, "/connect ", 9) == 0 || strcmp(input, "/connect") == 0) return CMD_CONNECT; + if (strcmp(input, "/status") == 0) return CMD_STATUS; + return CMD_UNKNOWN; } diff --git a/command_processor.h b/command_processor.h index f427759..34ea461 100644 --- a/command_processor.h +++ b/command_processor.h @@ -5,7 +5,9 @@ enum CommandAction { CMD_NONE, CMD_REFRESH, CMD_CLEAR, - CMD_WIFI, + CMD_SCAN, + CMD_CONNECT, + CMD_STATUS, CMD_UNKNOWN }; diff --git a/epaper_manager.cpp b/epaper_manager.cpp index 5feba44..1ca52f3 100644 --- a/epaper_manager.cpp +++ b/epaper_manager.cpp @@ -1,4 +1,5 @@ #include "epaper_manager.h" +#include "wifi_manager.h" #include "pico/stdlib.h" #include "pico/multicore.h" #include @@ -97,6 +98,9 @@ static void init_epaper_display() { * Core 1 function: Runs display initialization and update handling */ static void core1_display_init() { + // Initialize WiFi on Core 1 so IRQs are handled here + wifi_init(); + init_epaper_display(); g_display_ready = true; diff --git a/hello_usb.cpp b/hello_usb.cpp index e4ee0c9..8233623 100644 --- a/hello_usb.cpp +++ b/hello_usb.cpp @@ -10,12 +10,14 @@ #include #include #include +#include #include "display.h" #include "commands/echo.h" #include "keyboard_input.h" #include "command_processor.h" #include "epaper_manager.h" +#include "wifi_manager.h" // Holds last echoed line for display static char g_last_echo[128] = ""; @@ -28,7 +30,7 @@ static DisplayManager* g_display_manager = nullptr; static char g_input_buffer[MAX_INPUT_LEN]; static int g_buffer_index = 0; -static bool execute_command(CommandAction action) { +static bool execute_command(CommandAction action, const char* input) { switch (action) { case CMD_REFRESH: printf("Command: /refresh\n"); @@ -38,9 +40,54 @@ static bool execute_command(CommandAction action) { printf("Command: /clear\n"); epaper_clear(); return true; - case CMD_WIFI: - printf("Command: /wifisetup (Not implemented)\n"); + case CMD_SCAN: + printf("Command: /scan\n"); + if (g_display_manager) g_display_manager->refresh("Scanning WiFi...", nullptr); + epaper_send_update("Scanning WiFi...", true); + wifi_scan(); return true; + case CMD_CONNECT: { + printf("Command: /connect\n"); + char ssid[33] = {0}; + char password[64] = {0}; + // Skip "/connect " (9 chars) + const char* args = input + 9; + + // Simple parsing: first word is ssid, rest is password + int parsed = sscanf(args, "%32s %63s", ssid, password); + if (parsed < 2) { + printf("Usage: /connect \n"); + if (g_display_manager) g_display_manager->refresh("Usage: /connect ", nullptr); + // epaper_send_update("Usage: /connect ", true); + return true; + } + + if (g_display_manager) g_display_manager->refresh("Connecting...", ssid); + // epaper_send_update("Connecting...", true); + + if (wifi_connect(ssid, password)) { + if (g_display_manager) g_display_manager->refresh("Connected!", ssid); + // epaper_send_update("Connected!", true); + // wifi_save_credentials(ssid, password); + } else { + if (g_display_manager) g_display_manager->refresh("Connection Failed", nullptr); + // epaper_send_update("Connection Failed", true); + } + return true; + } + case CMD_STATUS: { + printf("Command: /status\n"); + struct mallinfo m = mallinfo(); + char status_msg[64]; + // fordblks is the free chunk size in the arena. + // Note: This might not account for the total available system RAM if the heap hasn't grown to fill it yet. + // But it gives an idea of fragmentation and available malloc-able memory within the current arena. + snprintf(status_msg, sizeof(status_msg), "Heap: %d B, IP: %s", m.fordblks, wifi_get_ip()); + printf("%s\n", status_msg); + if (g_display_manager) g_display_manager->refresh(status_msg, nullptr); + epaper_send_update(status_msg, true); + return true; + } default: return false; } @@ -64,34 +111,35 @@ static void process_kbd_report(hid_keyboard_report_t const *report) { } else if (event.is_enter) { printf("\n"); + // Always commit the input line to e-Paper so commands are visible + if (g_buffer_index > 0) { + epaper_send_update(g_input_buffer, true); + } + CommandAction action = parse_command(g_input_buffer); - if (execute_command(action)) { - // Command handled, clear buffer but don't commit line - g_buffer_index = 0; - g_input_buffer[0] = '\0'; - + if (execute_command(action, g_input_buffer)) { + // Command handled if (g_display_manager) { g_display_manager->set_last_echo("Command Executed"); g_display_manager->refresh("", "Command Executed"); } } else { - // Update e-Paper (commit line) - epaper_send_update(g_input_buffer, true); + // Not a command, just text (already committed to e-Paper above) // Save to last echo strncpy(g_last_echo, g_input_buffer, sizeof(g_last_echo) - 1); g_last_echo[sizeof(g_last_echo) - 1] = '\0'; - // Clear buffer - g_buffer_index = 0; - g_input_buffer[0] = '\0'; - // Update OLED if (g_display_manager) { g_display_manager->set_last_echo(g_last_echo); g_display_manager->refresh("", g_last_echo); } } + + // Clear buffer + g_buffer_index = 0; + g_input_buffer[0] = '\0'; } else if (event.is_printable) { printf("%c", event.ascii); if (g_buffer_index < MAX_INPUT_LEN - 1) { @@ -178,7 +226,12 @@ int main() { display.init(); g_display_manager = &display; - display.refresh("Waiting for Keyboard", nullptr); + if (wifi_try_auto_connect()) { + display.refresh("WiFi Auto-Connected", nullptr); + epaper_send_update("WiFi Auto-Connected", true); + } else { + display.refresh("Waiting for Keyboard", nullptr); + } printf("Initializing TinyUSB Host...\n"); tuh_init(BOARD_TUH_RHPORT); diff --git a/lwipopts.h b/lwipopts.h new file mode 100644 index 0000000..d65b650 --- /dev/null +++ b/lwipopts.h @@ -0,0 +1,53 @@ +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + +// Common settings used in most of the pico_w examples +#define NO_SYS 1 +#define LWIP_SOCKET 0 + +#define MEM_LIBC_MALLOC 0 + +#define MEM_ALIGNMENT 4 +#define MEM_SIZE 4000 +#define MEMP_NUM_TCP_SEG 32 +#define MEMP_NUM_ARP_QUEUE 10 +#define PBUF_POOL_SIZE 24 +#define LWIP_ARP 1 +#define LWIP_ETHERNET 1 +#define LWIP_ICMP 1 +#define LWIP_RAW 1 +#define TCP_WND (8 * TCP_MSS) +#define TCP_MSS 1460 +#define TCP_SND_BUF (8 * TCP_MSS) +#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS)) +#define LWIP_NETIF_STATUS_CALLBACK 1 +#define LWIP_NETIF_LINK_CALLBACK 1 +#define LWIP_NETIF_HOSTNAME 1 +#define LWIP_NETCONN 0 +#define MEM_STATS 0 +#define SYS_STATS 0 +#define MEMP_STATS 0 +#define LINK_STATS 0 +#define LWIP_CHKSUM_ALGORITHM 3 +#define LWIP_DHCP 1 +#define LWIP_IPV4 1 +#define LWIP_TCP 1 +#define LWIP_UDP 1 +#define LWIP_DNS 1 +#define LWIP_TCP_KEEPALIVE 1 +#define LWIP_NETIF_TX_SINGLE_PBUF 1 +#define DHCP_DOES_ARP_CHECK 0 +#define LWIP_DHCP_DOES_ACD_CHECK 0 + +// Enable debugging +#ifndef NDEBUG +#define LWIP_DEBUG 1 +#define LWIP_STATS 1 +#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0) +#else +#define LWIP_DEBUG 0 +#define LWIP_STATS 0 +#define LWIP_PLATFORM_DIAG(x) do {} while(0) +#endif + +#endif /* _LWIPOPTS_H */ diff --git a/wifi_manager.cpp b/wifi_manager.cpp new file mode 100644 index 0000000..6fb60e5 --- /dev/null +++ b/wifi_manager.cpp @@ -0,0 +1,125 @@ +#include "wifi_manager.h" +#include "pico/cyw43_arch.h" +#include "hardware/flash.h" +#include "hardware/sync.h" +#include +#include + +// Use the last sector of the 2MB flash +#define FLASH_TARGET_OFFSET (2 * 1024 * 1024 - FLASH_SECTOR_SIZE) +#define WIFI_CREDS_MAGIC 0x42574946 // 'BWIF' + +struct WifiCreds { + uint32_t magic; + char ssid[33]; + char password[64]; +}; + +static volatile bool g_wifi_initialized = false; +static bool g_wifi_connected = false; + +bool wifi_init() { + if (g_wifi_initialized) return true; + + if (cyw43_arch_init()) { + printf("WiFi init failed!\n"); + return false; + } + + cyw43_arch_enable_sta_mode(); + g_wifi_initialized = true; + printf("WiFi initialized on Core %u\n", get_core_num()); + return true; +} + +static int scan_result(void *env, const cyw43_ev_scan_result_t *result) { + if (result) { + printf("SSID: %-32s RSSI: %4d Auth: %u\n", result->ssid, result->rssi, result->auth_mode); + } + return 0; +} + +void wifi_scan() { + // Wait for initialization + int timeout = 500; // 5 seconds + while (!g_wifi_initialized && timeout > 0) { + sleep_ms(10); + timeout--; + } + + if (!g_wifi_initialized) { + printf("WiFi not initialized!\n"); + return; + } + + printf("Starting WiFi scan...\n"); + cyw43_wifi_scan_options_t scan_options = {0}; + int err = cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, scan_result); + if (err != 0) { + printf("Failed to start scan: %d\n", err); + } +} + +void wifi_save_credentials(const char* ssid, const char* password) { + WifiCreds creds; + creds.magic = WIFI_CREDS_MAGIC; + strncpy(creds.ssid, ssid, sizeof(creds.ssid) - 1); + creds.ssid[sizeof(creds.ssid) - 1] = '\0'; + strncpy(creds.password, password, sizeof(creds.password) - 1); + creds.password[sizeof(creds.password) - 1] = '\0'; + + uint32_t ints = save_and_disable_interrupts(); + flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE); + flash_range_program(FLASH_TARGET_OFFSET, (const uint8_t*)&creds, sizeof(creds)); + restore_interrupts(ints); + + printf("WiFi credentials saved to flash.\n"); +} + +bool wifi_try_auto_connect() { + const WifiCreds* creds = (const WifiCreds*)(XIP_BASE + FLASH_TARGET_OFFSET); + + if (creds->magic != WIFI_CREDS_MAGIC) { + printf("No saved WiFi credentials found.\n"); + return false; + } + + printf("Found saved credentials for SSID: %s\n", creds->ssid); + return wifi_connect(creds->ssid, creds->password); +} + +bool wifi_connect(const char* ssid, const char* password) { + // Wait for initialization + int timeout = 500; // 5 seconds + while (!g_wifi_initialized && timeout > 0) { + sleep_ms(10); + timeout--; + } + + if (!g_wifi_initialized) { + printf("WiFi not initialized!\n"); + return false; + } + + printf("Connecting to WiFi: %s...\n", ssid); + + // Connect in blocking mode for simplicity + if (cyw43_arch_wifi_connect_timeout_ms(ssid, password, CYW43_AUTH_WPA2_AES_PSK, 30000)) { + printf("WiFi connection failed!\n"); + g_wifi_connected = false; + return false; + } + + printf("WiFi connected!\n"); + g_wifi_connected = true; + return true; +} + +bool wifi_is_connected() { + return g_wifi_connected; +} + +const char* wifi_get_ip() { + if (!g_wifi_connected) return "Disconnected"; + return ip4addr_ntoa(netif_ip4_addr(&cyw43_state.netif[0])); +} diff --git a/wifi_manager.h b/wifi_manager.h new file mode 100644 index 0000000..625a1fe --- /dev/null +++ b/wifi_manager.h @@ -0,0 +1,44 @@ +#ifndef WIFI_MANAGER_H +#define WIFI_MANAGER_H + +#include + +/** + * Initializes the WiFi hardware. + * Returns true on success. + */ +bool wifi_init(); + +/** + * Scans for available WiFi networks and prints them to stdout. + */ +void wifi_scan(); + +/** + * Connects to the specified WiFi network. + * Returns true if connected successfully. + */ +bool wifi_connect(const char* ssid, const char* password); + +/** + * Saves the WiFi credentials to flash memory for auto-connect. + */ +void wifi_save_credentials(const char* ssid, const char* password); + +/** + * Attempts to connect using saved credentials. + * Returns true if connected. + */ +bool wifi_try_auto_connect(); + +/** + * Returns true if currently connected to WiFi. + */ +bool wifi_is_connected(); + +/** + * Returns the current IP address as a string, or "Disconnected". + */ +const char* wifi_get_ip(); + +#endif // WIFI_MANAGER_H