diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 29203a4..b397401 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,6 @@ { "recommendations": [ + "Jason2866.esp-decoder", "pioarduino.pioarduino-ide" ], "unwantedRecommendations": [ diff --git a/lib/kodedot_bsp/src/display_manager.cpp b/lib/kodedot_bsp/src/display_manager.cpp index c29d8f5..3416768 100644 --- a/lib/kodedot_bsp/src/display_manager.cpp +++ b/lib/kodedot_bsp/src/display_manager.cpp @@ -51,7 +51,7 @@ bool DisplayManager::init() { if (pct > 100) pct = 100; gfx->setBrightness((uint8_t)(((uint16_t)pct * 255 + 50) / 100)); } - gfx->fillScreen(BLACK); + gfx->fillScreen((uint16_t)0x0000); Serial.println("[DISP] Panel OK"); /* ── LVGL 9 init ───────────────────────────────────────────── */ diff --git a/src/main.cpp b/src/main.cpp index cff6c3a..75f0736 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -72,6 +72,12 @@ static bool pmic_ok = false; static unsigned long last_time_update = 0; static unsigned long last_batt_update = 0; +// FTP task → main loop cross-core signalling (set by FTP callbacks, consumed in loop) +enum FtpUiState : uint8_t { FTP_ST_IDLE, FTP_ST_CONNECTED, FTP_ST_TRANSFERRING }; +static volatile FtpUiState ftp_ui_state = FTP_ST_IDLE; +static volatile bool ftp_ui_dirty = false; // UI label + LED need refresh +static volatile bool ftp_sd_dirty = false; // SD usage info needs refresh + // --------------------------------------------------------------------------- // NeoPixel helpers // --------------------------------------------------------------------------- @@ -141,6 +147,7 @@ static void updateSdInfo() { /** * Called on FTP connect / disconnect events. + * NOTE: Runs on the FTP task (core 0) — only set flags here, never call LVGL/UI. */ void ftpCallback(FtpOperation ftpOperation, uint32_t freeSpace, uint32_t totalSpace) { switch (ftpOperation) { @@ -148,57 +155,62 @@ void ftpCallback(FtpOperation ftpOperation, uint32_t freeSpace, uint32_t totalSp Serial.println("[FTP] Client connected"); ftp_client_on = true; ftp_transfer = false; - ui_set_ftp("Connected"); + ftp_ui_state = FTP_ST_CONNECTED; + ftp_ui_dirty = true; break; case FTP_DISCONNECT: Serial.println("[FTP] Client disconnected"); ftp_client_on = false; ftp_transfer = false; - ui_set_ftp("Idle"); + ftp_ui_state = FTP_ST_IDLE; + ftp_ui_dirty = true; break; case FTP_FREE_SPACE_CHANGE: Serial.printf("[FTP] Free space: %u / %u\n", freeSpace, totalSpace); - updateSdInfo(); + ftp_sd_dirty = true; break; default: break; } - updateLed(); } /** * Called during file transfer operations. + * NOTE: Runs on the FTP task (core 0) — only set flags here, never call LVGL/UI. */ void ftpTransferCallback(FtpTransferOperation ftpOperation, const char *name, uint32_t transferredSize) { switch (ftpOperation) { case FTP_UPLOAD_START: Serial.printf("[FTP] Upload start: %s\n", name); - ftp_transfer = true; - ui_set_ftp("Transferring"); + ftp_transfer = true; + ftp_ui_state = FTP_ST_TRANSFERRING; + ftp_ui_dirty = true; break; case FTP_DOWNLOAD_START: Serial.printf("[FTP] Download start: %s\n", name); - ftp_transfer = true; - ui_set_ftp("Transferring"); + ftp_transfer = true; + ftp_ui_state = FTP_ST_TRANSFERRING; + ftp_ui_dirty = true; break; case FTP_UPLOAD: case FTP_DOWNLOAD: break; // Ongoing — silent to avoid serial flood case FTP_TRANSFER_STOP: Serial.printf("[FTP] Transfer done: %s (%u bytes)\n", name, transferredSize); - ftp_transfer = false; - ui_set_ftp(ftp_client_on ? "Connected" : "Idle"); - updateSdInfo(); + ftp_transfer = false; + ftp_ui_state = ftp_client_on ? FTP_ST_CONNECTED : FTP_ST_IDLE; + ftp_ui_dirty = true; + ftp_sd_dirty = true; break; case FTP_TRANSFER_ERROR: Serial.printf("[FTP] Transfer error: %s\n", name); - ftp_transfer = false; - ui_set_ftp(ftp_client_on ? "Connected" : "Idle"); + ftp_transfer = false; + ftp_ui_state = ftp_client_on ? FTP_ST_CONNECTED : FTP_ST_IDLE; + ftp_ui_dirty = true; break; default: break; } - updateLed(); } // --------------------------------------------------------------------------- @@ -438,7 +450,16 @@ static void initNtp() { Serial.println("[NTP] Warning: time not yet synced (will update in background)"); } -/** Start FTP server with callbacks */ +/** FTP task — runs handleFTP() in a dedicated loop on core 0 */ +static void ftpTask(void *param) { + (void)param; + for (;;) { + ftpSrv.handleFTP(); + vTaskDelay(pdMS_TO_TICKS(1)); // yield ~1 ms between iterations + } +} + +/** Start FTP server with callbacks and launch dedicated task */ static void initFtp() { Serial.println("[FTP] Starting FTP server (user: kode, port: 21)..."); ftpSrv.setCallback(ftpCallback); @@ -446,7 +467,11 @@ static void initFtp() { /* Set local IP so PASV mode sends the correct address to clients */ ftpSrv.setLocalIp(WiFi.localIP()); ftpSrv.begin("kode", "kode"); - Serial.printf("[FTP] FTP server ready (PASV IP: %s, data port: 50009)\n", + + /* Run FTP in its own task on core 0 so it never blocks LVGL on core 1 */ + xTaskCreatePinnedToCore(ftpTask, "ftp", 8192, NULL, 1, NULL, 0); + + Serial.printf("[FTP] FTP server ready on core 0 (PASV IP: %s, data port: 50009)\n", WiFi.localIP().toString().c_str()); ui_set_ftp("Idle"); } @@ -543,7 +568,18 @@ void setup() { // --------------------------------------------------------------------------- void loop() { - ftpSrv.handleFTP(); + /* Process FTP status flags (set by callbacks on core 0, consumed here on core 1) */ + if (ftp_ui_dirty) { + ftp_ui_dirty = false; + static const char *ftp_st_str[] = {"Idle", "Connected", "Transferring"}; + ui_set_ftp(ftp_st_str[ftp_ui_state]); + updateLed(); + } + if (ftp_sd_dirty) { + ftp_sd_dirty = false; + updateSdInfo(); + } + display.update(); updateTimeDisplay(); updateBattery();