some refactoring 2

This commit is contained in:
2025-10-25 12:34:53 +02:00
parent 1ee132898b
commit f8735d4bce
32 changed files with 816 additions and 818 deletions

View File

@@ -4,7 +4,6 @@ idf_component_register(
"src/buttons.cpp"
"src/buzzer.cpp"
"src/esp_backend.cpp"
"src/event_bus.cpp"
"src/display.cpp"
"src/fs_helper.cpp"
"src/i2c_global.cpp"

View File

@@ -5,9 +5,10 @@
#ifndef BUTTONS_HPP
#define BUTTONS_HPP
#include "cardboy/sdk/event_bus.hpp"
#include <cstdint>
#include "cardboy/sdk/input_state.hpp"
#include "cardboy/sdk/services.hpp"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
@@ -24,20 +25,19 @@ typedef enum {
class Buttons {
public:
static Buttons& get();
void pooler(); // FIXME:
uint8_t get_pressed();
void install_isr();
void register_listener(TaskHandle_t task);
void setEventBus(cardboy::sdk::IEventBus* bus);
static Buttons& get();
void pooler(); // FIXME:
uint8_t get_pressed();
cardboy::sdk::InputState get_state();
void install_isr();
void setEventBus(cardboy::sdk::IEventBus* bus);
TaskHandle_t _pooler_task;
private:
Buttons();
uint8_t _previous;
volatile uint8_t _current;
volatile TaskHandle_t _listener = nullptr;
cardboy::sdk::IEventBus* _eventBus = nullptr;
};

View File

@@ -1,27 +0,0 @@
#pragma once
#include <cardboy/sdk/event_bus.hpp>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/queue.h"
namespace cardboy::backend::esp {
class EventBus final : public cardboy::sdk::IEventBus {
public:
EventBus();
~EventBus() override;
void signal(std::uint32_t bits) override;
void signalFromISR(std::uint32_t bits) override;
std::uint32_t wait(std::uint32_t mask, std::uint32_t timeout_ms) override;
void postEvent(const cardboy::sdk::AppEvent& event) override;
bool popEvent(cardboy::sdk::AppEvent& outEvent) override;
private:
EventGroupHandle_t group;
QueueHandle_t eventQueue;
};
} // namespace cardboy::backend::esp

View File

@@ -2,7 +2,6 @@
#include <cardboy/sdk/display_spec.hpp>
#include "cardboy/backend/esp/display.hpp"
#include "cardboy/backend/esp/event_bus.hpp"
#include "cardboy/sdk/platform.hpp"
#include "cardboy/sdk/services.hpp"
@@ -63,19 +62,22 @@ private:
class LoopHooksService;
class NotificationService;
class TimerService;
class EventBus;
class AppScopedServices;
class AppServiceProvider;
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<FilesystemService> filesystemService;
std::unique_ptr<EventBus> eventBus;
std::unique_ptr<TimerService> timerService;
std::unique_ptr<LoopHooksService> loopHooksService;
std::unique_ptr<NotificationService> notificationService;
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<FilesystemService> _filesystemService;
std::unique_ptr<EventBus> _eventBus;
std::unique_ptr<LoopHooksService> _loopHooksService;
std::unique_ptr<NotificationService> _notificationService;
std::unique_ptr<AppServiceProvider> _appServiceProvider;
cardboy::sdk::Services services{};
cardboy::sdk::Services _services{};
};
struct Backend {

View File

@@ -13,7 +13,6 @@
#include "cardboy/backend/esp/config.hpp"
#include "cardboy/backend/esp/i2c_global.hpp"
#include "cardboy/sdk/event_bus.hpp"
static i2c_master_dev_handle_t dev_handle;
static inline i2c_device_config_t dev_cfg = {
@@ -76,6 +75,28 @@ static void delay(unsigned long long loop) {
}
}
static cardboy::sdk::InputState buttons_to_input_state(uint8_t pressed) {
cardboy::sdk::InputState state{};
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;
}
void Buttons::pooler() {
while (true) {
BaseType_t xResult = xTaskNotifyWait(pdFALSE, ULONG_MAX, nullptr, portMAX_DELAY);
@@ -88,15 +109,27 @@ void Buttons::pooler() {
reg = 1;
ESP_ERROR_CHECK(
i2c_master_transmit_receive(dev_handle, &reg, sizeof(reg), reinterpret_cast<uint8_t*>(&buffer), 1, -1));
if (_listener)
xTaskNotifyGive(_listener);
if (_eventBus)
_eventBus->signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Input));
if (_eventBus) {
cardboy::sdk::AppButtonEvent button{};
button.current = buttons_to_input_state(_current);
button.previous = buttons_to_input_state(_previous);
_previous = _current;
cardboy::sdk::AppEvent evt{};
// TODO: dedup?
TickType_t ticks = xTaskGetTickCount();
auto now = static_cast<std::uint32_t>((static_cast<std::uint64_t>(ticks) * 1000ULL) / configTICK_RATE_HZ);
evt.timestamp_ms = now;
evt.data = button;
_eventBus->post(evt);
}
}
}
uint8_t Buttons::get_pressed() { return _current; }
void Buttons::install_isr() { gpio_isr_handler_add(EXP_INT, wakeup, nullptr); }
void Buttons::register_listener(TaskHandle_t task) { _listener = task; }
void Buttons::setEventBus(cardboy::sdk::IEventBus* bus) { _eventBus = bus; }
uint8_t Buttons::get_pressed() { return _current; }
void Buttons::install_isr() { gpio_isr_handler_add(EXP_INT, wakeup, nullptr); }
cardboy::sdk::InputState Buttons::get_state() { return buttons_to_input_state(get_pressed()); }
void Buttons::setEventBus(cardboy::sdk::IEventBus* bus) { _eventBus = bus; }

View File

@@ -18,15 +18,17 @@
#include "esp_random.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "nvs.h"
#include "nvs_flash.h"
#include <algorithm>
#include <atomic>
#include <cstdint>
#include <ctime>
#include <deque>
#include <list>
#include <mutex>
#include <string>
@@ -134,61 +136,96 @@ public:
void onLoopIteration() override { vTaskDelay(1); }
};
class EspRuntime::TimerService final : public cardboy::sdk::ITimerService {
class EspRuntime::EventBus final : public cardboy::sdk::IEventBus {
public:
explicit TimerService(cardboy::sdk::IEventBus& bus);
~TimerService() override;
explicit EventBus() {
_queueHandle =
xQueueCreateStatic(_kMaxQueueSize, sizeof(cardboy::sdk::AppEvent), _queueStorage.data(), &_queue);
}
cardboy::sdk::TimerClientId acquireClient() override;
void releaseClient(cardboy::sdk::TimerClientId clientId) override;
cardboy::sdk::AppTimerHandle scheduleTimer(cardboy::sdk::TimerClientId clientId, std::uint32_t delay_ms,
bool repeat) override;
void cancelTimer(cardboy::sdk::TimerClientId clientId, cardboy::sdk::AppTimerHandle handle) override;
void cancelAllTimers(cardboy::sdk::TimerClientId clientId) override;
~EventBus() override { vQueueDelete(_queueHandle); }
void post(const sdk::AppEvent& event) override { xQueueSendToBack(_queueHandle, &event, portMAX_DELAY); }
sdk::AppEvent pop() override {
sdk::AppEvent out;
xQueueReceive(_queueHandle, &out, portMAX_DELAY);
return out;
}
private:
struct TimerRecord {
TimerService* owner = nullptr;
TimerHandle_t timer = nullptr;
cardboy::sdk::TimerClientId clientId = cardboy::sdk::kInvalidTimerClient;
cardboy::sdk::AppTimerHandle handle = cardboy::sdk::kInvalidAppTimer;
bool repeat = false;
bool active = true;
};
static constexpr std::uint32_t _kMaxQueueSize = 32;
StaticQueue_t _queue;
std::array<std::uint8_t, 32 * sizeof(cardboy::sdk::AppEvent)> _queueStorage{};
QueueHandle_t _queueHandle;
};
class RecursiveLock {
public:
explicit RecursiveLock(SemaphoreHandle_t m) : mutex(m) { lock(); }
~RecursiveLock() { unlock(); }
void lock() {
if (mutex && !locked) {
if (xSemaphoreTakeRecursive(mutex, portMAX_DELAY) == pdTRUE)
locked = true;
}
class EspRuntime::TimerService final : public cardboy::sdk::ITimerService {
public:
explicit TimerService(cardboy::sdk::IEventBus& appBus);
~TimerService() override;
cardboy::sdk::AppTimerHandle scheduleTimer(std::uint32_t delay_ms, bool repeat) override;
void cancelTimer(cardboy::sdk::AppTimerHandle handle) override;
void cancelAllTimers() override;
private:
static inline EspRuntime::TimerService* _current = nullptr;
struct InitializedSemaphore {
InitializedSemaphore() {
_handle = xSemaphoreCreateBinary();
xSemaphoreGive(_handle);
}
~InitializedSemaphore() { vSemaphoreDelete(_handle); }
void unlock() {
if (mutex && locked) {
xSemaphoreGiveRecursive(mutex);
locked = false;
}
}
SemaphoreHandle_t operator*() { return _handle; }
SemaphoreHandle_t operator->() { return _handle; }
private:
SemaphoreHandle_t mutex;
bool locked = false;
SemaphoreHandle_t _handle;
};
static inline InitializedSemaphore _currentSemaphore;
struct TimerRecord {
TimerService* owner = nullptr;
TimerHandle_t timer = nullptr;
cardboy::sdk::AppTimerHandle handle = cardboy::sdk::kInvalidAppTimer;
bool repeat = false;
};
static void timerCallback(TimerHandle_t timer);
void handleTimer(TimerRecord* record);
void shutdown();
void handleTimer(sdk::AppTimerHandle record);
cardboy::sdk::IEventBus& eventBus;
SemaphoreHandle_t mutex = nullptr;
std::list<TimerRecord> timers;
cardboy::sdk::AppTimerHandle nextTimerHandle = 1;
cardboy::sdk::TimerClientId nextClientId = 1;
cardboy::sdk::IEventBus& _appEventBus;
SemaphoreHandle_t _mutex;
StaticSemaphore_t _mutexStatic;
std::list<TimerRecord> _timers;
std::atomic<sdk::AppTimerHandle> _nextTimerHandle = 1;
static_assert(std::atomic<sdk::AppTimerHandle>::is_always_lock_free);
};
class EspRuntime::AppScopedServices final : public cardboy::sdk::AppScopedServices {
public:
AppScopedServices(std::unique_ptr<TimerService> timer) : _ownedTimer(std::move(timer)) {
this->timer = _ownedTimer.get();
}
private:
std::unique_ptr<TimerService> _ownedTimer;
};
class EspRuntime::AppServiceProvider final : public cardboy::sdk::IAppServiceProvider {
public:
explicit AppServiceProvider(cardboy::sdk::IEventBus& bus) : eventBus(bus) {}
[[nodiscard]] std::unique_ptr<cardboy::sdk::AppScopedServices>
createScopedServices(std::uint64_t generation) override {
(void) generation;
auto timer = std::make_unique<TimerService>(eventBus);
return std::make_unique<AppScopedServices>(std::move(timer));
}
private:
cardboy::sdk::IEventBus& eventBus;
};
class EspRuntime::NotificationService final : public cardboy::sdk::INotificationCenter {
@@ -262,8 +299,8 @@ public:
bool removed = false;
for (auto it = entries.begin(); it != entries.end();) {
if (it->id == id) {
it = entries.erase(it);
removed = true;
it = entries.erase(it);
removed = true;
} else {
++it;
}
@@ -279,8 +316,8 @@ public:
bool removed = false;
for (auto it = entries.begin(); it != entries.end();) {
if (it->externalId == externalId) {
it = entries.erase(it);
removed = true;
it = entries.erase(it);
removed = true;
} else {
++it;
}
@@ -307,99 +344,69 @@ private:
std::uint32_t revisionCounter = 0;
};
EspRuntime::TimerService::TimerService(cardboy::sdk::IEventBus& bus) : eventBus(bus) {
mutex = xSemaphoreCreateRecursiveMutex();
EspRuntime::TimerService::TimerService(cardboy::sdk::IEventBus& appBus) : _appEventBus(appBus) {
xSemaphoreTake(*_currentSemaphore, portMAX_DELAY);
assert(_current == nullptr);
_mutex = xSemaphoreCreateBinaryStatic(&_mutexStatic);
assert(_mutex);
xSemaphoreGive(_mutex);
_current = this;
xSemaphoreGive(*_currentSemaphore);
}
EspRuntime::TimerService::~TimerService() {
shutdown();
if (mutex)
vSemaphoreDelete(mutex);
xSemaphoreTake(*_currentSemaphore, portMAX_DELAY);
assert(_current == this);
_current = nullptr;
cancelAllTimers();
vSemaphoreDelete(_mutex);
xSemaphoreGive(*_currentSemaphore);
}
cardboy::sdk::TimerClientId EspRuntime::TimerService::acquireClient() {
if (!mutex)
return cardboy::sdk::kInvalidTimerClient;
RecursiveLock lock(mutex);
cardboy::sdk::TimerClientId id = cardboy::sdk::kInvalidTimerClient;
cardboy::sdk::AppTimerHandle EspRuntime::TimerService::scheduleTimer(std::uint32_t delay_ms, bool repeat) {
TimerRecord record{};
record.owner = this;
record.repeat = repeat;
cardboy::sdk::AppTimerHandle newHandle = cardboy::sdk::kInvalidAppTimer;
do {
id = nextClientId++;
} while (id == cardboy::sdk::kInvalidTimerClient);
if (nextClientId == cardboy::sdk::kInvalidTimerClient)
++nextClientId;
return id;
}
newHandle = _nextTimerHandle++;
} while (newHandle == cardboy::sdk::kInvalidAppTimer);
if (_nextTimerHandle == cardboy::sdk::kInvalidAppTimer)
++_nextTimerHandle;
record.handle = newHandle;
xSemaphoreTake(_mutex, portMAX_DELAY);
TimerRecord* storedRecord = &_timers.emplace_back(record);
xSemaphoreGive(_mutex);
void EspRuntime::TimerService::releaseClient(cardboy::sdk::TimerClientId clientId) { cancelAllTimers(clientId); }
const TickType_t ticks = std::max<TickType_t>(pdMS_TO_TICKS(delay_ms), 1);
cardboy::sdk::AppTimerHandle EspRuntime::TimerService::scheduleTimer(cardboy::sdk::TimerClientId clientId,
std::uint32_t delay_ms, bool repeat) {
if (!mutex || clientId == cardboy::sdk::kInvalidTimerClient)
return cardboy::sdk::kInvalidAppTimer;
TimerRecord* storedRecord = nullptr;
{
RecursiveLock lock(mutex);
TimerRecord record{};
record.owner = this;
record.clientId = clientId;
record.repeat = repeat;
record.active = true;
cardboy::sdk::AppTimerHandle newHandle = cardboy::sdk::kInvalidAppTimer;
do {
newHandle = nextTimerHandle++;
} while (newHandle == cardboy::sdk::kInvalidAppTimer);
if (nextTimerHandle == cardboy::sdk::kInvalidAppTimer)
++nextTimerHandle;
record.handle = newHandle;
timers.emplace_back(record);
storedRecord = &timers.back();
}
const std::uint32_t effectiveDelay = std::max<std::uint32_t>(1, delay_ms);
const TickType_t ticks = std::max<TickType_t>(pdMS_TO_TICKS(effectiveDelay), 1);
static_assert(sizeof(void*) >= sizeof(cardboy::sdk::AppTimerHandle));
TimerHandle_t timerHandle =
xTimerCreate("AppSvcTimer", ticks, repeat ? pdTRUE : pdFALSE, storedRecord, &TimerService::timerCallback);
if (!timerHandle) {
RecursiveLock lock(mutex);
for (auto it = timers.begin(); it != timers.end(); ++it) {
if (&(*it) == storedRecord) {
timers.erase(it);
break;
}
}
return cardboy::sdk::kInvalidAppTimer;
}
xTimerCreate("AppSvcTimer", ticks, repeat ? pdTRUE : pdFALSE, reinterpret_cast<void*>(storedRecord->handle),
&TimerService::timerCallback);
storedRecord->timer = timerHandle;
{
RecursiveLock lock(mutex);
storedRecord->timer = timerHandle;
if (xTimerStart(timerHandle, portMAX_DELAY) != pdPASS) {
assert(false);
}
if (xTimerStart(timerHandle, 0) != pdPASS) {
cancelTimer(clientId, storedRecord->handle);
return cardboy::sdk::kInvalidAppTimer;
}
return storedRecord->handle;
}
void EspRuntime::TimerService::cancelTimer(cardboy::sdk::TimerClientId clientId,
cardboy::sdk::AppTimerHandle handle) {
if (!mutex || clientId == cardboy::sdk::kInvalidTimerClient || handle == cardboy::sdk::kInvalidAppTimer)
return;
void EspRuntime::TimerService::cancelTimer(cardboy::sdk::AppTimerHandle handle) {
assert(handle != sdk::kInvalidAppTimer);
TimerHandle_t timerHandle = nullptr;
{
RecursiveLock lock(mutex);
for (auto& record: timers) {
if (record.clientId == clientId && record.handle == handle) {
record.active = false;
timerHandle = record.timer;
break;
xSemaphoreTake(_mutex, portMAX_DELAY);
for (auto it = _timers.begin(); it != _timers.end(); ++it) {
if (it->handle == handle) {
timerHandle = it->timer;
it = _timers.erase(it);
}
}
xSemaphoreGive(_mutex);
}
if (!timerHandle)
@@ -407,143 +414,106 @@ void EspRuntime::TimerService::cancelTimer(cardboy::sdk::TimerClientId clientId,
xTimerStop(timerHandle, portMAX_DELAY);
xTimerDelete(timerHandle, portMAX_DELAY);
{
RecursiveLock lock(mutex);
for (auto it = timers.begin(); it != timers.end();) {
if (it->clientId == clientId && it->handle == handle)
it = timers.erase(it);
else
++it;
}
}
}
void EspRuntime::TimerService::cancelAllTimers(cardboy::sdk::TimerClientId clientId) {
if (!mutex || clientId == cardboy::sdk::kInvalidTimerClient)
void EspRuntime::TimerService::cancelAllTimers() {
if (!_mutex)
return;
std::vector<TimerHandle_t> handles;
handles.resize(_timers.size());
{
RecursiveLock lock(mutex);
for (auto& record: timers) {
if (record.clientId == clientId) {
record.active = false;
if (record.timer)
handles.push_back(record.timer);
xSemaphoreTake(_mutex, portMAX_DELAY);
size_t i = 0;
for (auto& record: _timers) {
if (record.timer) {
assert(record.timer);
handles[i] = record.timer;
}
i++;
}
_timers.clear();
xSemaphoreGive(_mutex);
}
for (auto timerHandle: handles) {
xTimerStop(timerHandle, portMAX_DELAY);
xTimerDelete(timerHandle, portMAX_DELAY);
}
{
RecursiveLock lock(mutex);
for (auto it = timers.begin(); it != timers.end();) {
if (it->clientId == clientId)
it = timers.erase(it);
else
++it;
}
}
}
void EspRuntime::TimerService::timerCallback(TimerHandle_t timer) {
auto* record = static_cast<TimerRecord*>(pvTimerGetTimerID(timer));
if (!record || !record->owner)
auto handle = reinterpret_cast<sdk::AppTimerHandle>(pvTimerGetTimerID(timer));
xSemaphoreTake(*_currentSemaphore, portMAX_DELAY);
if (!_current)
return;
record->owner->handleTimer(record);
_current->handleTimer(handle);
xSemaphoreGive(*_currentSemaphore);
}
void EspRuntime::TimerService::handleTimer(TimerRecord* record) {
if (!record || !record->active)
return;
void EspRuntime::TimerService::handleTimer(sdk::AppTimerHandle handle) {
TimerRecord* record = nullptr;
TimerHandle_t timerHandle = nullptr;
bool repeat = false;
const cardboy::sdk::TimerClientId clientId = record->clientId;
const cardboy::sdk::AppTimerHandle handle = record->handle;
const bool isRepeating = record->repeat;
TimerHandle_t timerHandle = record->timer;
if (!isRepeating) {
record->active = false;
{
RecursiveLock lock(mutex);
for (auto it = timers.begin(); it != timers.end();) {
if (&(*it) == record)
it = timers.erase(it);
else
++it;
{
xSemaphoreTake(_mutex, portMAX_DELAY);
for (auto it = _timers.begin(); it != _timers.end(); ++it) {
if (it->handle == handle) {
record = &(*it);
timerHandle = it->timer;
if (!it->repeat) {
_timers.erase(it);
} else {
repeat = true;
}
break;
}
}
if (timerHandle)
xTimerDelete(timerHandle, 0);
xSemaphoreGive(_mutex);
}
if (!record)
return;
if (!repeat && timerHandle)
xTimerDelete(timerHandle, portMAX_DELAY);
cardboy::sdk::AppTimerEvent timerEvent{};
timerEvent.clientId = clientId;
timerEvent.handle = handle;
timerEvent.handle = handle;
cardboy::sdk::AppEvent event{};
event.timestamp_ms = static_cast<std::uint32_t>(esp_timer_get_time() / 1000ULL);
event.data = timerEvent;
eventBus.postEvent(event);
}
void EspRuntime::TimerService::shutdown() {
if (!mutex)
return;
std::vector<TimerHandle_t> handles;
{
RecursiveLock lock(mutex);
for (auto& record: timers) {
record.active = false;
if (record.timer)
handles.push_back(record.timer);
}
}
for (auto timerHandle: handles) {
xTimerStop(timerHandle, portMAX_DELAY);
xTimerDelete(timerHandle, portMAX_DELAY);
}
{
RecursiveLock lock(mutex);
timers.clear();
}
_appEventBus.post(event);
}
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>();
filesystemService = std::make_unique<FilesystemService>();
eventBus = std::make_unique<EventBus>();
timerService = std::make_unique<TimerService>(*eventBus);
loopHooksService = std::make_unique<LoopHooksService>();
notificationService = std::make_unique<NotificationService>();
_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>();
_filesystemService = std::make_unique<FilesystemService>();
_eventBus = std::make_unique<EventBus>();
_appServiceProvider = std::make_unique<AppServiceProvider>(*_eventBus);
_loopHooksService = std::make_unique<LoopHooksService>();
_notificationService = std::make_unique<NotificationService>();
services.buzzer = buzzerService.get();
services.battery = batteryService.get();
services.storage = storageService.get();
services.random = randomService.get();
services.highResClock = highResClockService.get();
services.filesystem = filesystemService.get();
services.eventBus = eventBus.get();
services.timer = timerService.get();
services.loopHooks = loopHooksService.get();
services.notifications = notificationService.get();
_services.buzzer = _buzzerService.get();
_services.battery = _batteryService.get();
_services.storage = _storageService.get();
_services.random = _randomService.get();
_services.highResClock = _highResClockService.get();
_services.filesystem = _filesystemService.get();
_services.eventBus = _eventBus.get();
_services.appServices = _appServiceProvider.get();
_services.loopHooks = _loopHooksService.get();
_services.notifications = _notificationService.get();
Buttons::get().setEventBus(eventBus.get());
set_notification_center(notificationService.get());
Buttons::get().setEventBus(_eventBus.get());
set_notification_center(_notificationService.get());
}
EspRuntime::~EspRuntime() {
@@ -551,7 +521,7 @@ EspRuntime::~EspRuntime() {
shutdown_time_sync_service();
}
cardboy::sdk::Services& EspRuntime::serviceRegistry() { return services; }
cardboy::sdk::Services& EspRuntime::serviceRegistry() { return _services; }
void EspRuntime::initializeHardware() {
static bool initialized = false;
@@ -595,27 +565,7 @@ void EspFramebuffer::sendFrame_impl(bool clearAfterSend) { SMD::send_frame(clear
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;
}
cardboy::sdk::InputState EspInput::readState_impl() { return Buttons::get().get_state(); }
std::uint32_t EspClock::millis_impl() {
TickType_t ticks = xTaskGetTickCount();

View File

@@ -1,73 +0,0 @@
#include "cardboy/backend/esp/event_bus.hpp"
#include "cardboy/sdk/event_bus.hpp"
#include "freertos/portmacro.h"
#include <algorithm>
namespace cardboy::backend::esp {
namespace {
[[nodiscard]] TickType_t toTicks(std::uint32_t timeout_ms) {
if (timeout_ms == cardboy::sdk::IEventBus::kWaitForever)
return portMAX_DELAY;
return pdMS_TO_TICKS(timeout_ms);
}
constexpr UBaseType_t kEventQueueLength = 16;
} // namespace
EventBus::EventBus() :
group(xEventGroupCreate()),
eventQueue(xQueueCreate(kEventQueueLength, sizeof(cardboy::sdk::AppEvent))) {}
EventBus::~EventBus() {
if (group)
vEventGroupDelete(group);
if (eventQueue)
vQueueDelete(eventQueue);
}
void EventBus::signal(std::uint32_t bits) {
if (!group || bits == 0)
return;
xEventGroupSetBits(group, bits);
}
void EventBus::signalFromISR(std::uint32_t bits) {
if (!group || bits == 0)
return;
BaseType_t higherPriorityTaskWoken = pdFALSE;
xEventGroupSetBitsFromISR(group, bits, &higherPriorityTaskWoken);
if (higherPriorityTaskWoken == pdTRUE)
portYIELD_FROM_ISR(higherPriorityTaskWoken);
}
std::uint32_t EventBus::wait(std::uint32_t mask, std::uint32_t timeout_ms) {
if (!group || mask == 0)
return 0;
const EventBits_t bits = xEventGroupWaitBits(group, mask, pdTRUE, pdFALSE, toTicks(timeout_ms));
return static_cast<std::uint32_t>(bits & mask);
}
void EventBus::postEvent(const cardboy::sdk::AppEvent& event) {
if (!eventQueue)
return;
if (xQueueSend(eventQueue, &event, 0) == errQUEUE_FULL) {
cardboy::sdk::AppEvent discarded{};
if (xQueueReceive(eventQueue, &discarded, 0) == pdTRUE)
xQueueSend(eventQueue, &event, 0);
}
signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Timer));
}
bool EventBus::popEvent(cardboy::sdk::AppEvent& outEvent) {
if (!eventQueue)
return false;
return xQueueReceive(eventQueue, &outEvent, 0) == pdTRUE;
}
} // namespace cardboy::backend::esp