2026-04-11 14:28:34 +08:00
|
|
|
#include <errno.h>
|
2026-04-11 13:41:35 +08:00
|
|
|
#include <stdbool.h>
|
|
|
|
|
|
|
|
|
|
#include <app_event_manager.h>
|
|
|
|
|
|
2026-04-11 14:28:34 +08:00
|
|
|
#define MODULE display_module
|
2026-04-11 13:41:35 +08:00
|
|
|
#include <caf/events/module_state_event.h>
|
|
|
|
|
#include <caf/events/power_event.h>
|
|
|
|
|
|
2026-04-11 14:28:34 +08:00
|
|
|
#include <lvgl_zephyr.h>
|
2026-04-11 13:41:35 +08:00
|
|
|
#include <zephyr/device.h>
|
|
|
|
|
#include <zephyr/drivers/display.h>
|
|
|
|
|
#include <zephyr/drivers/led.h>
|
|
|
|
|
#include <zephyr/logging/log.h>
|
|
|
|
|
|
2026-04-11 16:40:54 +08:00
|
|
|
#include "bat_state_event.h"
|
|
|
|
|
#include "hid_led_event.h"
|
|
|
|
|
#include "mode_switch_event.h"
|
2026-04-13 15:56:45 +08:00
|
|
|
#include "theme_color.h"
|
2026-04-11 14:28:34 +08:00
|
|
|
#include "ui/ui_main.h"
|
2026-04-11 13:41:35 +08:00
|
|
|
|
2026-04-11 14:28:34 +08:00
|
|
|
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
|
2026-04-11 13:41:35 +08:00
|
|
|
|
|
|
|
|
BUILD_ASSERT(DT_HAS_CHOSEN(zephyr_display), "Missing zephyr,display chosen node");
|
|
|
|
|
BUILD_ASSERT(DT_NODE_HAS_STATUS(DT_ALIAS(backlight), okay),
|
|
|
|
|
"Missing backlight alias");
|
|
|
|
|
|
|
|
|
|
static const struct device *const display_dev =
|
|
|
|
|
DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
|
|
|
|
|
static const struct device *const backlight_dev =
|
|
|
|
|
DEVICE_DT_GET(DT_PARENT(DT_ALIAS(backlight)));
|
|
|
|
|
static const uint32_t backlight_idx = DT_NODE_CHILD_IDX(DT_ALIAS(backlight));
|
2026-04-11 16:40:54 +08:00
|
|
|
static struct ui_main_model ui_model = {
|
2026-04-13 15:56:45 +08:00
|
|
|
.theme_color = LV_COLOR_MAKE(BLINKY_THEME_DEFAULT_R,
|
|
|
|
|
BLINKY_THEME_DEFAULT_G,
|
|
|
|
|
BLINKY_THEME_DEFAULT_B),
|
2026-04-11 16:40:54 +08:00
|
|
|
.inactive_border_color = LV_COLOR_MAKE(0x3A, 0x44, 0x52),
|
|
|
|
|
.mode = MODE_SWITCH_BLE,
|
|
|
|
|
};
|
2026-04-11 13:41:35 +08:00
|
|
|
static bool initialized;
|
|
|
|
|
static bool running;
|
2026-04-11 14:28:34 +08:00
|
|
|
static bool lvgl_initialized;
|
2026-04-11 13:41:35 +08:00
|
|
|
|
2026-04-11 14:28:34 +08:00
|
|
|
static int backlight_set(bool on)
|
2026-04-11 13:41:35 +08:00
|
|
|
{
|
2026-04-11 14:28:34 +08:00
|
|
|
if (on) {
|
|
|
|
|
return led_on(backlight_dev, backlight_idx);
|
2026-04-11 13:41:35 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-11 14:28:34 +08:00
|
|
|
return led_off(backlight_dev, backlight_idx);
|
2026-04-11 13:41:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int module_init(void)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
2026-04-11 14:28:34 +08:00
|
|
|
LOG_INF("Display init on %s", display_dev->name);
|
2026-04-11 13:41:35 +08:00
|
|
|
|
|
|
|
|
if (!device_is_ready(display_dev)) {
|
|
|
|
|
LOG_ERR("Display device %s not ready", display_dev->name);
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!device_is_ready(backlight_dev)) {
|
|
|
|
|
LOG_ERR("Backlight device %s not ready", backlight_dev->name);
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 14:28:34 +08:00
|
|
|
err = backlight_set(false);
|
2026-04-11 13:41:35 +08:00
|
|
|
if (err) {
|
|
|
|
|
LOG_ERR("Backlight off failed (%d)", err);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int module_start(void)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (running) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 14:28:34 +08:00
|
|
|
if (!lvgl_initialized) {
|
|
|
|
|
err = lvgl_init();
|
|
|
|
|
if (err) {
|
|
|
|
|
LOG_ERR("lvgl_init failed (%d)", err);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2026-04-11 13:41:35 +08:00
|
|
|
|
2026-04-11 14:28:34 +08:00
|
|
|
lvgl_initialized = true;
|
|
|
|
|
|
|
|
|
|
lvgl_lock();
|
2026-04-11 16:40:54 +08:00
|
|
|
ui_main_init(&ui_model, "WH Mini", "Hello World");
|
2026-04-11 14:28:34 +08:00
|
|
|
lvgl_unlock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = backlight_set(true);
|
2026-04-11 13:41:35 +08:00
|
|
|
if (err) {
|
|
|
|
|
LOG_ERR("Backlight enable failed (%d)", err);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 14:28:34 +08:00
|
|
|
err = display_blanking_off(display_dev);
|
2026-04-11 13:41:35 +08:00
|
|
|
if (err) {
|
2026-04-11 14:28:34 +08:00
|
|
|
LOG_ERR("display_blanking_off failed (%d)", err);
|
|
|
|
|
(void)backlight_set(false);
|
2026-04-11 13:41:35 +08:00
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
running = true;
|
2026-04-11 14:28:34 +08:00
|
|
|
LOG_INF("LVGL display started");
|
2026-04-11 13:41:35 +08:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void module_pause(void)
|
|
|
|
|
{
|
|
|
|
|
if (!running) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(void)display_blanking_on(display_dev);
|
2026-04-11 14:28:34 +08:00
|
|
|
(void)backlight_set(false);
|
2026-04-11 13:41:35 +08:00
|
|
|
running = false;
|
2026-04-11 14:28:34 +08:00
|
|
|
LOG_INF("LVGL display paused");
|
2026-04-11 13:41:35 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-11 16:40:54 +08:00
|
|
|
static void refresh_ui(void)
|
|
|
|
|
{
|
|
|
|
|
if (!lvgl_initialized) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lvgl_lock();
|
|
|
|
|
ui_main_refresh_all(&ui_model, "WH Mini", "Hello World");
|
|
|
|
|
lvgl_unlock();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 13:41:35 +08:00
|
|
|
static bool app_event_handler(const struct app_event_header *aeh)
|
|
|
|
|
{
|
2026-04-11 16:40:54 +08:00
|
|
|
if (is_bat_state_event(aeh)) {
|
|
|
|
|
const struct bat_state_event *event = cast_bat_state_event(aeh);
|
|
|
|
|
|
|
|
|
|
ui_model.battery_level = event->soc;
|
|
|
|
|
ui_model.charging = event->charging;
|
|
|
|
|
ui_model.full = event->full;
|
|
|
|
|
refresh_ui();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_mode_switch_event(aeh)) {
|
|
|
|
|
const struct mode_switch_event *event = cast_mode_switch_event(aeh);
|
|
|
|
|
|
|
|
|
|
ui_model.mode = event->mode;
|
|
|
|
|
refresh_ui();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_hid_led_event(aeh)) {
|
|
|
|
|
const struct hid_led_event *event = cast_hid_led_event(aeh);
|
|
|
|
|
|
|
|
|
|
ui_model.led_mask = event->led_bm;
|
|
|
|
|
refresh_ui();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 13:41:35 +08:00
|
|
|
if (is_module_state_event(aeh)) {
|
|
|
|
|
const struct module_state_event *event = cast_module_state_event(aeh);
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
|
|
|
|
|
if (!initialized) {
|
|
|
|
|
err = module_init();
|
|
|
|
|
if (err) {
|
|
|
|
|
module_set_state(MODULE_STATE_ERROR);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
initialized = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = module_start();
|
|
|
|
|
if (err) {
|
|
|
|
|
module_set_state(MODULE_STATE_ERROR);
|
|
|
|
|
} else {
|
|
|
|
|
module_set_state(MODULE_STATE_READY);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_power_down_event(aeh)) {
|
|
|
|
|
if (initialized) {
|
|
|
|
|
module_pause();
|
|
|
|
|
module_set_state(MODULE_STATE_STANDBY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_wake_up_event(aeh)) {
|
|
|
|
|
if (initialized) {
|
|
|
|
|
int err = module_start();
|
|
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
|
module_set_state(MODULE_STATE_ERROR);
|
|
|
|
|
} else {
|
|
|
|
|
module_set_state(MODULE_STATE_READY);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
APP_EVENT_LISTENER(MODULE, app_event_handler);
|
2026-04-11 16:40:54 +08:00
|
|
|
APP_EVENT_SUBSCRIBE(MODULE, bat_state_event);
|
|
|
|
|
APP_EVENT_SUBSCRIBE(MODULE, hid_led_event);
|
2026-04-11 13:41:35 +08:00
|
|
|
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
|
2026-04-11 16:40:54 +08:00
|
|
|
APP_EVENT_SUBSCRIBE(MODULE, mode_switch_event);
|
2026-04-11 13:41:35 +08:00
|
|
|
APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event);
|
|
|
|
|
APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);
|