fix wifi & flash + eink screen on core 1, by using queue

This commit is contained in:
Adolfo Reyna
2025-12-10 13:38:04 -05:00
parent 65650a7b57
commit f197b9c1f4
5 changed files with 80 additions and 36 deletions

View File

@@ -62,7 +62,7 @@ if (TARGET tinyusb_host)
) )
# pull in common dependencies # 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 pico_cyw43_arch_lwip_threadsafe_background) target_link_libraries(hello_usb pico_stdlib pico_ssd1306 hardware_i2c Config ePaper GUI Fonts hardware_spi pico_multicore pico_flash tinyusb_host tinyusb_board pico_cyw43_arch_lwip_threadsafe_background)
# enable usb output, disable uart output # enable usb output, disable uart output
pico_enable_stdio_usb(hello_usb 0) pico_enable_stdio_usb(hello_usb 0)

View File

@@ -2,6 +2,8 @@
#include "wifi_manager.h" #include "wifi_manager.h"
#include "pico/stdlib.h" #include "pico/stdlib.h"
#include "pico/multicore.h" #include "pico/multicore.h"
#include "pico/flash.h"
#include "pico/util/queue.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
@@ -31,6 +33,10 @@ static bool g_force_full_refresh = false; // Flag to force full refresh when li
// Synchronization flag for multicore initialization // Synchronization flag for multicore initialization
static volatile bool g_display_ready = false; static volatile bool g_display_ready = false;
// Queue for display messages (replacing raw FIFO to avoid conflict with flash_safe_execute)
static queue_t g_display_queue;
#define QUEUE_LENGTH 8
// Display update message structure for inter-core communication // Display update message structure for inter-core communication
typedef struct { typedef struct {
EntryList entries; EntryList entries;
@@ -84,9 +90,18 @@ static void init_epaper_display() {
EPD_7IN5B_V2_Display_Partial(g_epd_image, 0, 0, EPD_7IN5B_V2_WIDTH, EPD_7IN5B_V2_HEIGHT); EPD_7IN5B_V2_Display_Partial(g_epd_image, 0, 0, EPD_7IN5B_V2_WIDTH, EPD_7IN5B_V2_HEIGHT);
EPD_7IN5B_V2_Display_Partial(g_epd_image, 0, 0, EPD_7IN5B_V2_WIDTH, EPD_7IN5B_V2_HEIGHT); EPD_7IN5B_V2_Display_Partial(g_epd_image, 0, 0, EPD_7IN5B_V2_WIDTH, EPD_7IN5B_V2_HEIGHT);
printf("Trying to connect to wifi\r\n");
bool connected = wifi_try_auto_connect();
Paint_SelectImage(g_epd_red); Paint_SelectImage(g_epd_red);
printf("Drawing header\r\n"); printf("Drawing header\r\n");
if (connected) {
Paint_DrawString_EN(10, 10, "What's new today: ...", &Font24, WHITE, RED);
} else {
Paint_DrawString_EN(10, 10, "What's new today:", &Font24, WHITE, RED); Paint_DrawString_EN(10, 10, "What's new today:", &Font24, WHITE, RED);
}
// Display the image with both black and red buffers // Display the image with both black and red buffers
EPD_7IN5B_V2_Display_Partial(g_epd_red, 0, 0, EPD_7IN5B_V2_WIDTH, EPD_7IN5B_V2_HEIGHT); EPD_7IN5B_V2_Display_Partial(g_epd_red, 0, 0, EPD_7IN5B_V2_WIDTH, EPD_7IN5B_V2_HEIGHT);
@@ -98,6 +113,9 @@ static void init_epaper_display() {
* Core 1 function: Runs display initialization and update handling * Core 1 function: Runs display initialization and update handling
*/ */
static void core1_display_init() { static void core1_display_init() {
// Initialize flash safe execute support for this core
flash_safe_execute_core_init();
// Initialize WiFi on Core 1 so IRQs are handled here // Initialize WiFi on Core 1 so IRQs are handled here
wifi_init(); wifi_init();
@@ -109,20 +127,18 @@ static void core1_display_init() {
// Initialize for partial refresh on second call // Initialize for partial refresh on second call
EPD_7IN5B_V2_Init_Part(); EPD_7IN5B_V2_Init_Part();
// Core 1 main loop: handle display updates via FIFO // Core 1 main loop: handle display updates via Queue
while (true) { while (true) {
// Check if there's a message from core 0 DisplayMessage *msg;
if (multicore_fifo_rvalid()) { // Check if there's a message in the queue
// Read the message pointer if (queue_try_remove(&g_display_queue, &msg)) {
uint32_t msg_addr = multicore_fifo_pop_blocking();
DisplayMessage *msg = (DisplayMessage *)msg_addr;
// Drain FIFO to get the latest message // Drain Queue to get the latest message
while (multicore_fifo_rvalid()) { DisplayMessage *next_msg;
while (queue_try_remove(&g_display_queue, &next_msg)) {
printf("[Core 1] Skipping intermediate update\n"); printf("[Core 1] Skipping intermediate update\n");
free(msg); // Free the stale message free(msg); // Free the stale message
msg_addr = multicore_fifo_pop_blocking(); msg = next_msg;
msg = (DisplayMessage *)msg_addr;
} }
printf("[Core 1] Updating display with %d entries\n", msg->entries.count); printf("[Core 1] Updating display with %d entries\n", msg->entries.count);
@@ -173,12 +189,16 @@ static void core1_display_init() {
} }
void epaper_start_background_thread() { void epaper_start_background_thread() {
// Initialize the queue before starting the thread
queue_init(&g_display_queue, sizeof(DisplayMessage*), QUEUE_LENGTH);
printf("Launching e-Paper display init on core 1...\n"); printf("Launching e-Paper display init on core 1...\n");
multicore_launch_core1(core1_display_init); multicore_launch_core1(core1_display_init);
} }
void epaper_send_update(const char *entry, bool finish_line) { void epaper_send_update(const char *entry, bool finish_line) {
if (!g_display_ready) return; // Don't send if display isn't ready if (!g_display_ready) return; // Don't send if display isn't ready
// printf("[Core 0] Preparing display update: '%s' (finish_line=%d)\n", entry, finish_line);
// Check if list is at capacity // Check if list is at capacity
if (g_entry_list.count >= MAX_ENTRIES) { if (g_entry_list.count >= MAX_ENTRIES) {
@@ -224,11 +244,11 @@ void epaper_send_update(const char *entry, bool finish_line) {
msg->yend = msg->ystart + 25; msg->yend = msg->ystart + 25;
} }
// Send message pointer to core 1 if FIFO has space // Send message pointer to core 1 via Queue
if (multicore_fifo_wready()) { if (queue_try_add(&g_display_queue, &msg)) {
multicore_fifo_push_blocking((uint32_t)msg); // printf("[Core 0] Display update sent to core 1\n");
} else { } else {
printf("[Core 0] FIFO full, skipping display update\n"); printf("[Core 0] Queue full, skipping display update\n");
free(msg); free(msg);
} }

View File

@@ -58,7 +58,7 @@ static bool execute_command(CommandAction action, const char* input) {
if (parsed < 2) { if (parsed < 2) {
printf("Usage: /connect <ssid> <password>\n"); printf("Usage: /connect <ssid> <password>\n");
if (g_display_manager) g_display_manager->refresh("Usage: /connect <ssid> <pass>", nullptr); if (g_display_manager) g_display_manager->refresh("Usage: /connect <ssid> <pass>", nullptr);
// epaper_send_update("Usage: /connect <ssid> <pass>", true); epaper_send_update("Usage: /connect <ssid> <pass>", true);
return true; return true;
} }
@@ -67,11 +67,18 @@ static bool execute_command(CommandAction action, const char* input) {
if (wifi_connect(ssid, password)) { if (wifi_connect(ssid, password)) {
if (g_display_manager) g_display_manager->refresh("Connected!", ssid); if (g_display_manager) g_display_manager->refresh("Connected!", ssid);
// epaper_send_update("Connected!", true); int rc = wifi_save_credentials(ssid, password);
// wifi_save_credentials(ssid, password); if (rc != 0) {
char err_msg[32];
snprintf(err_msg, sizeof(err_msg), "Flash Error: %d", rc);
if (g_display_manager) g_display_manager->refresh(err_msg, nullptr);
epaper_send_update(err_msg, true);
} else {
epaper_send_update("Connected!", true);
}
} else { } else {
if (g_display_manager) g_display_manager->refresh("Connection Failed", nullptr); if (g_display_manager) g_display_manager->refresh("Connection Failed", nullptr);
// epaper_send_update("Connection Failed", true); epaper_send_update("Connection Failed", true);
} }
return true; return true;
} }
@@ -219,20 +226,13 @@ int main() {
sleep_ms(3000); // Give time for power to settle and serial to connect sleep_ms(3000); // Give time for power to settle and serial to connect
printf("System Booting...\n"); printf("System Booting...\n");
// Launch display initialization on core 1 // Launch display initialization on core 1, WIFI and Flash safe execute
epaper_start_background_thread(); epaper_start_background_thread();
DisplayManager display; DisplayManager display;
display.init(); display.init();
g_display_manager = &display; g_display_manager = &display;
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"); printf("Initializing TinyUSB Host...\n");
tuh_init(BOARD_TUH_RHPORT); tuh_init(BOARD_TUH_RHPORT);
printf("TinyUSB Host Initialized.\n"); printf("TinyUSB Host Initialized.\n");

View File

@@ -1,5 +1,7 @@
#include "wifi_manager.h" #include "wifi_manager.h"
#include "pico/cyw43_arch.h" #include "pico/cyw43_arch.h"
#include "pico/multicore.h"
#include "pico/flash.h"
#include "hardware/flash.h" #include "hardware/flash.h"
#include "hardware/sync.h" #include "hardware/sync.h"
#include <stdio.h> #include <stdio.h>
@@ -60,7 +62,20 @@ void wifi_scan() {
} }
} }
void wifi_save_credentials(const char* ssid, const char* password) { // Helper struct for flash operations
struct FlashWriteParams {
const uint8_t* data;
size_t size;
};
// Actual flash operation to be executed safely
static void do_flash_write(void *param) {
FlashWriteParams *p = (FlashWriteParams*)param;
flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE);
flash_range_program(FLASH_TARGET_OFFSET, p->data, p->size);
}
int wifi_save_credentials(const char* ssid, const char* password) {
WifiCreds creds; WifiCreds creds;
creds.magic = WIFI_CREDS_MAGIC; creds.magic = WIFI_CREDS_MAGIC;
strncpy(creds.ssid, ssid, sizeof(creds.ssid) - 1); strncpy(creds.ssid, ssid, sizeof(creds.ssid) - 1);
@@ -68,17 +83,25 @@ void wifi_save_credentials(const char* ssid, const char* password) {
strncpy(creds.password, password, sizeof(creds.password) - 1); strncpy(creds.password, password, sizeof(creds.password) - 1);
creds.password[sizeof(creds.password) - 1] = '\0'; creds.password[sizeof(creds.password) - 1] = '\0';
uint32_t ints = save_and_disable_interrupts(); FlashWriteParams params = { (const uint8_t*)&creds, sizeof(creds) };
flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE); int rc = flash_safe_execute(do_flash_write, &params, 1000);
flash_range_program(FLASH_TARGET_OFFSET, (const uint8_t*)&creds, sizeof(creds));
restore_interrupts(ints);
if (rc == PICO_OK) {
printf("WiFi credentials saved to flash.\n"); printf("WiFi credentials saved to flash.\n");
} else {
printf("Failed to save WiFi credentials: %d\n", rc);
}
return rc;
} }
bool wifi_try_auto_connect() { bool wifi_try_auto_connect() {
// On RP2040/RP2350, flash is memory-mapped at XIP_BASE (0x10000000).
// We can read from it directly like a normal pointer without special API calls.
// The hardware XIP controller handles fetching data from the flash chip.
const WifiCreds* creds = (const WifiCreds*)(XIP_BASE + FLASH_TARGET_OFFSET); const WifiCreds* creds = (const WifiCreds*)(XIP_BASE + FLASH_TARGET_OFFSET);
// Check for the magic number to verify valid data exists.
// Erased flash reads as 0xFFFFFFFF, so this check fails if no data was saved.
if (creds->magic != WIFI_CREDS_MAGIC) { if (creds->magic != WIFI_CREDS_MAGIC) {
printf("No saved WiFi credentials found.\n"); printf("No saved WiFi credentials found.\n");
return false; return false;

View File

@@ -22,8 +22,9 @@ bool wifi_connect(const char* ssid, const char* password);
/** /**
* Saves the WiFi credentials to flash memory for auto-connect. * Saves the WiFi credentials to flash memory for auto-connect.
* Returns 0 (PICO_OK) on success, or an error code on failure.
*/ */
void wifi_save_credentials(const char* ssid, const char* password); int wifi_save_credentials(const char* ssid, const char* password);
/** /**
* Attempts to connect using saved credentials. * Attempts to connect using saved credentials.