Compare commits

..

1 Commits

Author SHA1 Message Date
6a104cea00 feat: add list and started user input manager 2025-05-25 13:12:08 +02:00
13 changed files with 141 additions and 331 deletions

View File

View File

@ -285,7 +285,7 @@
*LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
*LV_LOG_LEVEL_USER Only logs added by the user
*LV_LOG_LEVEL_NONE Do not log anything*/
#define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
#define LV_LOG_LEVEL LV_LOG_LEVEL_INFO
/*1: Print the log with 'printf';
*0: User need to register a callback with `lv_log_register_print_cb()`*/
@ -861,14 +861,14 @@
#define LV_USE_SNAPSHOT 0
/*1: Enable system monitor component*/
#define LV_USE_SYSMON 1
#define LV_USE_SYSMON 0
#if LV_USE_SYSMON
/*Get the idle percentage. E.g. uint32_t my_get_idle(void);*/
#define LV_SYSMON_GET_IDLE lv_timer_get_idle
/*1: Show CPU usage and FPS count
* Requires `LV_USE_SYSMON = 1`*/
#define LV_USE_PERF_MONITOR 1
#define LV_USE_PERF_MONITOR 0
#if LV_USE_PERF_MONITOR
#define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT

View File

@ -13,7 +13,6 @@ platform = espressif32
board = esp32doit-devkit-v1
framework = arduino
monitor_speed = 115200
board_build.filesystem = littlefs
lib_deps =
ruiseixasm/Versatile_RotaryEncoder@^1.3.1
bodmer/TFT_eSPI@^2.5.43

View File

@ -51,21 +51,25 @@ namespace UserInputManager
void pushEvent(InputEvent event)
{
Serial.println("pushing new event " + String(event));
for (auto &queue : input_event_queues)
{
Serial.println("pushing event to queue " + queue.first + " (" + String(queue.second.size()) + ")");
queue.second.push(event);
}
}
InputEvent getNextEvent(String name)
{
queue<InputEvent> &events = input_event_queues.at(name);
queue<InputEvent> events = input_event_queues.at(name);
if (events.empty())
{
return InputEvent::None;
}
Serial.print("getting next event (" + String(events.size()) + "->");
InputEvent event = events.front();
events.pop();
Serial.println(String(events.size()) + ")");
return event;
}

View File

@ -15,7 +15,6 @@
#include <input/pad_reader.hpp>
#include <input/user_input_manager.hpp>
#include <audio/tone_generator.hpp>
#include <tools/file_manager.hpp>
const int ONBOARD_LED_PIN = 2;
@ -26,7 +25,6 @@ void setup()
PadReader::init();
Encoder::init();
FileManager::init();
Display::init();
ToneGenerator::init();
UserInputManager::init();

View File

@ -1,47 +0,0 @@
#include <Arduino.h>
#include <LittleFS.h>
#include <tools/file_manager.hpp>
#include <vector>
namespace FileManager
{
using std::vector;
void init()
{
if (!LittleFS.begin())
{
Serial.println("LittleFS Mount Failed");
return;
}
}
vector<File> listFiles(const String path)
{
vector<File> files;
fs::File root = LittleFS.open(path, "r");
if (!root)
{
Serial.println("Failed to open root directory");
}
if (!root.isDirectory())
{
Serial.println("Root is not a directory");
}
fs::File fsFile = root.openNextFile();
while (fsFile)
{
File file;
file.name = fsFile.name();
file.isDirectory = fsFile.isDirectory();
files.push_back(file);
fsFile = root.openNextFile();
}
return files;
}
}

View File

@ -1,21 +0,0 @@
#ifndef DRUMZ_TOOLS_FILE_MANAGER
#define DRUMZ_TOOLS_FILE_MANAGER
#include <Arduino.h>
#include <vector>
namespace FileManager
{
using std::vector;
struct File
{
String name;
bool isDirectory;
};
void init();
vector<File> listFiles(const String path);
}
#endif //DRUMZ_TOOLS_FILE_MANAGER

View File

@ -7,6 +7,8 @@
#include <ui/views/main_view.hpp>
#include <input/user_input_manager.hpp>
#include <vector>
#ifndef SCREEN_WIDTH
#define SCREEN_WIDTH 320
#endif
@ -15,34 +17,28 @@
#define SCREEN_HEIGHT 240
#endif
#define DRAW_BUF_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT / 6 * (LV_COLOR_DEPTH / 8))
#define DRAW_BUF_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT / 10 * (LV_COLOR_DEPTH / 8))
namespace Display
{
using std::vector;
using UserInputManager::InputEvent;
const uint8_t MAIN_VIEW = 1;
const String USER_INPUT_HANDLE = "display";
uint32_t draw_buf[DRAW_BUF_SIZE / 4];
uint32_t getTime();
void handleLvglLogs(lv_log_level_t level, const char *buf);
void handleUpdateView(lv_timer_t *timer);
void handleUpdateValues(lv_timer_t *timer);
void handleReadUserInput(lv_indev_t *indev, lv_indev_data_t *data);
void setCurrentView(uint8_t view);
lv_display_t *setupDisplay();
lv_indev_t *setupInputDevice();
static void flushCallback(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map);
static void resolutionChangedEventCallback(lv_event_t *e);
typedef struct
{
TFT_eSPI *tft;
} lv_tft_espi_t;
const String USER_INPUT_HANDLE = "display";
uint32_t draw_buf[DRAW_BUF_SIZE / 4];
static lv_color_t *buf1 = (lv_color_t *)heap_caps_aligned_alloc(32, DRAW_BUF_SIZE, MALLOC_CAP_DMA);
static lv_color_t *buf2 = (lv_color_t *)heap_caps_aligned_alloc(32, DRAW_BUF_SIZE, MALLOC_CAP_DMA);
lv_timer_t *update_view_timer;
lv_timer_t *update_values_timer;
lv_indev_t *indev;
lv_display_t *display;
InputEvent last_input_event;
uint8_t current_view = 0;
void init()
{
@ -50,14 +46,22 @@ namespace Display
lv_log_register_print_cb(handleLvglLogs);
lv_tick_set_cb(getTime);
display = setupDisplay();
indev = setupInputDevice();
update_values_timer = lv_timer_create(handleUpdateValues, 5, NULL);
lv_timer_ready(update_values_timer);
lv_obj_t *parent = lv_screen_active();
MainView::render(parent, indev);
lv_display_t *display;
display = lv_tft_espi_create(SCREEN_HEIGHT, SCREEN_WIDTH, draw_buf, sizeof(draw_buf));
lv_display_set_rotation(display, LV_DISPLAY_ROTATION_90);
update_view_timer = lv_timer_create(handleUpdateView, 5, NULL);
lv_timer_ready(update_view_timer);
UserInputManager::registerForEvents(USER_INPUT_HANDLE);
indev = lv_indev_create();
lv_indev_set_type(indev, LV_INDEV_TYPE_KEYPAD);
lv_indev_set_read_cb(indev, handleReadUserInput);
setCurrentView(MAIN_VIEW);
lv_group_t *group = lv_group_create();
lv_group_add_obj(group, lv_screen_active());
lv_indev_set_group(indev, group);
}
void update()
@ -65,75 +69,6 @@ namespace Display
lv_timer_handler();
}
lv_display_t *setupDisplay()
{
void *buf = draw_buf;
uint32_t buf_size_bytes = sizeof(draw_buf);
lv_tft_espi_t *dsc = (lv_tft_espi_t *)lv_malloc_zeroed(sizeof(lv_tft_espi_t));
LV_ASSERT_MALLOC(dsc);
if (dsc == NULL)
return NULL;
lv_display_t *disp = lv_display_create(SCREEN_HEIGHT, SCREEN_WIDTH);
if (disp == NULL)
{
lv_free(dsc);
return NULL;
}
dsc->tft = new TFT_eSPI(SCREEN_HEIGHT, SCREEN_WIDTH);
dsc->tft->begin(); /* TFT init */
dsc->tft->setRotation(0);
lv_display_set_driver_data(disp, (void *)dsc);
lv_display_set_flush_cb(disp, flushCallback);
lv_display_add_event_cb(disp, resolutionChangedEventCallback, LV_EVENT_RESOLUTION_CHANGED, NULL);
lv_display_set_buffers(disp, buf1, buf2, SCREEN_HEIGHT * SCREEN_WIDTH / 6, LV_DISPLAY_RENDER_MODE_PARTIAL);
lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_90);
return disp;
}
static void flushCallback(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
{
lv_tft_espi_t *dsc = (lv_tft_espi_t *)lv_display_get_driver_data(disp);
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
dsc->tft->startWrite();
dsc->tft->setAddrWindow(area->x1, area->y1, w, h);
dsc->tft->pushColors((uint16_t *)px_map, w * h, true);
dsc->tft->endWrite();
lv_display_flush_ready(disp);
}
static void resolutionChangedEventCallback(lv_event_t *e)
{
lv_display_t *disp = (lv_display_t *)lv_event_get_target(e);
lv_tft_espi_t *dsc = (lv_tft_espi_t *)lv_display_get_driver_data(disp);
int32_t hor_res = lv_display_get_horizontal_resolution(disp);
int32_t ver_res = lv_display_get_vertical_resolution(disp);
lv_display_rotation_t rot = lv_display_get_rotation(disp);
/* handle rotation */
switch (rot)
{
case LV_DISPLAY_ROTATION_0:
dsc->tft->setRotation(0); /* Portrait orientation */
break;
case LV_DISPLAY_ROTATION_90:
dsc->tft->setRotation(1); /* Landscape orientation */
break;
case LV_DISPLAY_ROTATION_180:
dsc->tft->setRotation(2); /* Portrait orientation, flipped */
break;
case LV_DISPLAY_ROTATION_270:
dsc->tft->setRotation(3); /* Landscape orientation, flipped */
break;
}
}
void handleLvglLogs(lv_log_level_t level, const char *buf)
{
Serial.printf("LVGL: %s\r\n", buf);
@ -144,42 +79,70 @@ namespace Display
return esp_timer_get_time() / 1000;
}
void handleUpdateView(lv_timer_t *timer)
void setCurrentView(uint8_t view)
{
MainView::update();
current_view = view;
lv_obj_t *parent = lv_screen_active();
lv_obj_clean(parent);
switch (view)
{
case MAIN_VIEW:
MainView::render(parent);
break;
}
}
lv_indev_t *setupInputDevice()
void handleUpdateValues(lv_timer_t *timer)
{
UserInputManager::registerForEvents(USER_INPUT_HANDLE);
lv_indev_t *indev = lv_indev_create();
lv_indev_set_type(indev, LV_INDEV_TYPE_KEYPAD);
lv_indev_set_read_cb(indev, handleReadUserInput);
return indev;
switch (current_view)
{
case MAIN_VIEW:
MainView::update();
break;
}
}
void handleReadUserInput(lv_indev_t *indev, lv_indev_data_t *data)
{
//switch(last_input_event) {
// case InputEvent::EncoderRotateLeft:
// data->key = LV_KEY_PREV;
// data->state = LV_INDEV_STATE_RELEASED;
// last_input_event = InputEvent::None;
// return;
// case InputEvent::EncoderRotateRight:
// data->key = LV_KEY_NEXT;
// data->state = LV_INDEV_STATE_RELEASED;
// last_input_event = InputEvent::None;
// return;
//}
InputEvent event = UserInputManager::getNextEvent(USER_INPUT_HANDLE);
switch (event)
{
case InputEvent::EncoderButtonRelease:
data->key = LV_KEY_ENTER;
data->state = LV_INDEV_STATE_RELEASED;
break;
case InputEvent::EncoderButtonPress:
data->key = LV_KEY_ENTER;
data->state = LV_INDEV_STATE_PRESSED;
break;
case InputEvent::EncoderRotateLeft:
data->key = LV_KEY_PREV;
data->state = LV_INDEV_STATE_PRESSED;
break;
case InputEvent::EncoderRotateRight:
data->key = LV_KEY_NEXT;
data->state = LV_INDEV_STATE_PRESSED;
break;
if (event != InputEvent::None) {
Serial.println("processing input event " + String(event));
}
//switch (event)
//{
//case InputEvent::EncoderButtonRelease:
// data->key = LV_KEY_ENTER;
// data->state = LV_INDEV_STATE_RELEASED;
// Serial.println("indev event EncoderButtonRelease");
// break;
//case InputEvent::EncoderButtonPress:
// data->key = LV_KEY_ENTER;
// data->state = LV_INDEV_STATE_PR;
// Serial.println("indev event EncoderButtonPress");
// break;
//case InputEvent::EncoderRotateLeft:
// data->key = LV_KEY_PREV;
// data->state = LV_INDEV_STATE_PRESSED;
// Serial.println("indev event EncoderRotateLeft");
// break;
//case InputEvent::EncoderRotateRight:
// data->key = LV_KEY_NEXT;
// data->state = LV_INDEV_STATE_PRESSED;
// Serial.println("indev event EncoderRotateRight");
// break;
//}
//last_input_event = event;
}
}

View File

@ -1,119 +0,0 @@
#include <Arduino.h>
#include <lvgl.h>
#include <vector>
#include <tools/file_manager.hpp>
namespace FileBrowserView
{
using FileManager::File;
using std::vector;
void renderList(lv_obj_t *parent);
void updateList();
void changeDirRelative(String dir);
void changeDir(String dir);
static vector<File> files;
String currentDir = "/";
lv_obj_t *label;
lv_obj_t *list;
lv_obj_t *content;
lv_group_t *group;
lv_indev_t *indev;
void render(lv_obj_t *parent, lv_indev_t *newIndev)
{
indev = newIndev;
group = lv_group_create();
lv_indev_set_group(indev, group);
content = lv_obj_create(parent);
lv_obj_set_width(content, lv_pct(100));
lv_obj_set_height(content, lv_pct(100));
lv_obj_center(content);
files = FileManager::listFiles(currentDir);
lv_obj_clean(content);
renderList(parent);
}
void update()
{
}
void renderList(lv_obj_t *parent)
{
list = lv_list_create(parent);
lv_obj_set_width(list, lv_pct(100));
lv_obj_set_height(list, lv_pct(100));
lv_obj_center(list);
updateList();
}
void updateList()
{
lv_obj_clean(list);
lv_obj_t *btn;
if (currentDir != "/")
{
btn = lv_list_add_btn(list, LV_SYMBOL_DIRECTORY, "..");
lv_group_add_obj(group, btn);
auto callback = [](lv_event_t *event) {
changeDirRelative("..");
};
lv_obj_add_event_cb(btn, callback, LV_EVENT_CLICKED, NULL);
}
for (int i = 0; i < files.size(); i++)
{
File file = files.at(i);
auto symbol = file.isDirectory? LV_SYMBOL_DIRECTORY : LV_SYMBOL_FILE;
btn = lv_list_add_btn(list, symbol, file.name.c_str());
lv_group_add_obj(group, btn);
auto callback = [](lv_event_t *event) {
File *file = (File *)lv_event_get_user_data(event);
if (!file->isDirectory)
return;
changeDirRelative(file->name);
};
Serial.println(file.name + "\t" + (int)&files.at(i));
lv_obj_add_event_cb(btn, callback, LV_EVENT_CLICKED, &files.at(i));
}
}
void changeDirRelative(String dir)
{
String newDir = currentDir;
if (dir == "..")
{
if (currentDir == "/")
return;
if (newDir.endsWith("/"))
{
newDir = newDir.substring(0, newDir.length() - 1);
}
int lastSlash = newDir.lastIndexOf("/");
if (lastSlash == -1)
{
return;
}
newDir = newDir.substring(0, lastSlash + 1);
}
else
{
newDir = newDir + dir + "/";
}
changeDir(newDir);
}
void changeDir(String dir)
{
Serial.println("changeDir: " + dir);
currentDir = dir;
files = FileManager::listFiles(currentDir);
updateList();
}
}

View File

@ -1,12 +0,0 @@
#ifndef DRUMZ_VIEWS_FILE_BROWSER_VIEW
#define DRUMZ_VIEWS_FILE_BROWSER_VIEW
#include <lvgl.h>
namespace FileBrowserView
{
void update();
void render(lv_obj_t *parent, lv_indev_t *indev);
}
#endif //DRUMZ_VIEWS_FILE_BROWSER_VIEW

View File

@ -3,7 +3,6 @@
#include <input/encoder.hpp>
#include <audio/tone_generator.hpp>
#include <ui/views/file_browser_view.hpp>
#include <vector>
@ -11,15 +10,61 @@ namespace MainView
{
using std::vector;
String label_text;
void handleEnterButton();
void render(lv_obj_t *parent, lv_indev_t *indev)
lv_obj_t *label;
lv_obj_t *list;
String label_text;
int frequency = ToneGenerator::getFrequency();
void render(lv_obj_t *parent)
{
FileBrowserView::render(parent, indev);
list = lv_list_create(parent);
lv_obj_set_width(list, lv_pct(100));
lv_obj_set_height(list, lv_pct(100));
lv_obj_center(list);
vector<String> items = {
"Michael Jackson",
"The Beatles",
"Rolling Stones",
"Led Zeppelin",
"Queen",
"Prince",
"The Who",
"Nirvana",
"Pink Floyd",
"The Clash"
};
lv_obj_t *btn;
for (String item : items)
{
btn = lv_list_add_btn(list, NULL, item.c_str());
}
//label = lv_label_create(lv_screen_active());
//lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
label_text = "Main view initialized.";
Encoder::onButtonPress(handleEnterButton);
}
void update()
{
FileBrowserView::update();
if (frequency != ToneGenerator::getFrequency())
{
frequency = ToneGenerator::getFrequency();
label_text = "Frequency: " + String(frequency) + "Hz";
}
if (label != NULL && lv_label_get_text(label) != label_text.c_str())
{
lv_label_set_text(label, label_text.c_str());
}
}
void handleEnterButton()
{
label_text = "Button pressed.";
}
}

View File

@ -5,7 +5,7 @@
namespace MainView
{
void render(lv_obj_t *parent, lv_indev_t *indev);
void render(lv_obj_t *parent);
void update();
}