some refactoring

This commit is contained in:
2025-10-11 16:44:48 +02:00
parent e9a05259c5
commit f721ebcb4c
35 changed files with 413 additions and 331 deletions

View File

@@ -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
)

View File

@@ -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 <cstdint>
class BatMon {
public:

View File

@@ -7,6 +7,7 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <cstdint>
typedef enum {
BTN_START = 1 << 1,

View File

@@ -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 <array>
#include <bitset>
#include <cassert>
namespace SMD {

View File

@@ -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"

View File

@@ -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"

View File

@@ -0,0 +1,80 @@
#pragma once
#include "cardboy/sdk/platform.hpp"
#include "cardboy/sdk/services.hpp"
#include <cstdint>
#include <memory>
namespace cardboy::backend::esp {
class EspRuntime;
class EspFramebuffer final : public cardboy::sdk::FramebufferFacade<EspFramebuffer> {
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<EspInput> {
public:
cardboy::sdk::InputState readState_impl();
};
class EspClock final : public cardboy::sdk::ClockFacade<EspClock> {
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> buzzerService;
std::unique_ptr<BatteryService> batteryService;
std::unique_ptr<StorageService> storageService;
std::unique_ptr<RandomService> randomService;
std::unique_ptr<HighResClockService> highResClockService;
std::unique_ptr<PowerService> powerService;
std::unique_ptr<FilesystemService> 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

View File

@@ -2,15 +2,15 @@
// Created by Stepan Usatiuk on 02.03.2025.
//
#include "bat_mon.hpp"
#include "cardboy/backend/esp/bat_mon.hpp"
#include <power_helper.hpp>
#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;

View File

@@ -2,18 +2,18 @@
// Created by Stepan Usatiuk on 02.03.2025.
//
#include "buttons.hpp"
#include "cardboy/backend/esp/buttons.hpp"
#include <driver/gpio.h>
#include <esp_err.h>
#include <power_helper.hpp>
#include "cardboy/backend/esp/power_helper.hpp"
#include <rom/ets_sys.h>
#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 = {

View File

@@ -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 <driver/ledc.h>
#include <esp_err.h>

View File

@@ -1,6 +1,6 @@
// Double-buffered display implementation with async memcpy ---------------------------------
#include "display.hpp"
#include "cardboy/backend/esp/display.hpp"
#include <cassert>
#include <cstring>
#include <driver/gpio.h>

View File

@@ -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 <algorithm>
#include <cstdint>
#include <string>
#include <string_view>
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<std::uint64_t>(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<BuzzerService>();
batteryService = std::make_unique<BatteryService>();
storageService = std::make_unique<StorageService>();
randomService = std::make_unique<RandomService>();
highResClockService = std::make_unique<HighResClockService>();
powerService = std::make_unique<PowerService>();
filesystemService = std::make_unique<FilesystemService>();
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<std::uint32_t>((static_cast<std::uint64_t>(ticks) * 1000ULL) / configTICK_RATE_HZ);
}
void EspClock::sleep_ms_impl(std::uint32_t ms) {
if (ms == 0)
return;
PowerHelper::get().delay(static_cast<int>(ms), static_cast<int>(ms));
}
} // namespace cardboy::backend::esp

View File

@@ -1,4 +1,4 @@
#include "fs_helper.hpp"
#include "cardboy/backend/esp/fs_helper.hpp"
#include <esp_idf_version.h>
#include <esp_littlefs.h>

View File

@@ -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() {

View File

@@ -2,11 +2,7 @@
// Created by Stepan Usatiuk on 03.03.2025.
//
#include "power_helper.hpp"
#include <config.hpp>
#include <driver/gpio.h>
#include <esp_sleep.h>
#include "cardboy/backend/esp/power_helper.hpp"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

View File

@@ -2,12 +2,12 @@
// Created by Stepan Usatiuk on 02.03.2025.
//
#include "shutdowner.hpp"
#include "cardboy/backend/esp/shutdowner.hpp"
#include <driver/gpio.h>
#include <esp_sleep.h>
#include "config.hpp"
#include "cardboy/backend/esp/config.hpp"
Shutdowner& Shutdowner::get() {
static Shutdowner instance;

View File

@@ -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;

View File

@@ -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
)

View File

@@ -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)

View File

@@ -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;

View File

@@ -1,73 +0,0 @@
#pragma once
#include "cardboy/sdk/display_spec.hpp"
#include "cardboy/sdk/platform.hpp"
#include "config.hpp"
#include <buttons.hpp>
#include <display.hpp>
#include <power_helper.hpp>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
class PlatformFramebuffer final : public cardboy::sdk::FramebufferFacade<PlatformFramebuffer> {
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<PlatformInput> {
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<PlatformClock> {
public:
std::uint32_t millis_impl() {
TickType_t ticks = xTaskGetTickCount();
return static_cast<std::uint32_t>((static_cast<std::uint64_t>(ticks) * 1000ULL) / configTICK_RATE_HZ);
}
void sleep_ms_impl(std::uint32_t ms) {
if (ms == 0)
return;
PowerHelper::get().delay(static_cast<int>(ms), static_cast<int>(ms));
}
};

View File

@@ -1,5 +0,0 @@
#pragma once
#include "cardboy/sdk/app_system.hpp"
using AppSystem = cardboy::sdk::AppSystem;

View File

@@ -1,4 +0,0 @@
#pragma once
#include "cardboy/apps/clock_app.hpp"

View File

@@ -1,3 +0,0 @@
#pragma once
#include "cardboy/apps/gameboy_app.hpp"

View File

@@ -1,3 +0,0 @@
#pragma once
#include "cardboy/apps/menu_app.hpp"

View File

@@ -1,3 +0,0 @@
#pragma once
#include "cardboy/apps/tetris_app.hpp"

View File

@@ -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

View File

@@ -1,5 +0,0 @@
#pragma once
#include "cardboy/sdk/input_state.hpp"
using InputState = cardboy::sdk::InputState;

View File

@@ -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 <bat_mon.hpp>
#include <buttons.hpp>
#include <buzzer.hpp>
#include <display.hpp>
#include <fs_helper.hpp>
#include <i2c_global.hpp>
#include <nvs.h>
#include <nvs_flash.h>
#include <power_helper.hpp>
#include <shutdowner.hpp>
#include <spi_global.hpp>
#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<std::uint64_t>(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<const apps::EmbeddedRomDescriptor>(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());