diff --git a/Firmware/components/backend-esp/CMakeLists.txt b/Firmware/components/backend-esp/CMakeLists.txt new file mode 100644 index 0000000..aabc092 --- /dev/null +++ b/Firmware/components/backend-esp/CMakeLists.txt @@ -0,0 +1,35 @@ +idf_component_register( + SRCS + "src/bat_mon.cpp" + "src/buttons.cpp" + "src/buzzer.cpp" + "src/esp_backend.cpp" + "src/display.cpp" + "src/fs_helper.cpp" + "src/i2c_global.cpp" + "src/power_helper.cpp" + "src/shutdowner.cpp" + "src/spi_global.cpp" + INCLUDE_DIRS + "include" + PRIV_REQUIRES + driver + esp_timer + esp_driver_i2c + esp_driver_spi + littlefs + nvs_flash +) + +add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/../../sdk/backend_interface" backend_interface_from_backend_esp) + +add_library(cardboy_backend_esp INTERFACE) +target_link_libraries(cardboy_backend_esp + INTERFACE + ${COMPONENT_LIB} +) + +target_link_libraries(${COMPONENT_LIB} + PUBLIC + cardboy_backend_interface +) diff --git a/Firmware/main/include/cardboy/backend/backend_impl.hpp b/Firmware/components/backend-esp/include/cardboy/backend/backend_impl.hpp similarity index 100% rename from Firmware/main/include/cardboy/backend/backend_impl.hpp rename to Firmware/components/backend-esp/include/cardboy/backend/backend_impl.hpp diff --git a/Firmware/main/include/bat_mon.hpp b/Firmware/components/backend-esp/include/cardboy/backend/esp/bat_mon.hpp similarity index 89% rename from Firmware/main/include/bat_mon.hpp rename to Firmware/components/backend-esp/include/cardboy/backend/esp/bat_mon.hpp index fd3b85a..805aed8 100644 --- a/Firmware/main/include/bat_mon.hpp +++ b/Firmware/components/backend-esp/include/cardboy/backend/esp/bat_mon.hpp @@ -5,10 +5,12 @@ #ifndef CB_BAT_MON_HPP #define CB_BAT_MON_HPP -#include "config.hpp" +#include "cardboy/backend/esp/config.hpp" #include "driver/i2c_master.h" #include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include class BatMon { public: diff --git a/Firmware/main/include/buttons.hpp b/Firmware/components/backend-esp/include/cardboy/backend/esp/buttons.hpp similarity index 97% rename from Firmware/main/include/buttons.hpp rename to Firmware/components/backend-esp/include/cardboy/backend/esp/buttons.hpp index e086652..bdfd519 100644 --- a/Firmware/main/include/buttons.hpp +++ b/Firmware/components/backend-esp/include/cardboy/backend/esp/buttons.hpp @@ -7,6 +7,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include typedef enum { BTN_START = 1 << 1, diff --git a/Firmware/main/include/buzzer.hpp b/Firmware/components/backend-esp/include/cardboy/backend/esp/buzzer.hpp similarity index 100% rename from Firmware/main/include/buzzer.hpp rename to Firmware/components/backend-esp/include/cardboy/backend/esp/buzzer.hpp diff --git a/Firmware/main/include/config.hpp b/Firmware/components/backend-esp/include/cardboy/backend/esp/config.hpp similarity index 100% rename from Firmware/main/include/config.hpp rename to Firmware/components/backend-esp/include/cardboy/backend/esp/config.hpp diff --git a/Firmware/main/include/display.hpp b/Firmware/components/backend-esp/include/cardboy/backend/esp/display.hpp similarity index 97% rename from Firmware/main/include/display.hpp rename to Firmware/components/backend-esp/include/cardboy/backend/esp/display.hpp index 1e36b50..98155c5 100644 --- a/Firmware/main/include/display.hpp +++ b/Firmware/components/backend-esp/include/cardboy/backend/esp/display.hpp @@ -5,13 +5,14 @@ #ifndef CB_DISPLAY_HPP #define CB_DISPLAY_HPP -#include "config.hpp" +#include "cardboy/backend/esp/config.hpp" #include "driver/spi_master.h" // (Async memcpy removed for debugging simplification) #include #include +#include namespace SMD { diff --git a/Firmware/main/include/fs_helper.hpp b/Firmware/components/backend-esp/include/cardboy/backend/esp/fs_helper.hpp similarity index 100% rename from Firmware/main/include/fs_helper.hpp rename to Firmware/components/backend-esp/include/cardboy/backend/esp/fs_helper.hpp diff --git a/Firmware/main/include/i2c_global.hpp b/Firmware/components/backend-esp/include/cardboy/backend/esp/i2c_global.hpp similarity index 94% rename from Firmware/main/include/i2c_global.hpp rename to Firmware/components/backend-esp/include/cardboy/backend/esp/i2c_global.hpp index a167817..d129af9 100644 --- a/Firmware/main/include/i2c_global.hpp +++ b/Firmware/components/backend-esp/include/cardboy/backend/esp/i2c_global.hpp @@ -5,7 +5,7 @@ #ifndef CB_I2C_GLOBAL_HPP #define CB_I2C_GLOBAL_HPP -#include "config.hpp" +#include "cardboy/backend/esp/config.hpp" #include "driver/i2c_master.h" diff --git a/Firmware/main/include/power_helper.hpp b/Firmware/components/backend-esp/include/cardboy/backend/esp/power_helper.hpp similarity index 100% rename from Firmware/main/include/power_helper.hpp rename to Firmware/components/backend-esp/include/cardboy/backend/esp/power_helper.hpp diff --git a/Firmware/main/include/shutdowner.hpp b/Firmware/components/backend-esp/include/cardboy/backend/esp/shutdowner.hpp similarity index 100% rename from Firmware/main/include/shutdowner.hpp rename to Firmware/components/backend-esp/include/cardboy/backend/esp/shutdowner.hpp diff --git a/Firmware/main/include/spi_global.hpp b/Firmware/components/backend-esp/include/cardboy/backend/esp/spi_global.hpp similarity index 94% rename from Firmware/main/include/spi_global.hpp rename to Firmware/components/backend-esp/include/cardboy/backend/esp/spi_global.hpp index 1a8395c..8643985 100644 --- a/Firmware/main/include/spi_global.hpp +++ b/Firmware/components/backend-esp/include/cardboy/backend/esp/spi_global.hpp @@ -5,7 +5,7 @@ #ifndef CB_SPI_GLOBAL_HPP #define CB_SPI_GLOBAL_HPP -#include "config.hpp" +#include "cardboy/backend/esp/config.hpp" #include "driver/spi_master.h" diff --git a/Firmware/components/backend-esp/include/cardboy/backend/esp_backend.hpp b/Firmware/components/backend-esp/include/cardboy/backend/esp_backend.hpp new file mode 100644 index 0000000..a9df431 --- /dev/null +++ b/Firmware/components/backend-esp/include/cardboy/backend/esp_backend.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include "cardboy/sdk/platform.hpp" +#include "cardboy/sdk/services.hpp" + +#include +#include + +namespace cardboy::backend::esp { + +class EspRuntime; + +class EspFramebuffer final : public cardboy::sdk::FramebufferFacade { +public: + EspFramebuffer() = default; + + [[nodiscard]] int width_impl() const; + [[nodiscard]] int height_impl() const; + void drawPixel_impl(int x, int y, bool on); + void clear_impl(bool on); + void frameReady_impl(); + void sendFrame_impl(bool clearAfterSend); + [[nodiscard]] bool frameInFlight_impl() const; +}; + +class EspInput final : public cardboy::sdk::InputFacade { +public: + cardboy::sdk::InputState readState_impl(); +}; + +class EspClock final : public cardboy::sdk::ClockFacade { +public: + std::uint32_t millis_impl(); + void sleep_ms_impl(std::uint32_t ms); +}; + +class EspRuntime { +public: + EspRuntime(); + ~EspRuntime(); + + cardboy::sdk::Services& serviceRegistry(); + + EspFramebuffer framebuffer; + EspInput input; + EspClock clock; + +private: + void initializeHardware(); + + class BuzzerService; + class BatteryService; + class StorageService; + class RandomService; + class HighResClockService; + class PowerService; + class FilesystemService; + + std::unique_ptr buzzerService; + std::unique_ptr batteryService; + std::unique_ptr storageService; + std::unique_ptr randomService; + std::unique_ptr highResClockService; + std::unique_ptr powerService; + std::unique_ptr filesystemService; + + cardboy::sdk::Services services{}; +}; + +struct Backend { + using Framebuffer = EspFramebuffer; + using Input = EspInput; + using Clock = EspClock; +}; + +} // namespace cardboy::backend::esp + +namespace cardboy::backend { +using EspBackend = esp::Backend; +} // namespace cardboy::backend diff --git a/Firmware/main/src/bat_mon.cpp b/Firmware/components/backend-esp/src/bat_mon.cpp similarity index 96% rename from Firmware/main/src/bat_mon.cpp rename to Firmware/components/backend-esp/src/bat_mon.cpp index 284de54..669354f 100644 --- a/Firmware/main/src/bat_mon.cpp +++ b/Firmware/components/backend-esp/src/bat_mon.cpp @@ -2,15 +2,15 @@ // Created by Stepan Usatiuk on 02.03.2025. // -#include "bat_mon.hpp" +#include "cardboy/backend/esp/bat_mon.hpp" -#include +#include "cardboy/backend/esp/power_helper.hpp" #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "i2c_global.hpp" -#include "shutdowner.hpp" +#include "cardboy/backend/esp/i2c_global.hpp" +#include "cardboy/backend/esp/shutdowner.hpp" static i2c_master_dev_handle_t dev_handle; diff --git a/Firmware/main/src/buttons.cpp b/Firmware/components/backend-esp/src/buttons.cpp similarity index 94% rename from Firmware/main/src/buttons.cpp rename to Firmware/components/backend-esp/src/buttons.cpp index 8d6685e..5e64f2a 100644 --- a/Firmware/main/src/buttons.cpp +++ b/Firmware/components/backend-esp/src/buttons.cpp @@ -2,18 +2,18 @@ // Created by Stepan Usatiuk on 02.03.2025. // -#include "buttons.hpp" +#include "cardboy/backend/esp/buttons.hpp" #include #include -#include +#include "cardboy/backend/esp/power_helper.hpp" #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "config.hpp" -#include "i2c_global.hpp" +#include "cardboy/backend/esp/config.hpp" +#include "cardboy/backend/esp/i2c_global.hpp" static i2c_master_dev_handle_t dev_handle; static inline i2c_device_config_t dev_cfg = { diff --git a/Firmware/main/src/buzzer.cpp b/Firmware/components/backend-esp/src/buzzer.cpp similarity index 98% rename from Firmware/main/src/buzzer.cpp rename to Firmware/components/backend-esp/src/buzzer.cpp index 156b7d8..05015c1 100644 --- a/Firmware/main/src/buzzer.cpp +++ b/Firmware/components/backend-esp/src/buzzer.cpp @@ -1,6 +1,6 @@ // Buzzer implementation -#include "buzzer.hpp" -#include "config.hpp" +#include "cardboy/backend/esp/buzzer.hpp" +#include "cardboy/backend/esp/config.hpp" #include #include diff --git a/Firmware/main/src/display.cpp b/Firmware/components/backend-esp/src/display.cpp similarity index 99% rename from Firmware/main/src/display.cpp rename to Firmware/components/backend-esp/src/display.cpp index 78dd1d5..022d1a0 100644 --- a/Firmware/main/src/display.cpp +++ b/Firmware/components/backend-esp/src/display.cpp @@ -1,6 +1,6 @@ // Double-buffered display implementation with async memcpy --------------------------------- -#include "display.hpp" +#include "cardboy/backend/esp/display.hpp" #include #include #include diff --git a/Firmware/components/backend-esp/src/esp_backend.cpp b/Firmware/components/backend-esp/src/esp_backend.cpp new file mode 100644 index 0000000..9d379a5 --- /dev/null +++ b/Firmware/components/backend-esp/src/esp_backend.cpp @@ -0,0 +1,240 @@ +#include "cardboy/backend/esp_backend.hpp" + +#include "cardboy/backend/esp/bat_mon.hpp" +#include "cardboy/backend/esp/buttons.hpp" +#include "cardboy/backend/esp/buzzer.hpp" +#include "cardboy/backend/esp/config.hpp" +#include "cardboy/backend/esp/display.hpp" +#include "cardboy/backend/esp/fs_helper.hpp" +#include "cardboy/backend/esp/i2c_global.hpp" +#include "cardboy/backend/esp/power_helper.hpp" +#include "cardboy/backend/esp/shutdowner.hpp" +#include "cardboy/backend/esp/spi_global.hpp" + +#include "cardboy/sdk/display_spec.hpp" + +#include "driver/gpio.h" +#include "esp_err.h" +#include "esp_random.h" +#include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "nvs.h" +#include "nvs_flash.h" + +#include +#include +#include +#include + +namespace cardboy::backend::esp { + +namespace { +void ensureNvsInit() { + static bool nvsReady = false; + if (nvsReady) + return; + + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + err = nvs_flash_init(); + } + ESP_ERROR_CHECK(err); + nvsReady = true; +} +} // namespace + +class EspRuntime::BuzzerService final : public cardboy::sdk::IBuzzer { +public: + void tone(std::uint32_t freq, std::uint32_t duration_ms, std::uint32_t gap_ms = 0) override { + Buzzer::get().tone(freq, duration_ms, gap_ms); + } + + void beepRotate() override { Buzzer::get().beepRotate(); } + void beepMove() override { Buzzer::get().beepMove(); } + void beepLock() override { Buzzer::get().beepLock(); } + void beepLines(int lines) override { Buzzer::get().beepLines(lines); } + void beepLevelUp(int level) override { Buzzer::get().beepLevelUp(level); } + void beepGameOver() override { Buzzer::get().beepGameOver(); } + + void setMuted(bool muted) override { Buzzer::get().setMuted(muted); } + void toggleMuted() override { Buzzer::get().toggleMuted(); } + [[nodiscard]] bool isMuted() const override { return Buzzer::get().isMuted(); } +}; + +class EspRuntime::BatteryService final : public cardboy::sdk::IBatteryMonitor { +public: + [[nodiscard]] bool hasData() const override { return true; } + [[nodiscard]] float voltage() const override { return BatMon::get().get_voltage(); } + [[nodiscard]] float charge() const override { return BatMon::get().get_charge(); } + [[nodiscard]] float current() const override { return BatMon::get().get_current(); } +}; + +class EspRuntime::StorageService final : public cardboy::sdk::IStorage { +public: + [[nodiscard]] bool readUint32(std::string_view ns, std::string_view key, std::uint32_t& out) override { + ensureNvsInit(); + nvs_handle_t handle; + std::string nsStr(ns); + std::string keyStr(key); + if (nvs_open(nsStr.c_str(), NVS_READONLY, &handle) != ESP_OK) + return false; + std::uint32_t value = 0; + esp_err_t err = nvs_get_u32(handle, keyStr.c_str(), &value); + nvs_close(handle); + if (err != ESP_OK) + return false; + out = value; + return true; + } + + void writeUint32(std::string_view ns, std::string_view key, std::uint32_t value) override { + ensureNvsInit(); + nvs_handle_t handle; + std::string nsStr(ns); + std::string keyStr(key); + if (nvs_open(nsStr.c_str(), NVS_READWRITE, &handle) != ESP_OK) + return; + nvs_set_u32(handle, keyStr.c_str(), value); + nvs_commit(handle); + nvs_close(handle); + } +}; + +class EspRuntime::RandomService final : public cardboy::sdk::IRandom { +public: + [[nodiscard]] std::uint32_t nextUint32() override { return esp_random(); } +}; + +class EspRuntime::HighResClockService final : public cardboy::sdk::IHighResClock { +public: + [[nodiscard]] std::uint64_t micros() override { return static_cast(esp_timer_get_time()); } +}; + +class EspRuntime::PowerService final : public cardboy::sdk::IPowerManager { +public: + void setSlowMode(bool enable) override { PowerHelper::get().set_slow(enable); } + [[nodiscard]] bool isSlowMode() const override { return PowerHelper::get().is_slow(); } +}; + +class EspRuntime::FilesystemService final : public cardboy::sdk::IFilesystem { +public: + bool mount() override { return FsHelper::get().mount() == ESP_OK; } + [[nodiscard]] bool isMounted() const override { return FsHelper::get().isMounted(); } + [[nodiscard]] std::string basePath() const override { + const char* path = FsHelper::get().basePath(); + return path ? std::string(path) : std::string{}; + } +}; + +EspRuntime::EspRuntime() : framebuffer(), input(), clock() { + initializeHardware(); + + buzzerService = std::make_unique(); + batteryService = std::make_unique(); + storageService = std::make_unique(); + randomService = std::make_unique(); + highResClockService = std::make_unique(); + powerService = std::make_unique(); + filesystemService = std::make_unique(); + + services.buzzer = buzzerService.get(); + services.battery = batteryService.get(); + services.storage = storageService.get(); + services.random = randomService.get(); + services.highResClock = highResClockService.get(); + services.powerManager = powerService.get(); + services.filesystem = filesystemService.get(); +} + +EspRuntime::~EspRuntime() = default; + +cardboy::sdk::Services& EspRuntime::serviceRegistry() { return services; } + +void EspRuntime::initializeHardware() { + static bool initialized = false; + if (initialized) + return; + initialized = true; + + ensureNvsInit(); + + PowerHelper::get(); + Shutdowner::get(); + Buttons::get(); + + esp_err_t isrErr = gpio_install_isr_service(0); + if (isrErr != ESP_OK && isrErr != ESP_ERR_INVALID_STATE) { + ESP_ERROR_CHECK(isrErr); + } + + Shutdowner::get().install_isr(); + PowerHelper::get().install_isr(); + Buttons::get().install_isr(); + + I2cGlobal::get(); + BatMon::get(); + SpiGlobal::get(); + SMD::init(); + Buzzer::get().init(); + + FsHelper::get().mount(); +} + +int EspFramebuffer::width_impl() const { return cardboy::sdk::kDisplayWidth; } + +int EspFramebuffer::height_impl() const { return cardboy::sdk::kDisplayHeight; } + +void EspFramebuffer::drawPixel_impl(int x, int y, bool on) { + if (x < 0 || y < 0 || x >= width_impl() || y >= height_impl()) + return; + SMD::set_pixel(x, y, on); +} + +void EspFramebuffer::clear_impl(bool on) { + for (int y = 0; y < height_impl(); ++y) + for (int x = 0; x < width_impl(); ++x) + SMD::set_pixel(x, y, on); +} + +void EspFramebuffer::frameReady_impl() { SMD::frame_ready(); } + +void EspFramebuffer::sendFrame_impl(bool clearAfterSend) { SMD::send_frame(clearAfterSend); } + +bool EspFramebuffer::frameInFlight_impl() const { return SMD::frame_transfer_in_flight(); } + +cardboy::sdk::InputState EspInput::readState_impl() { + cardboy::sdk::InputState state{}; + const uint8_t pressed = Buttons::get().get_pressed(); + if (pressed & BTN_UP) + state.up = true; + if (pressed & BTN_LEFT) + state.left = true; + if (pressed & BTN_RIGHT) + state.right = true; + if (pressed & BTN_DOWN) + state.down = true; + if (pressed & BTN_A) + state.a = true; + if (pressed & BTN_B) + state.b = true; + if (pressed & BTN_SELECT) + state.select = true; + if (pressed & BTN_START) + state.start = true; + return state; +} + +std::uint32_t EspClock::millis_impl() { + TickType_t ticks = xTaskGetTickCount(); + return static_cast((static_cast(ticks) * 1000ULL) / configTICK_RATE_HZ); +} + +void EspClock::sleep_ms_impl(std::uint32_t ms) { + if (ms == 0) + return; + PowerHelper::get().delay(static_cast(ms), static_cast(ms)); +} + +} // namespace cardboy::backend::esp diff --git a/Firmware/main/src/fs_helper.cpp b/Firmware/components/backend-esp/src/fs_helper.cpp similarity index 97% rename from Firmware/main/src/fs_helper.cpp rename to Firmware/components/backend-esp/src/fs_helper.cpp index 2d79ad1..75f739a 100644 --- a/Firmware/main/src/fs_helper.cpp +++ b/Firmware/components/backend-esp/src/fs_helper.cpp @@ -1,4 +1,4 @@ -#include "fs_helper.hpp" +#include "cardboy/backend/esp/fs_helper.hpp" #include #include diff --git a/Firmware/main/src/i2c_global.cpp b/Firmware/components/backend-esp/src/i2c_global.cpp similarity index 87% rename from Firmware/main/src/i2c_global.cpp rename to Firmware/components/backend-esp/src/i2c_global.cpp index df0816b..9e30468 100644 --- a/Firmware/main/src/i2c_global.cpp +++ b/Firmware/components/backend-esp/src/i2c_global.cpp @@ -2,7 +2,7 @@ // Created by Stepan Usatiuk on 02.03.2025. // -#include "i2c_global.hpp" +#include "cardboy/backend/esp/i2c_global.hpp" I2cGlobal& I2cGlobal::get() { diff --git a/Firmware/main/src/power_helper.cpp b/Firmware/components/backend-esp/src/power_helper.cpp similarity index 94% rename from Firmware/main/src/power_helper.cpp rename to Firmware/components/backend-esp/src/power_helper.cpp index 00afc38..106d4c3 100644 --- a/Firmware/main/src/power_helper.cpp +++ b/Firmware/components/backend-esp/src/power_helper.cpp @@ -2,11 +2,7 @@ // Created by Stepan Usatiuk on 03.03.2025. // -#include "power_helper.hpp" - -#include -#include -#include +#include "cardboy/backend/esp/power_helper.hpp" #include "freertos/FreeRTOS.h" #include "freertos/task.h" diff --git a/Firmware/main/src/shutdowner.cpp b/Firmware/components/backend-esp/src/shutdowner.cpp similarity index 93% rename from Firmware/main/src/shutdowner.cpp rename to Firmware/components/backend-esp/src/shutdowner.cpp index fd4b176..7e1b8a2 100644 --- a/Firmware/main/src/shutdowner.cpp +++ b/Firmware/components/backend-esp/src/shutdowner.cpp @@ -2,12 +2,12 @@ // Created by Stepan Usatiuk on 02.03.2025. // -#include "shutdowner.hpp" +#include "cardboy/backend/esp/shutdowner.hpp" #include #include -#include "config.hpp" +#include "cardboy/backend/esp/config.hpp" Shutdowner& Shutdowner::get() { static Shutdowner instance; diff --git a/Firmware/main/src/spi_global.cpp b/Firmware/components/backend-esp/src/spi_global.cpp similarity index 83% rename from Firmware/main/src/spi_global.cpp rename to Firmware/components/backend-esp/src/spi_global.cpp index fddc4aa..8f2e769 100644 --- a/Firmware/main/src/spi_global.cpp +++ b/Firmware/components/backend-esp/src/spi_global.cpp @@ -2,7 +2,7 @@ // Created by Stepan Usatiuk on 02.03.2025. // -#include "spi_global.hpp" +#include "cardboy/backend/esp/spi_global.hpp" SpiGlobal& SpiGlobal::get() { static SpiGlobal SpiGlobal; diff --git a/Firmware/components/sdk-esp/CMakeLists.txt b/Firmware/components/sdk-esp/CMakeLists.txt index 583ee97..02bf196 100644 --- a/Firmware/components/sdk-esp/CMakeLists.txt +++ b/Firmware/components/sdk-esp/CMakeLists.txt @@ -1,21 +1,21 @@ idf_component_register( - PRIV_REQUIRES driver esp_timer esp_driver_spi + INCLUDE_DIRS "" + REQUIRES backend-esp ) -add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/../../sdk" cb-sdk-build) +set(CARDBOY_BUILD_SFML OFF CACHE BOOL "Disable desktop backend build" FORCE) +set(CARDBOY_SDK_BACKEND_LIBRARY cardboy_backend_esp CACHE STRING "Cardboy backend implementation" FORCE) -target_include_directories(cardboy_backend - INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/../../main/include -) +add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/../../sdk/core" cb-sdk-build-core) +add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/../../sdk/apps" cb-sdk-build-apps) -target_compile_options(cardboy_backend +target_compile_options(cardboy_backend_esp INTERFACE -fjump-tables -ftree-switch-conversion ) -target_link_libraries(cardboy_backend +target_link_libraries(cardboy_backend_esp INTERFACE idf::driver idf::esp_timer @@ -25,7 +25,7 @@ target_link_libraries(cardboy_backend target_link_libraries(${COMPONENT_LIB} INTERFACE - cardboy_backend + cardboy_backend_esp cardboy_sdk cardboy_apps ) diff --git a/Firmware/main/CMakeLists.txt b/Firmware/main/CMakeLists.txt index aa60050..8603258 100644 --- a/Firmware/main/CMakeLists.txt +++ b/Firmware/main/CMakeLists.txt @@ -1,16 +1,16 @@ -idf_component_register(SRCS - src/app_main.cpp - src/display.cpp - src/bat_mon.cpp - src/spi_global.cpp - src/i2c_global.cpp - src/shutdowner.cpp - src/buttons.cpp - src/power_helper.cpp - src/buzzer.cpp - src/fs_helper.cpp - PRIV_REQUIRES spi_flash esp_driver_i2c driver sdk-esp esp_timer nvs_flash littlefs - EMBED_FILES "roms/builtin_demo1.gb" "roms/builtin_demo2.gb" - INCLUDE_DIRS "include" "../sdk/include") +idf_component_register( + SRCS + "src/app_main.cpp" + PRIV_REQUIRES + sdk-esp + littlefs + REQUIRES + backend-esp + EMBED_FILES + "roms/builtin_demo1.gb" + "roms/builtin_demo2.gb" + INCLUDE_DIRS + "" +) littlefs_create_partition_image(littlefs ../flash_data FLASH_IN_PROJECT) diff --git a/Firmware/main/include/app_framework.hpp b/Firmware/main/include/app_framework.hpp deleted file mode 100644 index 5c0d1ac..0000000 --- a/Firmware/main/include/app_framework.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "cardboy/sdk/app_framework.hpp" -#include "cardboy/sdk/platform.hpp" - -using AppTimerHandle = cardboy::sdk::AppTimerHandle; -constexpr AppTimerHandle kInvalidAppTimer = cardboy::sdk::kInvalidAppTimer; - -using AppEventType = cardboy::sdk::AppEventType; -using AppButtonEvent = cardboy::sdk::AppButtonEvent; -using AppTimerEvent = cardboy::sdk::AppTimerEvent; -using AppEvent = cardboy::sdk::AppEvent; - -using AppContext = cardboy::sdk::AppContext; - -using IApp = cardboy::sdk::IApp; -using IAppFactory = cardboy::sdk::IAppFactory; -using Services = cardboy::sdk::Services; -using IBuzzer = cardboy::sdk::IBuzzer; -using IBatteryMonitor = cardboy::sdk::IBatteryMonitor; -using IStorage = cardboy::sdk::IStorage; -using IRandom = cardboy::sdk::IRandom; -using IHighResClock = cardboy::sdk::IHighResClock; -using IPowerManager = cardboy::sdk::IPowerManager; -using IFilesystem = cardboy::sdk::IFilesystem; diff --git a/Firmware/main/include/app_platform.hpp b/Firmware/main/include/app_platform.hpp deleted file mode 100644 index 8b1bd78..0000000 --- a/Firmware/main/include/app_platform.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "cardboy/sdk/display_spec.hpp" -#include "cardboy/sdk/platform.hpp" -#include "config.hpp" - -#include -#include -#include - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" - -class PlatformFramebuffer final : public cardboy::sdk::FramebufferFacade { -public: - [[nodiscard]] int width_impl() const { return cardboy::sdk::kDisplayWidth; } - [[nodiscard]] int height_impl() const { return cardboy::sdk::kDisplayHeight; } - - __attribute__((always_inline)) void drawPixel_impl(int x, int y, bool on) { - if (x < 0 || y < 0 || x >= width() || y >= height()) - return; - SMD::set_pixel(x, y, on); - } - - void clear_impl(bool on) { - for (int y = 0; y < height(); ++y) - for (int x = 0; x < width(); ++x) - SMD::set_pixel(x, y, on); - } - - __attribute__((always_inline)) void frameReady_impl() { SMD::frame_ready(); } - __attribute__((always_inline)) void sendFrame_impl(bool clear) { SMD::send_frame(clear); } - __attribute__((always_inline)) [[nodiscard]] bool frameInFlight_impl() const { return SMD::frame_transfer_in_flight(); } -}; - -class PlatformInput final : public cardboy::sdk::InputFacade { -public: - cardboy::sdk::InputState readState_impl() { - cardboy::sdk::InputState state{}; - const uint8_t pressed = Buttons::get().get_pressed(); - if (pressed & BTN_UP) - state.up = true; - if (pressed & BTN_LEFT) - state.left = true; - if (pressed & BTN_RIGHT) - state.right = true; - if (pressed & BTN_DOWN) - state.down = true; - if (pressed & BTN_A) - state.a = true; - if (pressed & BTN_B) - state.b = true; - if (pressed & BTN_SELECT) - state.select = true; - if (pressed & BTN_START) - state.start = true; - return state; - } -}; - -class PlatformClock final : public cardboy::sdk::ClockFacade { -public: - std::uint32_t millis_impl() { - TickType_t ticks = xTaskGetTickCount(); - return static_cast((static_cast(ticks) * 1000ULL) / configTICK_RATE_HZ); - } - - void sleep_ms_impl(std::uint32_t ms) { - if (ms == 0) - return; - PowerHelper::get().delay(static_cast(ms), static_cast(ms)); - } -}; diff --git a/Firmware/main/include/app_system.hpp b/Firmware/main/include/app_system.hpp deleted file mode 100644 index 94bbcf9..0000000 --- a/Firmware/main/include/app_system.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "cardboy/sdk/app_system.hpp" - -using AppSystem = cardboy::sdk::AppSystem; diff --git a/Firmware/main/include/apps/clock_app.hpp b/Firmware/main/include/apps/clock_app.hpp deleted file mode 100644 index eb23da4..0000000 --- a/Firmware/main/include/apps/clock_app.hpp +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -#include "cardboy/apps/clock_app.hpp" - diff --git a/Firmware/main/include/apps/gameboy_app.hpp b/Firmware/main/include/apps/gameboy_app.hpp deleted file mode 100644 index a4c3656..0000000 --- a/Firmware/main/include/apps/gameboy_app.hpp +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include "cardboy/apps/gameboy_app.hpp" diff --git a/Firmware/main/include/apps/menu_app.hpp b/Firmware/main/include/apps/menu_app.hpp deleted file mode 100644 index 702a0e3..0000000 --- a/Firmware/main/include/apps/menu_app.hpp +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include "cardboy/apps/menu_app.hpp" diff --git a/Firmware/main/include/apps/tetris_app.hpp b/Firmware/main/include/apps/tetris_app.hpp deleted file mode 100644 index bcd1be1..0000000 --- a/Firmware/main/include/apps/tetris_app.hpp +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include "cardboy/apps/tetris_app.hpp" diff --git a/Firmware/main/include/cardboy/backend/esp_backend.hpp b/Firmware/main/include/cardboy/backend/esp_backend.hpp deleted file mode 100644 index 5a43107..0000000 --- a/Firmware/main/include/cardboy/backend/esp_backend.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "app_platform.hpp" -#include "cardboy/sdk/platform.hpp" - -namespace cardboy::backend { - -struct EspBackend { - using Framebuffer = PlatformFramebuffer; - using Input = PlatformInput; - using Clock = PlatformClock; -}; - -} // namespace cardboy::backend diff --git a/Firmware/main/include/input_state.hpp b/Firmware/main/include/input_state.hpp deleted file mode 100644 index c6c8c09..0000000 --- a/Firmware/main/include/input_state.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "cardboy/sdk/input_state.hpp" - -using InputState = cardboy::sdk::InputState; diff --git a/Firmware/main/src/app_main.cpp b/Firmware/main/src/app_main.cpp index 5d1bd62..d301005 100644 --- a/Firmware/main/src/app_main.cpp +++ b/Firmware/main/src/app_main.cpp @@ -1,36 +1,15 @@ // Cardboy firmware entry point: boot platform services and run the modular app system. -#include "app_system.hpp" - -#include "app_framework.hpp" -#include "app_platform.hpp" -#include "apps/clock_app.hpp" -#include "apps/gameboy_app.hpp" -#include "apps/menu_app.hpp" -#include "apps/tetris_app.hpp" -#include "config.hpp" -#include "cardboy/sdk/services.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include "cardboy/backend/esp_backend.hpp" +#include "cardboy/apps/clock_app.hpp" +#include "cardboy/apps/gameboy_app.hpp" +#include "cardboy/apps/menu_app.hpp" +#include "cardboy/apps/tetris_app.hpp" +#include "cardboy/sdk/app_system.hpp" #include "freertos/FreeRTOS.h" #include "freertos/task.h" - -#include "driver/gpio.h" #include "esp_err.h" -#include "esp_random.h" -#include "esp_timer.h" #include "esp_pm.h" #include "esp_sleep.h" #include "sdkconfig.h" @@ -69,87 +48,6 @@ constexpr apps::EmbeddedRomDescriptor kEmbeddedRoms[] = { }, }; -class EspBuzzer final : public cardboy::sdk::IBuzzer { -public: - void tone(std::uint32_t freq, std::uint32_t duration_ms, std::uint32_t gap_ms = 0) override { - Buzzer::get().tone(freq, duration_ms, gap_ms); - } - - void beepRotate() override { Buzzer::get().beepRotate(); } - void beepMove() override { Buzzer::get().beepMove(); } - void beepLock() override { Buzzer::get().beepLock(); } - void beepLines(int lines) override { Buzzer::get().beepLines(lines); } - void beepLevelUp(int level) override { Buzzer::get().beepLevelUp(level); } - void beepGameOver() override { Buzzer::get().beepGameOver(); } - - void setMuted(bool muted) override { Buzzer::get().setMuted(muted); } - void toggleMuted() override { Buzzer::get().toggleMuted(); } - [[nodiscard]] bool isMuted() const override { return Buzzer::get().isMuted(); } -}; - -class EspBatteryMonitor final : public cardboy::sdk::IBatteryMonitor { -public: - [[nodiscard]] bool hasData() const override { return true; } - [[nodiscard]] float voltage() const override { return BatMon::get().get_voltage(); } - [[nodiscard]] float charge() const override { return BatMon::get().get_charge(); } - [[nodiscard]] float current() const override { return BatMon::get().get_current(); } -}; - -class EspStorage final : public cardboy::sdk::IStorage { -public: - [[nodiscard]] bool readUint32(std::string_view ns, std::string_view key, std::uint32_t& out) override { - nvs_handle_t handle; - std::string nsStr(ns); - std::string keyStr(key); - if (nvs_open(nsStr.c_str(), NVS_READONLY, &handle) != ESP_OK) - return false; - std::uint32_t value = 0; - esp_err_t err = nvs_get_u32(handle, keyStr.c_str(), &value); - nvs_close(handle); - if (err != ESP_OK) - return false; - out = value; - return true; - } - - void writeUint32(std::string_view ns, std::string_view key, std::uint32_t value) override { - nvs_handle_t handle; - std::string nsStr(ns); - std::string keyStr(key); - if (nvs_open(nsStr.c_str(), NVS_READWRITE, &handle) != ESP_OK) - return; - nvs_set_u32(handle, keyStr.c_str(), value); - nvs_commit(handle); - nvs_close(handle); - } -}; - -class EspRandom final : public cardboy::sdk::IRandom { -public: - [[nodiscard]] std::uint32_t nextUint32() override { return esp_random(); } -}; - -class EspHighResClock final : public cardboy::sdk::IHighResClock { -public: - [[nodiscard]] std::uint64_t micros() override { return static_cast(esp_timer_get_time()); } -}; - -class EspPowerManager final : public cardboy::sdk::IPowerManager { -public: - void setSlowMode(bool enable) override { PowerHelper::get().set_slow(enable); } - [[nodiscard]] bool isSlowMode() const override { return PowerHelper::get().is_slow(); } -}; - -class EspFilesystem final : public cardboy::sdk::IFilesystem { -public: - bool mount() override { return FsHelper::get().mount() == ESP_OK; } - [[nodiscard]] bool isMounted() const override { return FsHelper::get().isMounted(); } - [[nodiscard]] std::string basePath() const override { - const char* path = FsHelper::get().basePath(); - return path ? std::string(path) : std::string{}; - } -}; - } // namespace #if CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS && CONFIG_FREERTOS_USE_TRACE_FACILITY @@ -308,49 +206,13 @@ extern "C" void app_main() { ESP_ERROR_CHECK(esp_sleep_enable_gpio_wakeup()); #endif - PowerHelper::get(); - Shutdowner::get(); - Buttons::get(); - - ESP_ERROR_CHECK(gpio_install_isr_service(0)); - Shutdowner::get().install_isr(); - PowerHelper::get().install_isr(); - Buttons::get().install_isr(); - - I2cGlobal::get(); - BatMon::get(); - SpiGlobal::get(); - SMD::init(); - Buzzer::get().init(); - - FsHelper::get().mount(); - apps::setGameboyEmbeddedRoms(std::span(kEmbeddedRoms)); - static PlatformFramebuffer framebuffer; - static PlatformInput input; - static PlatformClock clock; + static cardboy::backend::esp::EspRuntime runtime; - static EspBuzzer buzzerService; - static EspBatteryMonitor batteryService; - static EspStorage storageService; - static EspRandom randomService; - static EspHighResClock highResClockService; - static EspPowerManager powerService; - static EspFilesystem filesystemService; - - static cardboy::sdk::Services services{}; - services.buzzer = &buzzerService; - services.battery = &batteryService; - services.storage = &storageService; - services.random = &randomService; - services.highResClock = &highResClockService; - services.powerManager = &powerService; - services.filesystem = &filesystemService; - - AppContext context(framebuffer, input, clock); - context.services = &services; - AppSystem system(context); + cardboy::sdk::AppContext context(runtime.framebuffer, runtime.input, runtime.clock); + context.services = &runtime.serviceRegistry(); + cardboy::sdk::AppSystem system(context); context.system = &system; system.registerApp(apps::createMenuAppFactory());