diff --git a/src/debug/SerialDebug.cpp b/src/debug/SerialDebug.cpp new file mode 100644 index 0000000..b680aa4 --- /dev/null +++ b/src/debug/SerialDebug.cpp @@ -0,0 +1,125 @@ +#include "SerialDebug.h" + +enum State { + IDLE, READING_HEADER, READING_PAYLOAD +}; + +struct PacketHeader { + uint8_t command; + uint16_t length; // 2 bytes for length per protocol spec + uint8_t checksum; +}; + +bool debugMode = false; + +State currentState = IDLE; + +uint8_t rxBuffer[1024]; + +namespace SerialDebug { + +int handleSerialInput() { + static PacketHeader currentHeader; + static uint16_t bytesRead = 0; + + // Continue processing if we're in the middle of a packet, even if no bytes available + while (Serial.available() > 0 || currentState == READING_PAYLOAD) { + switch (currentState) { + case IDLE: + if (Serial.read() == 0xAA) { + Serial.println("Got magic byte 0xAA"); + currentState = READING_HEADER; + bytesRead = 0; + } + break; + case READING_HEADER: + { + int avail = Serial.available(); + if (avail < 4) { + Serial.print("Waiting for header bytes, have "); + Serial.print(avail); + Serial.println("/4"); + break; + } + currentHeader.command = Serial.read(); + // Read 2 bytes for length (little-endian) + uint8_t lengthLow = Serial.read(); + uint8_t lengthHigh = Serial.read(); + currentHeader.length = lengthLow | (lengthHigh << 8); + currentHeader.checksum = Serial.read(); + Serial.print("Got header: cmd=0x"); + Serial.print(currentHeader.command, HEX); + Serial.print(" len="); + Serial.print(currentHeader.length); + Serial.print(" checksum=0x"); + Serial.println(currentHeader.checksum, HEX); + currentState = READING_PAYLOAD; + bytesRead = 0; + } + break; + case READING_PAYLOAD: + if (currentHeader.length == 0) { + Serial.println("No payload, processing immediately"); + currentState = IDLE; + return processPacket(currentHeader, rxBuffer); + } else if (Serial.available() >= currentHeader.length) { + Serial.print("Reading "); + Serial.print(currentHeader.length); + Serial.println(" bytes of payload"); + Serial.readBytes(rxBuffer, currentHeader.length); + currentState = IDLE; + return processPacket(currentHeader, rxBuffer); + } else { + Serial.print("Waiting for payload: have "); + Serial.print(Serial.available()); + Serial.print("/"); + Serial.println(currentHeader.length); + } + break; + } + } + return -1; // No data processed +} + +int processPacket(PacketHeader& header, uint8_t* data) { + switch (header.command) { + case 0x01: + // Handle command 0x01 + if (!debugMode) { + return 0x00; + } + + break; + case 0x02: + // Handle command 0x02 + if (!debugMode) { + return 0x00; + } + break; + case 0x03: + // Handle command 0x03 + if (!debugMode) { + return 0x00; + } + break; + case 0x04: + debugMode = true; + Serial.println("DEBUG MODE SET TO TRUE"); + return 0x04; + break; + case 0x05: + debugMode = false; + Serial.println("DEBUG MODE SET TO FALSE"); + return 0x05; + break; + default: + return 0x00; + break; + } +} + +bool getDebugMode() { + return debugMode; +} + +} // namespace SerialDebug \ No newline at end of file diff --git a/src/debug/SerialDebug.h b/src/debug/SerialDebug.h new file mode 100644 index 0000000..190f5eb --- /dev/null +++ b/src/debug/SerialDebug.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +struct PacketHeader; + +namespace SerialDebug { + int handleSerialInput(); + int processPacket(PacketHeader& header, uint8_t* data); + bool getDebugMode(); +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 88545f6..b79463c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,6 +28,7 @@ extern "C" { #include "lua/bindings/lua_gfx.h" #include "lua/require_sd.h" #include "lua/bindings/lua_keyboard.h" +#include "debug/SerialDebug.h" // ------------------------------- // Build-time configuration knobs @@ -64,6 +65,9 @@ struct LuaHost { static LuaHost g_host; +// Sprite for double-buffered debug mode indicator +static LGFX_Sprite debug_sprite(&M5Cardputer.Display); + static void* host_from_lua(lua_State* L) { // Store LuaHost* in the Lua "extra space" (Lua 5.4 feature). void** p = reinterpret_cast(lua_getextraspace(L)); @@ -108,7 +112,7 @@ static bool read_entire_file_from_sd(const String& path, std::unique_ptr ") + next); - lua_boot_and_load(g_host, next); - // Note: lua_boot_and_load updates g_host fields. + // tick(dt) + lua_pushnumber(g_host.L, dt); + if (!lua_call_optional(g_host.L, "tick", 1, 0)) { + // On error, keep Lua alive so user can see the message. (They can switch apps via reset.) + delay(250); + return; } + + // draw() + if (!lua_call_optional(g_host.L, "draw", 0, 0)) { + delay(250); + return; + } + + // If Lua requested a new script, reload cleanly between frames. + if (g_host.reload_requested) { + String next = g_host.pending_path; + if (!next.length()) { + // If someone passed "", just ignore. + g_host.reload_requested = false; + } else { + ui_status("Switching to", next); + log_line(String("switch_app -> ") + next); + lua_boot_and_load(g_host, next); + // Note: lua_boot_and_load updates g_host fields. + } + } + + // Keep loop responsive; adjust later if you add a fixed framerate. + delay(1); } - // Keep loop responsive; adjust later if you add a fixed framerate. - delay(1); + int serialResult = SerialDebug::handleSerialInput(); + if (serialResult == 0x00) { + ui_status("Failed to process serial input", "Unknown command"); + delay(1000); // Show error briefly before continuing + } else if (serialResult == 0x04) { + Serial.println("Debug mode ENABLED"); + } + + if (SerialDebug::getDebugMode()) { + // Draw to sprite buffer first (double-buffering to prevent flicker) + debug_sprite.fillSprite(BLACK); + debug_sprite.setTextColor(WHITE, BLACK); + debug_sprite.drawString("DEV MODE", 5, 2.5); + + // Push sprite to display in one operation + debug_sprite.pushSprite(0, 0); + } }