From 6fd588e970470b15936187980829916d0dbe77bb Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 1 Jun 2023 15:28:32 +1000 Subject: [PATCH] Add touchwheel -> encoder adapter --- src/app_console/app_console.cpp | 80 +++----------------------- src/drivers/CMakeLists.txt | 2 +- src/drivers/display.cpp | 32 +++++++---- src/drivers/include/relative_wheel.hpp | 44 ++++++++++++++ src/drivers/relative_wheel.cpp | 78 +++++++++++++++++++++++++ src/events/event_queue.cpp | 3 +- src/events/include/event_queue.hpp | 14 ++++- src/system_fsm/booting.cpp | 16 ++++-- src/system_fsm/include/system_fsm.hpp | 2 + src/system_fsm/system_fsm.cpp | 2 + src/tasks/tasks.cpp | 2 +- src/ui/CMakeLists.txt | 2 +- src/ui/include/lvgl_task.hpp | 3 +- src/ui/include/screen.hpp | 28 +++++++++ src/ui/include/screen_menu.hpp | 29 ++++++++++ src/ui/include/screen_splash.hpp | 30 ++++++++++ src/ui/include/ui_fsm.hpp | 10 ++-- src/ui/include/ui_tick.hpp | 11 ++++ src/ui/include/wheel_encoder.hpp | 30 ++++++++++ src/ui/lvgl_task.cpp | 16 +++++- src/ui/screen_menu.cpp | 52 +++++++++++++++++ src/ui/screen_splash.cpp | 38 ++++++++++++ src/ui/ui_fsm.cpp | 12 ++-- src/ui/wheel_encoder.cpp | 39 +++++++++++++ 24 files changed, 463 insertions(+), 112 deletions(-) create mode 100644 src/drivers/include/relative_wheel.hpp create mode 100644 src/drivers/relative_wheel.cpp create mode 100644 src/ui/include/screen.hpp create mode 100644 src/ui/include/screen_menu.hpp create mode 100644 src/ui/include/screen_splash.hpp create mode 100644 src/ui/include/ui_tick.hpp create mode 100644 src/ui/include/wheel_encoder.hpp create mode 100644 src/ui/screen_menu.cpp create mode 100644 src/ui/screen_splash.cpp create mode 100644 src/ui/wheel_encoder.cpp diff --git a/src/app_console/app_console.cpp b/src/app_console/app_console.cpp index 23e8875e..39d6d8e0 100644 --- a/src/app_console/app_console.cpp +++ b/src/app_console/app_console.cpp @@ -14,9 +14,12 @@ #include #include +#include "audio_events.hpp" +#include "audio_fsm.hpp" #include "database.hpp" #include "esp_console.h" #include "esp_log.h" +#include "event_queue.hpp" namespace console { @@ -66,7 +69,7 @@ void RegisterListDir() { esp_console_cmd_register(&cmd); } -/* + //sInstance->playback_->Play(path + argv[1]); int CmdPlayFile(int argc, char** argv) { static const std::string usage = "usage: play [file]"; if (argc != 2) { @@ -75,7 +78,9 @@ int CmdPlayFile(int argc, char** argv) { } std::string path = "/"; - sInstance->playback_->Play(path + argv[1]); + + events::Dispatch( + audio::PlayFile{ .filename = path + argv[1] }); return 0; } @@ -89,77 +94,6 @@ void RegisterPlayFile() { esp_console_cmd_register(&cmd); } -int CmdToggle(int argc, char** argv) { - static const std::string usage = "usage: toggle"; - if (argc != 1) { - std::cout << usage << std::endl; - return 1; - } - - // sInstance->playback_->Toggle(); - - return 0; -} - -void RegisterToggle() { - esp_console_cmd_t cmd{.command = "toggle", - .help = "Toggles between play and pause", - .hint = NULL, - .func = &CmdToggle, - .argtable = NULL}; - esp_console_cmd_register(&cmd); -} - -int CmdVolume(int argc, char** argv) { - static const std::string usage = "usage: volume [0-255]"; - if (argc != 2) { - std::cout << usage << std::endl; - return 1; - } - - long int raw_vol = strtol(argv[1], NULL, 10); - if (raw_vol < 0 || raw_vol > 255) { - std::cout << usage << std::endl; - return 1; - } - - // sInstance->playback_->SetVolume((uint8_t)raw_vol); - - return 0; -} - -void RegisterVolume() { - esp_console_cmd_t cmd{ - .command = "vol", - .help = "Changes the volume (between 0 and 254. 255 is mute.)", - .hint = NULL, - .func = &CmdVolume, - .argtable = NULL}; - esp_console_cmd_register(&cmd); -} - -int CmdAudioStatus(int argc, char** argv) { - static const std::string usage = "usage: audio"; - if (argc != 1) { - std::cout << usage << std::endl; - return 1; - } - - sInstance->playback_->LogStatus(); - - return 0; -} - -void RegisterAudioStatus() { - esp_console_cmd_t cmd{.command = "audio", - .help = "logs the current status of the audio pipeline", - .hint = NULL, - .func = &CmdAudioStatus, - .argtable = NULL}; - esp_console_cmd_register(&cmd); -} -*/ - int CmdDbInit(int argc, char** argv) { static const std::string usage = "usage: db_init"; if (argc != 1) { diff --git a/src/drivers/CMakeLists.txt b/src/drivers/CMakeLists.txt index 43e67786..82acab3d 100644 --- a/src/drivers/CMakeLists.txt +++ b/src/drivers/CMakeLists.txt @@ -4,7 +4,7 @@ idf_component_register( SRCS "touchwheel.cpp" "dac.cpp" "gpio_expander.cpp" "battery.cpp" "storage.cpp" "i2c.cpp" - "spi.cpp" "display.cpp" "display_init.cpp" "samd.cpp" + "spi.cpp" "display.cpp" "display_init.cpp" "samd.cpp" "relative_wheel.cpp" INCLUDE_DIRS "include" REQUIRES "esp_adc" "fatfs" "result" "lvgl" "span" "tasks") target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) diff --git a/src/drivers/display.cpp b/src/drivers/display.cpp index a503d0e9..0c9e56b3 100644 --- a/src/drivers/display.cpp +++ b/src/drivers/display.cpp @@ -12,6 +12,7 @@ #include "assert.h" #include "driver/gpio.h" +#include "driver/ledc.h" #include "driver/spi_master.h" #include "esp_attr.h" #include "esp_err.h" @@ -20,6 +21,7 @@ #include "freertos/portmacro.h" #include "freertos/projdefs.h" #include "hal/gpio_types.h" +#include "hal/ledc_types.h" #include "hal/lv_hal_disp.h" #include "hal/spi_types.h" #include "lvgl/lvgl.h" @@ -95,16 +97,24 @@ auto Display::Create(GpioExpander* expander, gpio_config(&dr_config); gpio_set_level(kDisplayDr, 0); - // TODO: use pwm for the backlight. - gpio_config_t led_config{ - .pin_bit_mask = 1ULL << kDisplayLedEn, - .mode = GPIO_MODE_OUTPUT, - .pull_up_en = GPIO_PULLUP_ENABLE, - .pull_down_en = GPIO_PULLDOWN_DISABLE, - .intr_type = GPIO_INTR_DISABLE, + ledc_timer_config_t led_config { + .speed_mode = LEDC_LOW_SPEED_MODE, + .duty_resolution = LEDC_TIMER_13_BIT, + .timer_num = LEDC_TIMER_0, + .freq_hz = 5000, + .clk_cfg = LEDC_AUTO_CLK, + }; + ledc_timer_config(&led_config); + + ledc_channel_config_t led_channel { + .gpio_num = kDisplayLedEn, + .speed_mode = LEDC_LOW_SPEED_MODE, + .channel = LEDC_CHANNEL_0, + .timer_sel = LEDC_TIMER_0, + .duty = 4095, + .hpoint = 0 }; - gpio_config(&led_config); - gpio_set_level(kDisplayLedEn, 1); + ledc_channel_config(&led_channel); // Next, init the SPI device spi_device_interface_config_t spi_cfg = { @@ -250,7 +260,7 @@ void Display::OnLvglFlush(lv_disp_drv_t* disp_drv, // area is stack-allocated, so it isn't safe to reference from the flush // thread. lv_area_t area_copy = *area; - worker_task_->Dispatch([=, this]() { + //worker_task_->Dispatch([=, this]() { // Ideally we want to complete a single flush as quickly as possible, so // grab the bus for this entire transaction sequence. spi_device_acquire_bus(handle_, portMAX_DELAY); @@ -276,7 +286,7 @@ void Display::OnLvglFlush(lv_disp_drv_t* disp_drv, spi_device_release_bus(handle_); lv_disp_flush_ready(&driver_); - }); + //}); } void RenderMain(void* raw_args) { diff --git a/src/drivers/include/relative_wheel.hpp b/src/drivers/include/relative_wheel.hpp new file mode 100644 index 00000000..3ff64b70 --- /dev/null +++ b/src/drivers/include/relative_wheel.hpp @@ -0,0 +1,44 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include +#include +#include + +#include "esp_err.h" +#include "result.hpp" + +#include "gpio_expander.hpp" +#include "touchwheel.hpp" + +namespace drivers { + +class RelativeWheel { + public: + static auto Create(TouchWheel *touch) -> RelativeWheel* { return new RelativeWheel(touch); } + + explicit RelativeWheel(TouchWheel *touch); + + // Not copyable or movable. + RelativeWheel(const RelativeWheel&) = delete; + RelativeWheel& operator=(const RelativeWheel&) = delete; + + auto Update() -> void; + + auto is_pressed() -> bool; + auto ticks() -> std::int_fast16_t; + + private: + TouchWheel *touch_; + bool is_pressed_; + bool is_first_read_; + std::int_fast16_t ticks_; + uint8_t last_angle_; +}; + +} // namespace drivers diff --git a/src/drivers/relative_wheel.cpp b/src/drivers/relative_wheel.cpp new file mode 100644 index 00000000..f64d213d --- /dev/null +++ b/src/drivers/relative_wheel.cpp @@ -0,0 +1,78 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "relative_wheel.hpp" + +#include +#include + +#include "esp_log.h" + +namespace drivers { + +RelativeWheel::RelativeWheel(TouchWheel *touch) + :touch_(touch), + is_pressed_(false), + is_first_read_(true), + ticks_(0), + last_angle_(0) {} + +auto RelativeWheel::Update() -> void { + touch_->Update(); + TouchWheelData d = touch_->GetTouchWheelData(); + is_pressed_ = d.is_touched; + + uint8_t new_angle = d.wheel_position; + if (is_first_read_) { + is_first_read_ = false; + last_angle_ = new_angle; + return; + } + + // Work out the magnitude of travel. + uint8_t change_cw = last_angle_ - new_angle; + uint8_t change_ccw = new_angle - last_angle_; + int change = std::min(change_cw, change_ccw); + + last_angle_ = new_angle; + + // Round to eliminate noise. + if (change <= 2) { + ticks_ = 0; + return; + } + + // Quantize into ticks. + change /= 4; + + // Clamp to reliminate more noise. + if (change > 10) { + change = 0; + } + + // Work out the direction of travel. + if (change_cw > change_ccw) { + change *= -1; + } + + ticks_ = change; +} + +auto RelativeWheel::is_pressed() -> bool { + return is_pressed_; +} + +auto RelativeWheel::ticks() -> std::int_fast16_t { + int_fast16_t t = ticks_; + if (t != 0) { + ESP_LOGI("teeks", "ticks %d", t); + } + ticks_ = 0; + return t; +} + + +} // namespace drivers diff --git a/src/events/event_queue.cpp b/src/events/event_queue.cpp index c69b1e72..1b04cb90 100644 --- a/src/events/event_queue.cpp +++ b/src/events/event_queue.cpp @@ -15,7 +15,8 @@ static const std::size_t kMaxPendingEvents = 16; EventQueue::EventQueue() : system_handle_(xQueueCreate(kMaxPendingEvents, sizeof(WorkItem*))), - ui_handle_(xQueueCreate(kMaxPendingEvents, sizeof(WorkItem*))) {} + ui_handle_(xQueueCreate(kMaxPendingEvents, sizeof(WorkItem*))), + audio_handle_(xQueueCreate(kMaxPendingEvents, sizeof(WorkItem*))) {} auto EventQueue::ServiceSystem(TickType_t max_wait_time) -> bool { WorkItem* item; diff --git a/src/events/include/event_queue.hpp b/src/events/include/event_queue.hpp index 45d766fd..eb8dd0a0 100644 --- a/src/events/include/event_queue.hpp +++ b/src/events/include/event_queue.hpp @@ -9,6 +9,7 @@ #include #include +#include "audio_fsm.hpp" #include "freertos/FreeRTOS.h" #include "freertos/portmacro.h" #include "freertos/queue.h" @@ -20,6 +21,11 @@ namespace events { typedef std::function WorkItem; +/* + * Handles communication of events between the system's state machines. Each + * event will be dispatched separately to each FSM, on the correct task for + * that FSM. + */ class EventQueue { public: static EventQueue& GetInstance() { @@ -32,9 +38,11 @@ class EventQueue { WorkItem* item = new WorkItem( [=]() { tinyfsm::FsmList::template dispatch(ev); }); if (std::is_same()) { - xQueueSend(system_handle_, &item, portMAX_DELAY); - } else { xQueueSend(ui_handle_, &item, portMAX_DELAY); + } else if (std::is_same()) { + xQueueSend(audio_handle_, &item, portMAX_DELAY); + } else { + xQueueSend(system_handle_, &item, portMAX_DELAY); } Dispatch(ev); } @@ -44,6 +52,7 @@ class EventQueue { auto ServiceSystem(TickType_t max_wait_time) -> bool; auto ServiceUi(TickType_t max_wait_time) -> bool; + auto ServiceAudio(TickType_t max_wait_time) -> bool; EventQueue(EventQueue const&) = delete; void operator=(EventQueue const&) = delete; @@ -53,6 +62,7 @@ class EventQueue { QueueHandle_t system_handle_; QueueHandle_t ui_handle_; + QueueHandle_t audio_handle_; }; template diff --git a/src/system_fsm/booting.cpp b/src/system_fsm/booting.cpp index 383bba81..b14da10c 100644 --- a/src/system_fsm/booting.cpp +++ b/src/system_fsm/booting.cpp @@ -13,6 +13,7 @@ #include "event_queue.hpp" #include "gpio_expander.hpp" #include "lvgl/lvgl.h" +#include "relative_wheel.hpp" #include "spi.hpp" #include "system_events.hpp" #include "system_fsm.hpp" @@ -43,25 +44,28 @@ auto Booting::entry() -> void { assert(sGpioExpander != nullptr); // Start bringing up LVGL now, since we have all of its prerequisites. - ESP_LOGI(kTag, "starting ui"); + ESP_LOGI(kTag, "installing ui drivers"); lv_init(); sDisplay.reset(drivers::Display::Create(sGpioExpander.get(), drivers::displays::kST7735R)); assert(sDisplay != nullptr); + sTouch.reset(drivers::TouchWheel::Create()); + if (sTouch != nullptr) { + sRelativeTouch.reset(new drivers::RelativeWheel(sTouch.get())); + } // The UI FSM now has everything it needs to start setting up. Do this now, // so that we can properly show the user any errors that appear later. - ui::UiState::Init(sGpioExpander.get(), sTouch, sDisplay, sDatabase); + ui::UiState::Init(sGpioExpander.get(), sRelativeTouch, sDisplay); events::Dispatch(DisplayReady()); // These drivers are required for normal operation, but aren't critical for // booting. We will transition to the error state if these aren't present. ESP_LOGI(kTag, "installing required drivers"); sSamd.reset(drivers::Samd::Create()); - sTouch.reset(drivers::TouchWheel::Create()); - auto dac_res = drivers::AudioDac::create(sGpioExpander.get()); - if (dac_res.has_error() || !sSamd || !sTouch) { + + if (dac_res.has_error() || !sSamd || !sRelativeTouch) { events::Dispatch( FatalError()); return; @@ -70,7 +74,7 @@ auto Booting::entry() -> void { // These drivers are initialised on boot, but are recoverable (if weird) if // they fail. - ESP_LOGI(kTag, "installing extra drivers"); + ESP_LOGI(kTag, "installing optional drivers"); sBattery.reset(drivers::Battery::Create()); // All drivers are now loaded, so we can finish initing the other state diff --git a/src/system_fsm/include/system_fsm.hpp b/src/system_fsm/include/system_fsm.hpp index 0153e403..4413f64e 100644 --- a/src/system_fsm/include/system_fsm.hpp +++ b/src/system_fsm/include/system_fsm.hpp @@ -14,6 +14,7 @@ #include "database.hpp" #include "display.hpp" #include "gpio_expander.hpp" +#include "relative_wheel.hpp" #include "samd.hpp" #include "storage.hpp" #include "tinyfsm.hpp" @@ -51,6 +52,7 @@ class SystemState : public tinyfsm::Fsm { static std::shared_ptr sSamd; static std::shared_ptr sTouch; + static std::shared_ptr sRelativeTouch; static std::shared_ptr sBattery; static std::shared_ptr sStorage; static std::shared_ptr sDisplay; diff --git a/src/system_fsm/system_fsm.cpp b/src/system_fsm/system_fsm.cpp index 6956b87c..eb7cce52 100644 --- a/src/system_fsm/system_fsm.cpp +++ b/src/system_fsm/system_fsm.cpp @@ -5,6 +5,7 @@ */ #include "system_fsm.hpp" +#include "relative_wheel.hpp" #include "system_events.hpp" namespace system_fsm { @@ -13,6 +14,7 @@ std::shared_ptr SystemState::sGpioExpander; std::shared_ptr SystemState::sSamd; std::shared_ptr SystemState::sTouch; +std::shared_ptr SystemState::sRelativeTouch; std::shared_ptr SystemState::sBattery; std::shared_ptr SystemState::sStorage; std::shared_ptr SystemState::sDisplay; diff --git a/src/tasks/tasks.cpp b/src/tasks/tasks.cpp index aa5c4aa7..b95d8e16 100644 --- a/src/tasks/tasks.cpp +++ b/src/tasks/tasks.cpp @@ -47,7 +47,7 @@ auto AllocateStack() -> cpp::span { // PSRAM so we give it a bit of headroom for safety. template <> auto AllocateStack() -> cpp::span { - std::size_t size = 16 * 1024; + std::size_t size = 32 * 1024; return {static_cast(heap_caps_malloc(size, MALLOC_CAP_DEFAULT)), size}; } diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 9a41ae0d..bd0b76f0 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -3,7 +3,7 @@ # SPDX-License-Identifier: GPL-3.0-only idf_component_register( - SRCS "lvgl_task.cpp" "ui_fsm.cpp" "screen_splash.cpp" "screen_menu.cpp" + SRCS "lvgl_task.cpp" "ui_fsm.cpp" "screen_splash.cpp" "screen_menu.cpp" "wheel_encoder.cpp" INCLUDE_DIRS "include" REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer") target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) diff --git a/src/ui/include/lvgl_task.hpp b/src/ui/include/lvgl_task.hpp index 25e7dd14..8e387683 100644 --- a/src/ui/include/lvgl_task.hpp +++ b/src/ui/include/lvgl_task.hpp @@ -14,11 +14,12 @@ #include "freertos/task.h" #include "display.hpp" +#include "relative_wheel.hpp" #include "touchwheel.hpp" namespace ui { -auto StartLvgl(std::weak_ptr touch_wheel, +auto StartLvgl(std::weak_ptr touch_wheel, std::weak_ptr display) -> void; } // namespace ui diff --git a/src/ui/include/screen.hpp b/src/ui/include/screen.hpp new file mode 100644 index 00000000..87a0d9b8 --- /dev/null +++ b/src/ui/include/screen.hpp @@ -0,0 +1,28 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include + +#include "core/lv_obj.h" +#include "core/lv_obj_tree.h" +#include "lvgl.h" + +namespace ui { + +class Screen { + public: + Screen() : root_(lv_obj_create(NULL)) {} + virtual ~Screen() { lv_obj_del(root_); } + + auto root() -> lv_obj_t* { return root_; } + + protected: + lv_obj_t* const root_; +}; + +} // namespace ui diff --git a/src/ui/include/screen_menu.hpp b/src/ui/include/screen_menu.hpp new file mode 100644 index 00000000..a0b07b9e --- /dev/null +++ b/src/ui/include/screen_menu.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include + +#include "lvgl.h" + +#include "screen.hpp" + +namespace ui { +namespace screens { + +class Menu : public Screen { + public: + Menu(); + ~Menu(); + + private: + lv_obj_t* container_; + lv_obj_t* label_; +}; + +} // namespace screens +} // namespace ui diff --git a/src/ui/include/screen_splash.hpp b/src/ui/include/screen_splash.hpp new file mode 100644 index 00000000..1ee7dd89 --- /dev/null +++ b/src/ui/include/screen_splash.hpp @@ -0,0 +1,30 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include + +#include "lvgl.h" + +#include "screen.hpp" + +namespace ui { +namespace screens { + +class Splash : public Screen { + public: + Splash(); + ~Splash(); + + private: + lv_obj_t* container_; + lv_obj_t* label_; + lv_obj_t* spinner_; +}; + +} // namespace screens +} // namespace ui diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index 2afcfa86..d4d23bb0 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -8,9 +8,9 @@ #include +#include "relative_wheel.hpp" #include "tinyfsm.hpp" -#include "database.hpp" #include "display.hpp" #include "screen.hpp" #include "storage.hpp" @@ -22,9 +22,8 @@ namespace ui { class UiState : public tinyfsm::Fsm { public: static auto Init(drivers::GpioExpander* gpio_expander, - std::weak_ptr touchwheel, - std::weak_ptr display, - std::weak_ptr database) -> void; + std::weak_ptr touchwheel, + std::weak_ptr display) -> void; virtual ~UiState() {} @@ -43,9 +42,8 @@ class UiState : public tinyfsm::Fsm { protected: static drivers::GpioExpander* sGpioExpander; - static std::weak_ptr sTouchWheel; + static std::weak_ptr sTouchWheel; static std::weak_ptr sDisplay; - static std::weak_ptr sDatabase; static std::shared_ptr sCurrentScreen; }; diff --git a/src/ui/include/ui_tick.hpp b/src/ui/include/ui_tick.hpp new file mode 100644 index 00000000..37f8a8bd --- /dev/null +++ b/src/ui/include/ui_tick.hpp @@ -0,0 +1,11 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include "esp_timer.h" + +#define LV_TICK_CUSTOM_SYS_TIME_EXPR (esp_timer_get_time() / 1000) diff --git a/src/ui/include/wheel_encoder.hpp b/src/ui/include/wheel_encoder.hpp new file mode 100644 index 00000000..0651ce0b --- /dev/null +++ b/src/ui/include/wheel_encoder.hpp @@ -0,0 +1,30 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include + +#include "hal/lv_hal_indev.h" + +#include "relative_wheel.hpp" + +namespace ui { + +class TouchWheelEncoder { + public: + explicit TouchWheelEncoder(std::weak_ptr wheel); + + auto Read(lv_indev_data_t *data) -> void; + auto registration() -> lv_indev_t* { return registration_; } + + private: + lv_indev_drv_t driver_; + lv_indev_t *registration_; + std::weak_ptr wheel_; +}; + +} // namespace ui diff --git a/src/ui/lvgl_task.cpp b/src/ui/lvgl_task.cpp index f2f7c67c..1ce7fd40 100644 --- a/src/ui/lvgl_task.cpp +++ b/src/ui/lvgl_task.cpp @@ -15,6 +15,8 @@ #include #include "core/lv_disp.h" +#include "core/lv_group.h" +#include "core/lv_indev.h" #include "core/lv_obj.h" #include "core/lv_obj_pos.h" #include "core/lv_obj_tree.h" @@ -25,15 +27,18 @@ #include "freertos/projdefs.h" #include "freertos/timers.h" #include "hal/gpio_types.h" +#include "hal/lv_hal_indev.h" #include "hal/spi_types.h" #include "lv_api_map.h" #include "lvgl/lvgl.h" #include "misc/lv_color.h" #include "misc/lv_style.h" #include "misc/lv_timer.h" +#include "relative_wheel.hpp" #include "tasks.hpp" #include "touchwheel.hpp" #include "ui_fsm.hpp" +#include "wheel_encoder.hpp" #include "widgets/lv_label.h" #include "display.hpp" @@ -43,11 +48,16 @@ namespace ui { static const char* kTag = "lv_task"; -void LvglMain(std::weak_ptr weak_touch_wheel, +void LvglMain(std::weak_ptr weak_touch_wheel, std::weak_ptr weak_display) { ESP_LOGI(kTag, "init lvgl"); lv_init(); + TouchWheelEncoder encoder(weak_touch_wheel); + lv_group_t *nav_group = lv_group_create(); + lv_group_set_default(nav_group); + lv_indev_set_group(encoder.registration(), nav_group); + std::shared_ptr current_screen; auto& events = events::EventQueue::GetInstance(); while (1) { @@ -65,10 +75,12 @@ void LvglMain(std::weak_ptr weak_touch_wheel, // 30 FPS // TODO(jacqueline): make this dynamic vTaskDelay(pdMS_TO_TICKS(33)); + lv_indev_data_t d; + encoder.Read(&d); } } -auto StartLvgl(std::weak_ptr touch_wheel, +auto StartLvgl(std::weak_ptr touch_wheel, std::weak_ptr display) -> void { tasks::StartPersistent( [=]() { LvglMain(touch_wheel, display); }); diff --git a/src/ui/screen_menu.cpp b/src/ui/screen_menu.cpp new file mode 100644 index 00000000..da0a7d3c --- /dev/null +++ b/src/ui/screen_menu.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "screen_menu.hpp" + +#include "core/lv_group.h" +#include "core/lv_obj_pos.h" +#include "extra/widgets/menu/lv_menu.h" +#include "extra/widgets/spinner/lv_spinner.h" +#include "hal/lv_hal_disp.h" +#include "misc/lv_area.h" +#include "widgets/lv_label.h" + +namespace ui { +namespace screens { + +Menu::Menu() { + lv_obj_t *menu = lv_menu_create(root_); + lv_obj_set_size(menu, lv_disp_get_hor_res(NULL), lv_disp_get_ver_res(NULL)); + lv_obj_center(menu); + + lv_obj_t *main_page = lv_menu_page_create(menu, NULL); + + lv_obj_t *container; + lv_obj_t *label; + + container = lv_menu_cont_create(main_page); + label = lv_label_create(container); + lv_label_set_text(label, "I am an item"); + + container = lv_menu_cont_create(main_page); + label = lv_label_create(container); + lv_label_set_text(label, "I am also an item"); + + container = lv_menu_cont_create(main_page); + label = lv_label_create(container); + lv_label_set_text(label, "Item #3"); + + container = lv_menu_cont_create(main_page); + label = lv_label_create(container); + lv_label_set_text(label, "Yay!"); + + lv_menu_set_page(menu, main_page); +} + +Menu::~Menu() {} + +} // namespace screens +} // namespace ui diff --git a/src/ui/screen_splash.cpp b/src/ui/screen_splash.cpp new file mode 100644 index 00000000..2b2a7b1e --- /dev/null +++ b/src/ui/screen_splash.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "screen_splash.hpp" + +#include "core/lv_obj_pos.h" +#include "extra/widgets/spinner/lv_spinner.h" +#include "misc/lv_area.h" +#include "widgets/lv_label.h" + +namespace ui { +namespace screens { + +Splash::Splash() { + container_ = lv_obj_create(root_); + lv_obj_set_align(container_, LV_ALIGN_CENTER); + lv_obj_set_size(container_, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + + label_ = lv_label_create(container_); + lv_label_set_text_static(label_, "TANGARA"); + lv_obj_set_align(label_, LV_ALIGN_TOP_MID); + + spinner_ = lv_spinner_create(container_, 1000, 60); + lv_obj_set_size(spinner_, 32, 32); + lv_obj_align_to(spinner_, label_, LV_ALIGN_OUT_BOTTOM_MID, 0, 8); +} + +Splash::~Splash() { + lv_obj_del(spinner_); + lv_obj_del(label_); + lv_obj_del(container_); +} + +} // namespace screens +} // namespace ui diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index b08722aa..f12104e6 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -7,6 +7,7 @@ #include "ui_fsm.hpp" #include "display.hpp" #include "lvgl_task.hpp" +#include "relative_wheel.hpp" #include "screen.hpp" #include "screen_menu.hpp" #include "screen_splash.hpp" @@ -16,20 +17,17 @@ namespace ui { drivers::GpioExpander* UiState::sGpioExpander; -std::weak_ptr UiState::sTouchWheel; +std::weak_ptr UiState::sTouchWheel; std::weak_ptr UiState::sDisplay; -std::weak_ptr UiState::sDatabase; std::shared_ptr UiState::sCurrentScreen; auto UiState::Init(drivers::GpioExpander* gpio_expander, - std::weak_ptr touchwheel, - std::weak_ptr display, - std::weak_ptr database) -> void { + std::weak_ptr touchwheel, + std::weak_ptr display) -> void { sGpioExpander = gpio_expander; sTouchWheel = touchwheel; sDisplay = display; - sDatabase = database; } namespace states { @@ -47,7 +45,7 @@ void Splash::react(const system_fsm::BootComplete& ev) { } void Interactive::entry() { - // sCurrentScreen.reset(new screens::Menu()); + //sCurrentScreen.reset(new screens::Menu()); } } // namespace states diff --git a/src/ui/wheel_encoder.cpp b/src/ui/wheel_encoder.cpp new file mode 100644 index 00000000..0129434d --- /dev/null +++ b/src/ui/wheel_encoder.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "wheel_encoder.hpp" +#include "hal/lv_hal_indev.h" + +namespace ui { + +void encoder_read(lv_indev_drv_t * drv, lv_indev_data_t*data){ + TouchWheelEncoder *instance = reinterpret_cast(drv->user_data); + instance->Read(data); +} + + TouchWheelEncoder::TouchWheelEncoder(std::weak_ptr wheel) : wheel_(wheel) { + lv_indev_drv_init(&driver_); + driver_.type = LV_INDEV_TYPE_ENCODER; + driver_.read_cb = encoder_read; + driver_.user_data = this; + + registration_ = lv_indev_drv_register(&driver_); + } + +auto TouchWheelEncoder::Read(lv_indev_data_t *data) -> void { + auto lock = wheel_.lock(); + if (lock == nullptr) { + data->state = LV_INDEV_STATE_RELEASED; + data->enc_diff = 0; + return; + } + + lock->Update(); + data->state = lock->is_pressed() ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; + data->enc_diff = lock->ticks(); +} + +} // namespace ui