Add serial upload tool for rapid Lua game iteration

Implements a complete serial upload workflow that allows uploading and
immediately testing Lua games via USB serial connection.

New Components:
- SerialUploader: Receives files via serial, writes to SD card
- upload_game.py: Python tool for sending files from host computer
- Protocol: Text-based with base64 encoding for reliability

Key Features:
- Uploads file to /games folder on SD card
- Overwrites existing files (FA_CREATE_ALWAYS)
- Auto-launches uploaded game immediately
- Proper memory cleanup (prevents Lua state conflicts)

SD Card Fixes:
- Fixed SPI speed management (12.5MHz for SD, 32MHz for display)
- Fixed SD write protocol (poll for data response token)
- Added speed switching wrappers around all FatFS operations
- Cleaned up excessive debug output

Game Launcher Improvements:
- Added clear_games() to prevent duplicate registrations
- Added cleanup in select_game_by_name() to delete old instances
- Added exact match priority in game selection
- LuaGameLoader now has clear_factory_data() for memory cleanup

Integration:
- Added serial_uploader to CMakeLists.txt
- Integrated into main loop in basic1.cpp
- Re-scans games after upload to pick up new files

Documentation:
- UPLOAD_TOOL.md: Usage instructions
- sd_card_best_practices.md: Critical lessons learned

Known Issues:
- Game launch after upload occasionally causes freeze (needs investigation)
- Display may not refresh properly after upload

Usage:
  python upload_game.py games/lua_examples/2048.lua /dev/tty.usbmodem101

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Adolfo Reyna
2026-02-12 22:52:57 -05:00
parent b26f3bf775
commit 84b009c33e
14 changed files with 1222 additions and 34 deletions

View File

@@ -58,6 +58,7 @@ extern "C" {
#include "demo_game.h"
#include "monopoly_game.h"
#include "lua_game_loader.h"
#include "serial_uploader.h"
// Binary info for RP2350 - ensures proper boot image structure
@@ -436,7 +437,11 @@ int main()
// Create GameLauncher
GameLauncher launcher(V_WIDTH, V_HEIGHT, &renderer, &gui, &input_manager);
// Create SerialUploader for rapid game iteration
SerialUploader serial_uploader(&launcher);
printf("Serial uploader initialized\n");
// Register available games
launcher.register_game("Tic-Tac-Toe", "Classic 2-player game",
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
@@ -563,11 +568,22 @@ int main()
const uint32_t TARGET_FRAME_TIME_MS = 33; // 1000ms / 30fps ≈ 33ms
uint32_t last_frame_time = 0;
bool needs_refresh = false; // Track if screen needs redraw
while (1) {
// 0. Process serial uploads (for rapid game iteration)
bool game_launched_via_serial = serial_uploader.process();
if (game_launched_via_serial) {
// A new game was uploaded and launched - trigger redraw
needs_refresh = true;
current_game = launcher.get_selected_game();
// Note: game is already initialized by select_game_by_name()
}
// Determine if we should sleep or stay awake for updates
bool stay_awake = false;
if (pending_refresh) stay_awake = true;
if (launcher.is_game_selected()) {
Game* g = launcher.get_selected_game();
if (g && g->wants_frame_updates()) {
@@ -579,9 +595,8 @@ int main()
// Sleep until interrupt wakes us up (very power efficient!)
__wfi(); // Wait For Interrupt - CPU sleeps until any interrupt occurs
}
InputEvent input = {INPUT_NONE, 0, 0, 0, 0, 0, false};
bool needs_refresh = false;
// 1. Process button input first (higher priority)
input = input_manager.process_button_input();