Add touchwheel -> encoder adapter

custom
jacqueline 2 years ago
parent db2e29a72d
commit 6fd588e970
  1. 80
      src/app_console/app_console.cpp
  2. 2
      src/drivers/CMakeLists.txt
  3. 32
      src/drivers/display.cpp
  4. 44
      src/drivers/include/relative_wheel.hpp
  5. 78
      src/drivers/relative_wheel.cpp
  6. 3
      src/events/event_queue.cpp
  7. 14
      src/events/include/event_queue.hpp
  8. 16
      src/system_fsm/booting.cpp
  9. 2
      src/system_fsm/include/system_fsm.hpp
  10. 2
      src/system_fsm/system_fsm.cpp
  11. 2
      src/tasks/tasks.cpp
  12. 2
      src/ui/CMakeLists.txt
  13. 3
      src/ui/include/lvgl_task.hpp
  14. 28
      src/ui/include/screen.hpp
  15. 29
      src/ui/include/screen_menu.hpp
  16. 30
      src/ui/include/screen_splash.hpp
  17. 10
      src/ui/include/ui_fsm.hpp
  18. 11
      src/ui/include/ui_tick.hpp
  19. 30
      src/ui/include/wheel_encoder.hpp
  20. 16
      src/ui/lvgl_task.cpp
  21. 52
      src/ui/screen_menu.cpp
  22. 38
      src/ui/screen_splash.cpp
  23. 12
      src/ui/ui_fsm.cpp
  24. 39
      src/ui/wheel_encoder.cpp

@ -14,9 +14,12 @@
#include <iostream>
#include <string>
#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, audio::AudioState>(
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) {

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

@ -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<void>([=, this]() {
//worker_task_->Dispatch<void>([=, 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) {

@ -0,0 +1,44 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#pragma once
#include <stdint.h>
#include <cstdint>
#include <functional>
#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

@ -0,0 +1,78 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#include "relative_wheel.hpp"
#include <stdint.h>
#include <cstdint>
#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

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

@ -9,6 +9,7 @@
#include <functional>
#include <type_traits>
#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<void(void)> 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<Machine>::template dispatch<Event>(ev); });
if (std::is_same<Machine, ui::UiState>()) {
xQueueSend(system_handle_, &item, portMAX_DELAY);
} else {
xQueueSend(ui_handle_, &item, portMAX_DELAY);
} else if (std::is_same<Machine, audio::AudioState>()) {
xQueueSend(audio_handle_, &item, portMAX_DELAY);
} else {
xQueueSend(system_handle_, &item, portMAX_DELAY);
}
Dispatch<Event, Machines...>(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 <typename Event, typename... Machines>

@ -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, ui::UiState>(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, SystemState, ui::UiState, audio::AudioState>(
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

@ -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<SystemState> {
static std::shared_ptr<drivers::Samd> sSamd;
static std::shared_ptr<drivers::TouchWheel> sTouch;
static std::shared_ptr<drivers::RelativeWheel> sRelativeTouch;
static std::shared_ptr<drivers::Battery> sBattery;
static std::shared_ptr<drivers::SdStorage> sStorage;
static std::shared_ptr<drivers::Display> sDisplay;

@ -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<drivers::GpioExpander> SystemState::sGpioExpander;
std::shared_ptr<drivers::Samd> SystemState::sSamd;
std::shared_ptr<drivers::TouchWheel> SystemState::sTouch;
std::shared_ptr<drivers::RelativeWheel> SystemState::sRelativeTouch;
std::shared_ptr<drivers::Battery> SystemState::sBattery;
std::shared_ptr<drivers::SdStorage> SystemState::sStorage;
std::shared_ptr<drivers::Display> SystemState::sDisplay;

@ -47,7 +47,7 @@ auto AllocateStack<Type::kAudio>() -> cpp::span<StackType_t> {
// PSRAM so we give it a bit of headroom for safety.
template <>
auto AllocateStack<Type::kUi>() -> cpp::span<StackType_t> {
std::size_t size = 16 * 1024;
std::size_t size = 32 * 1024;
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_DEFAULT)),
size};
}

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

@ -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<drivers::TouchWheel> touch_wheel,
auto StartLvgl(std::weak_ptr<drivers::RelativeWheel> touch_wheel,
std::weak_ptr<drivers::Display> display) -> void;
} // namespace ui

@ -0,0 +1,28 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#pragma once
#include <memory>
#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

@ -0,0 +1,29 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#pragma once
#include <memory>
#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

@ -0,0 +1,30 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#pragma once
#include <memory>
#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

@ -8,9 +8,9 @@
#include <memory>
#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<UiState> {
public:
static auto Init(drivers::GpioExpander* gpio_expander,
std::weak_ptr<drivers::TouchWheel> touchwheel,
std::weak_ptr<drivers::Display> display,
std::weak_ptr<database::Database> database) -> void;
std::weak_ptr<drivers::RelativeWheel> touchwheel,
std::weak_ptr<drivers::Display> display) -> void;
virtual ~UiState() {}
@ -43,9 +42,8 @@ class UiState : public tinyfsm::Fsm<UiState> {
protected:
static drivers::GpioExpander* sGpioExpander;
static std::weak_ptr<drivers::TouchWheel> sTouchWheel;
static std::weak_ptr<drivers::RelativeWheel> sTouchWheel;
static std::weak_ptr<drivers::Display> sDisplay;
static std::weak_ptr<database::Database> sDatabase;
static std::shared_ptr<Screen> sCurrentScreen;
};

@ -0,0 +1,11 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* 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)

@ -0,0 +1,30 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#pragma once
#include <memory>
#include "hal/lv_hal_indev.h"
#include "relative_wheel.hpp"
namespace ui {
class TouchWheelEncoder {
public:
explicit TouchWheelEncoder(std::weak_ptr<drivers::RelativeWheel> 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<drivers::RelativeWheel> wheel_;
};
} // namespace ui

@ -15,6 +15,8 @@
#include <memory>
#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<drivers::TouchWheel> weak_touch_wheel,
void LvglMain(std::weak_ptr<drivers::RelativeWheel> weak_touch_wheel,
std::weak_ptr<drivers::Display> 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<Screen> current_screen;
auto& events = events::EventQueue::GetInstance();
while (1) {
@ -65,10 +75,12 @@ void LvglMain(std::weak_ptr<drivers::TouchWheel> 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<drivers::TouchWheel> touch_wheel,
auto StartLvgl(std::weak_ptr<drivers::RelativeWheel> touch_wheel,
std::weak_ptr<drivers::Display> display) -> void {
tasks::StartPersistent<tasks::Type::kUi>(
[=]() { LvglMain(touch_wheel, display); });

@ -0,0 +1,52 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* 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

@ -0,0 +1,38 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* 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

@ -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<drivers::TouchWheel> UiState::sTouchWheel;
std::weak_ptr<drivers::RelativeWheel> UiState::sTouchWheel;
std::weak_ptr<drivers::Display> UiState::sDisplay;
std::weak_ptr<database::Database> UiState::sDatabase;
std::shared_ptr<Screen> UiState::sCurrentScreen;
auto UiState::Init(drivers::GpioExpander* gpio_expander,
std::weak_ptr<drivers::TouchWheel> touchwheel,
std::weak_ptr<drivers::Display> display,
std::weak_ptr<database::Database> database) -> void {
std::weak_ptr<drivers::RelativeWheel> touchwheel,
std::weak_ptr<drivers::Display> 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

@ -0,0 +1,39 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* 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<TouchWheelEncoder*>(drv->user_data);
instance->Read(data);
}
TouchWheelEncoder::TouchWheelEncoder(std::weak_ptr<drivers::RelativeWheel> 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
Loading…
Cancel
Save