mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 15:17:48 +01:00
some refactoring 2
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
|
||||
@@ -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, ®, 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; }
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user