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

52
lib/serial_uploader.h Normal file
View File

@@ -0,0 +1,52 @@
#ifndef SERIAL_UPLOADER_H
#define SERIAL_UPLOADER_H
#include <cstdint>
#include "ff.h"
class GameLauncher;
class SerialUploader {
public:
SerialUploader(GameLauncher* launcher);
// Process incoming serial data (call this frequently in main loop)
// Returns true if a game was launched
bool process();
// Get the filename of the last uploaded game (without .lua extension)
const char* get_last_uploaded_filename() const { return last_uploaded_name; }
private:
enum State {
IDLE,
RECEIVING_FILENAME,
RECEIVING_SIZE,
RECEIVING_DATA,
WRITING_FILE,
LAUNCHING_GAME
};
State state;
GameLauncher* game_launcher;
// Upload state
char filename[64];
char last_uploaded_name[64]; // Game name without .lua extension
uint32_t file_size;
uint32_t bytes_received;
uint8_t* file_buffer;
// Base64 decoding buffer
char base64_buffer[4];
int base64_index;
// Helper methods
void reset();
bool write_file_to_sd();
void launch_game();
uint8_t decode_base64_char(char c);
void decode_base64_block(const char* input, uint8_t* output);
};
#endif // SERIAL_UPLOADER_H