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

@@ -124,6 +124,7 @@ function draw()
if state == STATE_MENU then
renderer.text_scaled(game.width() / 2 - 15, game.height() / 2 - 30, "2048", true, 2)
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2, "Tap to Start", true, 2)
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2 + 30, "Welcome Adolfo2!", true, 2)
elseif state == STATE_PLAYING or state == STATE_WIN or state == STATE_GAME_OVER then
-- Draw grid
@@ -138,14 +139,14 @@ function draw()
renderer.rect(tile_x, tile_y, tile_size, tile_size, true, false)
else
-- Filled tile
renderer.rect(tile_x, tile_y, tile_size, tile_size, true, true)
renderer.rect(tile_x+2, tile_y+2, tile_size-4, tile_size-4, true, true)
-- Draw value (simplified)
local text = tostring(value)
if string.len(text) <= 2 then
renderer.text_scaled(tile_x + 2, tile_y + 2, text, false, 2)
renderer.text_scaled(tile_x + tile_size / 2 - 4, tile_y + tile_size / 2, text, false, 2)
else
renderer.text_scaled(tile_x + 1, tile_y + 2, text, false, 2)
renderer.text_scaled(tile_x + tile_size / 2 - 8, tile_y + tile_size / 2, text, false, 2)
end
end
end