ESP32: зафиксировать рабочий LVGL nav prototype и тесты
This commit is contained in:
parent
a8734846a0
commit
32606fe1c2
@ -0,0 +1,12 @@
|
|||||||
|
# LVGL Russian font test
|
||||||
|
|
||||||
|
- Краткое описание: тест кастомного `LVGL`-шрифта с кириллицей на базе рабочего `LVGL + subserver touch` контура.
|
||||||
|
- Что проверять:
|
||||||
|
- на экране видны русские заголовки и подписи без транслита;
|
||||||
|
- отображаются буквы `Ё/ё`;
|
||||||
|
- видны кнопки `Статус`, `Подключение`, `Кошелёк`, `Запросы`, `Настройки`, `Регистрация`, `Разрешить`, `Отклонить`, `Назад`;
|
||||||
|
- длинная кнопка `Проверка переноса русского текста` отображается читаемо;
|
||||||
|
- строка `Нажато:` меняется при клике;
|
||||||
|
- строка `Касание:` меняется при касании.
|
||||||
|
- Ожидаемый результат: кириллица стабильно отображается на `LVGL`-экране и не ломает touch.
|
||||||
|
- Статус: pending
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
# ESP32 nav minimal test
|
||||||
|
|
||||||
|
- Краткое описание: новый минимальный навигационный UI-прототип для сабсервера на базе рабочего `LVGL + subserver touch`.
|
||||||
|
- Что проверять:
|
||||||
|
- стартует экран `HOME`;
|
||||||
|
- на `HOME` видны `login`, `subserver1`, `W BAT`, `STATUS`, кнопка `SETTINGS`;
|
||||||
|
- кнопка `SETTINGS` открывает `SETTINGS_MENU`;
|
||||||
|
- свайп влево на `HOME` открывает `SETTINGS_MENU`;
|
||||||
|
- в `SETTINGS_MENU` сначала видны только `Wi-Fi` и `Server`;
|
||||||
|
- свайп вверх показывает `Server` и `Account`;
|
||||||
|
- свайп вниз возвращает `Wi-Fi` и `Server`;
|
||||||
|
- свайп вправо из `SETTINGS_MENU` возвращает на `HOME`;
|
||||||
|
- нажатия по `Wi-Fi`, `Server`, `Account` открывают соответствующие экраны;
|
||||||
|
- свайп вправо из внутренних экранов возвращает в `SETTINGS_MENU`;
|
||||||
|
- если во время реального свайпа палец проходит по кнопке, это не должно открывать кнопку как обычный `click`.
|
||||||
|
- Ожидаемый результат: новый скетч даёт чистый навигационный каркас без старой перегруженной логики.
|
||||||
|
- Статус: pending
|
||||||
@ -0,0 +1,123 @@
|
|||||||
|
# SHiNE ESP32 Subserver UI Nav Minimal Spec
|
||||||
|
|
||||||
|
Минимальный навигационный прототип для `Waveshare ESP32-S3-Touch-AMOLED-2.16`.
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Этот прототип проверяет только базовую механику экранов, крупных кнопок и свайпов.
|
||||||
|
|
||||||
|
На этом этапе отсутствуют:
|
||||||
|
- реальный Wi-Fi;
|
||||||
|
- реальные серверные проверки;
|
||||||
|
- логин/пароль;
|
||||||
|
- PIN;
|
||||||
|
- кошелёк;
|
||||||
|
- QR;
|
||||||
|
- баланс;
|
||||||
|
- регистрация;
|
||||||
|
- PDA и транзакции;
|
||||||
|
- входящие запросы.
|
||||||
|
|
||||||
|
## Экраны
|
||||||
|
|
||||||
|
Прототип содержит 5 экранов:
|
||||||
|
- `HOME`
|
||||||
|
- `SETTINGS_MENU`
|
||||||
|
- `WIFI_SCREEN`
|
||||||
|
- `SERVER_SCREEN`
|
||||||
|
- `ACCOUNT_SCREEN`
|
||||||
|
|
||||||
|
## HOME
|
||||||
|
|
||||||
|
Показывает:
|
||||||
|
- сверху слева `login`;
|
||||||
|
- ниже `subserver1`;
|
||||||
|
- сверху справа простые индикаторы `W BAT`;
|
||||||
|
- по центру крупный текст `STATUS`;
|
||||||
|
- снизу большую кнопку `SETTINGS`.
|
||||||
|
|
||||||
|
Переходы:
|
||||||
|
- кнопка `SETTINGS` -> `SETTINGS_MENU`;
|
||||||
|
- свайп влево -> `SETTINGS_MENU`.
|
||||||
|
|
||||||
|
## SETTINGS_MENU
|
||||||
|
|
||||||
|
Показывает вертикальное меню из 3 пунктов:
|
||||||
|
- `Wi-Fi`
|
||||||
|
- `Server`
|
||||||
|
- `Account`
|
||||||
|
|
||||||
|
Одновременно видны только 2 пункта.
|
||||||
|
|
||||||
|
Начальное состояние:
|
||||||
|
- `Wi-Fi`
|
||||||
|
- `Server`
|
||||||
|
|
||||||
|
После свайпа вверх:
|
||||||
|
- `Server`
|
||||||
|
- `Account`
|
||||||
|
|
||||||
|
После свайпа вниз:
|
||||||
|
- `Wi-Fi`
|
||||||
|
- `Server`
|
||||||
|
|
||||||
|
Переходы:
|
||||||
|
- нажатие `Wi-Fi` -> `WIFI_SCREEN`;
|
||||||
|
- нажатие `Server` -> `SERVER_SCREEN`;
|
||||||
|
- нажатие `Account` -> `ACCOUNT_SCREEN`;
|
||||||
|
- свайп вправо -> `HOME`.
|
||||||
|
|
||||||
|
## WIFI_SCREEN
|
||||||
|
|
||||||
|
Показывает:
|
||||||
|
- `Wi-Fi`
|
||||||
|
- `Wi-Fi screen`
|
||||||
|
|
||||||
|
Переходы:
|
||||||
|
- свайп вправо -> `SETTINGS_MENU`
|
||||||
|
- кнопка `BACK` -> `SETTINGS_MENU`
|
||||||
|
|
||||||
|
## SERVER_SCREEN
|
||||||
|
|
||||||
|
Показывает:
|
||||||
|
- `Server`
|
||||||
|
- `Server screen`
|
||||||
|
|
||||||
|
Переходы:
|
||||||
|
- свайп вправо -> `SETTINGS_MENU`
|
||||||
|
- кнопка `BACK` -> `SETTINGS_MENU`
|
||||||
|
|
||||||
|
## ACCOUNT_SCREEN
|
||||||
|
|
||||||
|
Показывает:
|
||||||
|
- `Account`
|
||||||
|
- `Account screen`
|
||||||
|
|
||||||
|
Переходы:
|
||||||
|
- свайп вправо -> `SETTINGS_MENU`
|
||||||
|
- кнопка `BACK` -> `SETTINGS_MENU`
|
||||||
|
|
||||||
|
## Жесты
|
||||||
|
|
||||||
|
Поддерживаются направления:
|
||||||
|
- `SWIPE_LEFT`
|
||||||
|
- `SWIPE_RIGHT`
|
||||||
|
- `SWIPE_UP`
|
||||||
|
- `SWIPE_DOWN`
|
||||||
|
|
||||||
|
Маршруты:
|
||||||
|
- `HOME`: свайп влево -> `SETTINGS_MENU`
|
||||||
|
- `SETTINGS_MENU`: свайп вправо -> `HOME`
|
||||||
|
- `SETTINGS_MENU`: свайп вверх/вниз -> прокрутка списка
|
||||||
|
- `WIFI_SCREEN`: свайп вправо -> `SETTINGS_MENU`
|
||||||
|
- `SERVER_SCREEN`: свайп вправо -> `SETTINGS_MENU`
|
||||||
|
- `ACCOUNT_SCREEN`: свайп вправо -> `SETTINGS_MENU`
|
||||||
|
|
||||||
|
## Особенность обработки жестов
|
||||||
|
|
||||||
|
- если палец ушёл из зоны обычного тапа и жест определяется как свайп, случайное попадание на кнопку по пути не должно превращать свайп в нажатие;
|
||||||
|
- приоритет у свайпа, а не у `click`.
|
||||||
|
|
||||||
|
## Язык
|
||||||
|
|
||||||
|
Пока интерфейс отображается в ASCII/English, чтобы использовать подтверждённый рабочий шрифтовой путь на устройстве.
|
||||||
@ -19,6 +19,8 @@
|
|||||||
- `lvgl-touch-debug-test` — точечная диагностика touch: сырые координаты, маркер точки и большая тест-кнопка `LVGL`
|
- `lvgl-touch-debug-test` — точечная диагностика touch: сырые координаты, маркер точки и большая тест-кнопка `LVGL`
|
||||||
- `lvgl-official-based-test` — наш минимальный экран, но на максимально близкой к официальному `LVGL_Widgets` инициализации
|
- `lvgl-official-based-test` — наш минимальный экран, но на максимально близкой к официальному `LVGL_Widgets` инициализации
|
||||||
- `lvgl-subserver-touch-test` — гибрид: `LVGL`-интерфейс, но display/touch init и raw touch-read взяты из `shine_subserver_ui`; подтверждено на устройстве, touch работает, зелёных линий по краям нет
|
- `lvgl-subserver-touch-test` — гибрид: `LVGL`-интерфейс, но display/touch init и raw touch-read взяты из `shine_subserver_ui`; подтверждено на устройстве, touch работает, зелёных линий по краям нет
|
||||||
|
- `lvgl-russian-font-test` — тест кастомного `LVGL`-шрифта с кириллицей: русские кнопки, длинные подписи и статусы
|
||||||
|
- `lvgl-nav-minimal-test` — новый минимальный UI-каркас сабсервера: `HOME`, `SETTINGS_MENU`, `Wi-Fi`, `Server`, `Account`, свайпы и крупные кнопки
|
||||||
|
|
||||||
Запуск:
|
Запуск:
|
||||||
|
|
||||||
@ -35,4 +37,6 @@
|
|||||||
- `./burn.sh lvgl-touch-debug-test`
|
- `./burn.sh lvgl-touch-debug-test`
|
||||||
- `./burn.sh lvgl-official-based-test`
|
- `./burn.sh lvgl-official-based-test`
|
||||||
- `./burn.sh lvgl-subserver-touch-test`
|
- `./burn.sh lvgl-subserver-touch-test`
|
||||||
|
- `./burn.sh lvgl-russian-font-test`
|
||||||
|
- `./burn.sh lvgl-nav-minimal-test`
|
||||||
- `./flash_shine_subserver_ui.sh` - автоматически находит USB-порт и заливает `shine_subserver_ui`
|
- `./flash_shine_subserver_ui.sh` - автоматически находит USB-порт и заливает `shine_subserver_ui`
|
||||||
|
|||||||
@ -25,9 +25,11 @@ case "${MODE}" in
|
|||||||
lvgl-touch-debug-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_touch_debug_test" ;;
|
lvgl-touch-debug-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_touch_debug_test" ;;
|
||||||
lvgl-official-based-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_official_based_test" ;;
|
lvgl-official-based-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_official_based_test" ;;
|
||||||
lvgl-subserver-touch-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_subserver_touch_test" ;;
|
lvgl-subserver-touch-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_subserver_touch_test" ;;
|
||||||
|
lvgl-russian-font-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_russian_font_test" ;;
|
||||||
|
lvgl-nav-minimal-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_nav_minimal_test" ;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown mode: ${MODE}" >&2
|
echo "Unknown mode: ${MODE}" >&2
|
||||||
echo "Use one of: hello, widgets, audio, simple, argon2, subserver-ui, text-test, gfx-text-test, gfx-layout-test, lvgl-basic-test, lvgl-interaction-test, lvgl-touch-debug-test, lvgl-official-based-test, lvgl-subserver-touch-test" >&2
|
echo "Use one of: hello, widgets, audio, simple, argon2, subserver-ui, text-test, gfx-text-test, gfx-layout-test, lvgl-basic-test, lvgl-interaction-test, lvgl-touch-debug-test, lvgl-official-based-test, lvgl-subserver-touch-test, lvgl-russian-font-test, lvgl-nav-minimal-test" >&2
|
||||||
exit 2
|
exit 2
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@ -13,6 +13,8 @@
|
|||||||
- `lvgl_touch_debug_test/` - диагностика touch: сырые координаты, точка касания и одна большая кнопка `LVGL`
|
- `lvgl_touch_debug_test/` - диагностика touch: сырые координаты, точка касания и одна большая кнопка `LVGL`
|
||||||
- `lvgl_official_based_test/` - минимальный наш экран поверх максимально близкой к официальному `05_LVGL_Widgets` инициализации
|
- `lvgl_official_based_test/` - минимальный наш экран поверх максимально близкой к официальному `05_LVGL_Widgets` инициализации
|
||||||
- `lvgl_subserver_touch_test/` - гибридный тест: `LVGL`-экран с инициализацией дисплея и чтением touch из `shine_subserver_ui`; подтверждён на реальном устройстве
|
- `lvgl_subserver_touch_test/` - гибридный тест: `LVGL`-экран с инициализацией дисплея и чтением touch из `shine_subserver_ui`; подтверждён на реальном устройстве
|
||||||
|
- `lvgl_russian_font_test/` - тест кастомного кириллического `LVGL`-шрифта с русскими кнопками, длинными строками и рабочим touch
|
||||||
|
- `lvgl_nav_minimal_test/` - новый минимальный навигационный каркас сабсервера на рабочем `LVGL + subserver touch`
|
||||||
|
|
||||||
## Запуск
|
## Запуск
|
||||||
|
|
||||||
@ -25,3 +27,5 @@
|
|||||||
- `./burn.sh lvgl-touch-debug-test`
|
- `./burn.sh lvgl-touch-debug-test`
|
||||||
- `./burn.sh lvgl-official-based-test`
|
- `./burn.sh lvgl-official-based-test`
|
||||||
- `./burn.sh lvgl-subserver-touch-test`
|
- `./burn.sh lvgl-subserver-touch-test`
|
||||||
|
- `./burn.sh lvgl-russian-font-test`
|
||||||
|
- `./burn.sh lvgl-nav-minimal-test`
|
||||||
|
|||||||
@ -0,0 +1,499 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <lvgl.h>
|
||||||
|
#include <Arduino_GFX_Library.h>
|
||||||
|
#include <TouchDrvCSTXXX.hpp>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define PIN_LCD_CS 12
|
||||||
|
#define PIN_LCD_SCLK 38
|
||||||
|
#define PIN_LCD_D0 4
|
||||||
|
#define PIN_LCD_D1 5
|
||||||
|
#define PIN_LCD_D2 6
|
||||||
|
#define PIN_LCD_D3 7
|
||||||
|
#define PIN_LCD_RST 2
|
||||||
|
#define PIN_I2C_SDA 15
|
||||||
|
#define PIN_I2C_SCL 14
|
||||||
|
#define PIN_TP_INT 11
|
||||||
|
|
||||||
|
#define DISP_W 480
|
||||||
|
#define DISP_H 480
|
||||||
|
#define LVGL_TICK_MS 2
|
||||||
|
#define SWIPE_THRESHOLD 48
|
||||||
|
#define TAP_CANCEL_THRESHOLD 18
|
||||||
|
#define TEST_VERSION "NAV v2"
|
||||||
|
|
||||||
|
enum Screen {
|
||||||
|
SCREEN_HOME,
|
||||||
|
SCREEN_SETTINGS_MENU,
|
||||||
|
SCREEN_WIFI,
|
||||||
|
SCREEN_SERVER,
|
||||||
|
SCREEN_ACCOUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SwipeDirection {
|
||||||
|
SWIPE_NONE,
|
||||||
|
SWIPE_LEFT,
|
||||||
|
SWIPE_RIGHT,
|
||||||
|
SWIPE_UP,
|
||||||
|
SWIPE_DOWN,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ActionId {
|
||||||
|
ACTION_NONE,
|
||||||
|
ACTION_OPEN_SETTINGS,
|
||||||
|
ACTION_OPEN_WIFI,
|
||||||
|
ACTION_OPEN_SERVER,
|
||||||
|
ACTION_OPEN_ACCOUNT,
|
||||||
|
ACTION_BACK_HOME,
|
||||||
|
ACTION_BACK_SETTINGS,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *kMenuItems[] = {"Wi-Fi", "Server", "Account"};
|
||||||
|
static const size_t kMenuCount = sizeof(kMenuItems) / sizeof(kMenuItems[0]);
|
||||||
|
|
||||||
|
static lv_disp_draw_buf_t gDrawBuf;
|
||||||
|
static lv_color_t *gBuf1 = nullptr;
|
||||||
|
static lv_color_t *gBuf2 = nullptr;
|
||||||
|
static lv_obj_t *gRoot = nullptr;
|
||||||
|
|
||||||
|
Arduino_DataBus *gBus = new Arduino_ESP32QSPI(
|
||||||
|
PIN_LCD_CS, PIN_LCD_SCLK, PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3);
|
||||||
|
Arduino_CO5300 *gfx = new Arduino_CO5300(
|
||||||
|
gBus, PIN_LCD_RST, 0, DISP_W, DISP_H, 0, 0, 0, 0);
|
||||||
|
TouchDrvCST92xx gTouch;
|
||||||
|
|
||||||
|
static Screen gCurrentScreen = SCREEN_HOME;
|
||||||
|
static int gSettingsScrollIndex = 0;
|
||||||
|
static bool gTouchDown = false;
|
||||||
|
static int16_t gTouchStartX = 0;
|
||||||
|
static int16_t gTouchStartY = 0;
|
||||||
|
static int16_t gTouchLastX = 0;
|
||||||
|
static int16_t gTouchLastY = 0;
|
||||||
|
static SwipeDirection gPendingSwipe = SWIPE_NONE;
|
||||||
|
static bool gBlockClick = false;
|
||||||
|
|
||||||
|
static void rebuildScreen();
|
||||||
|
static void showScreen(Screen screen);
|
||||||
|
static void handleSwipe(SwipeDirection swipe);
|
||||||
|
|
||||||
|
static void lvglFlushCb(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *colorP) {
|
||||||
|
uint32_t w = area->x2 - area->x1 + 1;
|
||||||
|
uint32_t h = area->y2 - area->y1 + 1;
|
||||||
|
#if (LV_COLOR_16_SWAP != 0)
|
||||||
|
gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&colorP->full, w, h);
|
||||||
|
#else
|
||||||
|
gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&colorP->full, w, h);
|
||||||
|
#endif
|
||||||
|
lv_disp_flush_ready(disp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lvglTick(void *arg) {
|
||||||
|
LV_UNUSED(arg);
|
||||||
|
lv_tick_inc(LVGL_TICK_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SwipeDirection detectSwipe(int16_t startX, int16_t startY, int16_t endX, int16_t endY) {
|
||||||
|
int16_t dx = endX - startX;
|
||||||
|
int16_t dy = endY - startY;
|
||||||
|
int16_t absDx = abs(dx);
|
||||||
|
int16_t absDy = abs(dy);
|
||||||
|
|
||||||
|
if (absDx < SWIPE_THRESHOLD && absDy < SWIPE_THRESHOLD) {
|
||||||
|
return SWIPE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (absDx >= absDy) {
|
||||||
|
return dx > 0 ? SWIPE_RIGHT : SWIPE_LEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dy > 0 ? SWIPE_DOWN : SWIPE_UP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool movedTooFarForTap(int16_t startX, int16_t startY, int16_t endX, int16_t endY) {
|
||||||
|
return abs(endX - startX) >= TAP_CANCEL_THRESHOLD || abs(endY - startY) >= TAP_CANCEL_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lvglTouchRead(lv_indev_drv_t *indevDrv, lv_indev_data_t *data) {
|
||||||
|
LV_UNUSED(indevDrv);
|
||||||
|
int16_t x = 0;
|
||||||
|
int16_t y = 0;
|
||||||
|
bool touching = gTouch.getPoint(&x, &y, 1) > 0;
|
||||||
|
|
||||||
|
if (touching) {
|
||||||
|
if (!gTouchDown) {
|
||||||
|
gTouchDown = true;
|
||||||
|
gTouchStartX = x;
|
||||||
|
gTouchStartY = y;
|
||||||
|
}
|
||||||
|
gTouchLastX = x;
|
||||||
|
gTouchLastY = y;
|
||||||
|
if (movedTooFarForTap(gTouchStartX, gTouchStartY, gTouchLastX, gTouchLastY)) {
|
||||||
|
gBlockClick = true;
|
||||||
|
}
|
||||||
|
data->state = LV_INDEV_STATE_PR;
|
||||||
|
data->point.x = x;
|
||||||
|
data->point.y = y;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gTouchDown) {
|
||||||
|
gTouchDown = false;
|
||||||
|
gPendingSwipe = detectSwipe(gTouchStartX, gTouchStartY, gTouchLastX, gTouchLastY);
|
||||||
|
if (gPendingSwipe == SWIPE_NONE && !movedTooFarForTap(gTouchStartX, gTouchStartY, gTouchLastX, gTouchLastY)) {
|
||||||
|
gBlockClick = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data->state = LV_INDEV_STATE_REL;
|
||||||
|
data->point.x = gTouchLastX;
|
||||||
|
data->point.y = gTouchLastY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setRootStyle() {
|
||||||
|
lv_obj_set_style_bg_color(gRoot, lv_color_hex(0x08111B), 0);
|
||||||
|
lv_obj_set_style_bg_opa(gRoot, LV_OPA_COVER, 0);
|
||||||
|
lv_obj_set_style_pad_all(gRoot, 0, 0);
|
||||||
|
lv_obj_set_scrollbar_mode(gRoot, LV_SCROLLBAR_MODE_OFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
static lv_obj_t *makeTitle(const char *text, lv_coord_t y, const lv_font_t *font) {
|
||||||
|
lv_obj_t *label = lv_label_create(gRoot);
|
||||||
|
lv_label_set_text(label, text);
|
||||||
|
lv_obj_set_style_text_font(label, font, 0);
|
||||||
|
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0);
|
||||||
|
lv_obj_align(label, LV_ALIGN_TOP_MID, 0, y);
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
static lv_obj_t *makeBody(const char *text, lv_coord_t y, lv_coord_t width) {
|
||||||
|
lv_obj_t *label = lv_label_create(gRoot);
|
||||||
|
lv_label_set_text(label, text);
|
||||||
|
lv_obj_set_width(label, width);
|
||||||
|
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
|
||||||
|
lv_obj_set_style_text_font(label, &lv_font_montserrat_18, 0);
|
||||||
|
lv_obj_set_style_text_color(label, lv_color_hex(0xD9E1EA), 0);
|
||||||
|
lv_obj_align(label, LV_ALIGN_TOP_MID, 0, y);
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void actionButtonCb(lv_event_t *event) {
|
||||||
|
if (lv_event_get_code(event) != LV_EVENT_CLICKED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gBlockClick) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionId action = static_cast<ActionId>(reinterpret_cast<uintptr_t>(lv_event_get_user_data(event)));
|
||||||
|
switch (action) {
|
||||||
|
case ACTION_OPEN_SETTINGS:
|
||||||
|
showScreen(SCREEN_SETTINGS_MENU);
|
||||||
|
break;
|
||||||
|
case ACTION_OPEN_WIFI:
|
||||||
|
showScreen(SCREEN_WIFI);
|
||||||
|
break;
|
||||||
|
case ACTION_OPEN_SERVER:
|
||||||
|
showScreen(SCREEN_SERVER);
|
||||||
|
break;
|
||||||
|
case ACTION_OPEN_ACCOUNT:
|
||||||
|
showScreen(SCREEN_ACCOUNT);
|
||||||
|
break;
|
||||||
|
case ACTION_BACK_HOME:
|
||||||
|
showScreen(SCREEN_HOME);
|
||||||
|
break;
|
||||||
|
case ACTION_BACK_SETTINGS:
|
||||||
|
showScreen(SCREEN_SETTINGS_MENU);
|
||||||
|
break;
|
||||||
|
case ACTION_NONE:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static lv_obj_t *makeButton(const char *text,
|
||||||
|
lv_coord_t x,
|
||||||
|
lv_coord_t y,
|
||||||
|
lv_coord_t w,
|
||||||
|
lv_coord_t h,
|
||||||
|
uint32_t bgColor,
|
||||||
|
ActionId action,
|
||||||
|
const lv_font_t *font = &lv_font_montserrat_22) {
|
||||||
|
lv_obj_t *btn = lv_btn_create(gRoot);
|
||||||
|
lv_obj_set_size(btn, w, h);
|
||||||
|
lv_obj_set_pos(btn, x, y);
|
||||||
|
lv_obj_set_style_radius(btn, 18, 0);
|
||||||
|
lv_obj_set_style_bg_color(btn, lv_color_hex(bgColor), 0);
|
||||||
|
lv_obj_set_style_bg_opa(btn, LV_OPA_COVER, 0);
|
||||||
|
lv_obj_set_style_border_width(btn, 2, 0);
|
||||||
|
lv_obj_set_style_border_color(btn, lv_color_hex(0x6E8AA3), 0);
|
||||||
|
lv_obj_set_style_shadow_width(btn, 0, 0);
|
||||||
|
lv_obj_add_event_cb(btn, actionButtonCb, LV_EVENT_CLICKED, reinterpret_cast<void *>(static_cast<uintptr_t>(action)));
|
||||||
|
|
||||||
|
lv_obj_t *label = lv_label_create(btn);
|
||||||
|
lv_label_set_text(label, text);
|
||||||
|
lv_obj_set_style_text_font(label, font, 0);
|
||||||
|
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0);
|
||||||
|
lv_obj_center(label);
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void makeTopIcons() {
|
||||||
|
lv_obj_t *icons = lv_label_create(gRoot);
|
||||||
|
lv_label_set_text(icons, "W BAT");
|
||||||
|
lv_obj_set_style_text_font(icons, &lv_font_montserrat_18, 0);
|
||||||
|
lv_obj_set_style_text_color(icons, lv_color_hex(0xC9D3DE), 0);
|
||||||
|
lv_obj_align(icons, LV_ALIGN_TOP_RIGHT, -24, 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void makeVersionTag() {
|
||||||
|
lv_obj_t *tag = lv_label_create(gRoot);
|
||||||
|
lv_label_set_text(tag, TEST_VERSION);
|
||||||
|
lv_obj_set_style_text_font(tag, &lv_font_montserrat_14, 0);
|
||||||
|
lv_obj_set_style_text_color(tag, lv_color_hex(0x8398AD), 0);
|
||||||
|
lv_obj_align(tag, LV_ALIGN_BOTTOM_MID, 0, -10);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawHome() {
|
||||||
|
setRootStyle();
|
||||||
|
|
||||||
|
lv_obj_t *login = lv_label_create(gRoot);
|
||||||
|
lv_label_set_text(login, "login");
|
||||||
|
lv_obj_set_style_text_font(login, &lv_font_montserrat_18, 0);
|
||||||
|
lv_obj_set_style_text_color(login, lv_color_hex(0xFFFFFF), 0);
|
||||||
|
lv_obj_align(login, LV_ALIGN_TOP_LEFT, 24, 18);
|
||||||
|
|
||||||
|
lv_obj_t *subserver = lv_label_create(gRoot);
|
||||||
|
lv_label_set_text(subserver, "subserver1");
|
||||||
|
lv_obj_set_style_text_font(subserver, &lv_font_montserrat_18, 0);
|
||||||
|
lv_obj_set_style_text_color(subserver, lv_color_hex(0xD5DEE7), 0);
|
||||||
|
lv_obj_align_to(subserver, login, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 6);
|
||||||
|
|
||||||
|
makeTopIcons();
|
||||||
|
makeTitle("STATUS", 165, &lv_font_montserrat_28);
|
||||||
|
makeBody("Swipe left or tap the button below.", 214, 360);
|
||||||
|
makeButton("SETTINGS", 22, 360, 436, 78, 0x2A6F97, ACTION_OPEN_SETTINGS, &lv_font_montserrat_24);
|
||||||
|
makeVersionTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawSettingsMenu() {
|
||||||
|
setRootStyle();
|
||||||
|
|
||||||
|
makeTitle("SETTINGS", 18, &lv_font_montserrat_24);
|
||||||
|
makeBody("Swipe up/down to move. Swipe right to go home.", 56, 420);
|
||||||
|
|
||||||
|
for (int visibleIndex = 0; visibleIndex < 2; ++visibleIndex) {
|
||||||
|
int itemIndex = gSettingsScrollIndex + visibleIndex;
|
||||||
|
if (itemIndex >= static_cast<int>(kMenuCount)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionId action = ACTION_NONE;
|
||||||
|
if (itemIndex == 0) action = ACTION_OPEN_WIFI;
|
||||||
|
if (itemIndex == 1) action = ACTION_OPEN_SERVER;
|
||||||
|
if (itemIndex == 2) action = ACTION_OPEN_ACCOUNT;
|
||||||
|
|
||||||
|
makeButton(kMenuItems[itemIndex], 22, 132 + visibleIndex * 126, 436, 104,
|
||||||
|
visibleIndex == 0 ? 0x355C7D : 0x2A9D8F, action, &lv_font_montserrat_24);
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_obj_t *hint = lv_label_create(gRoot);
|
||||||
|
char hintText[48];
|
||||||
|
snprintf(hintText, sizeof(hintText), "Items %d-%d of %d",
|
||||||
|
gSettingsScrollIndex + 1,
|
||||||
|
min(gSettingsScrollIndex + 2, static_cast<int>(kMenuCount)),
|
||||||
|
static_cast<int>(kMenuCount));
|
||||||
|
lv_label_set_text(hint, hintText);
|
||||||
|
lv_obj_set_style_text_font(hint, &lv_font_montserrat_16, 0);
|
||||||
|
lv_obj_set_style_text_color(hint, lv_color_hex(0xA8B8C7), 0);
|
||||||
|
lv_obj_align(hint, LV_ALIGN_BOTTOM_MID, 0, -34);
|
||||||
|
|
||||||
|
makeVersionTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawSimpleScreen(const char *title, const char *body) {
|
||||||
|
setRootStyle();
|
||||||
|
makeTitle(title, 56, &lv_font_montserrat_28);
|
||||||
|
makeBody(body, 132, 400);
|
||||||
|
makeBody("Swipe right to return to Settings.", 204, 400);
|
||||||
|
makeButton("BACK", 140, 344, 200, 72, 0x5A6570, ACTION_BACK_SETTINGS, &lv_font_montserrat_22);
|
||||||
|
makeVersionTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawWifiScreen() {
|
||||||
|
drawSimpleScreen("Wi-Fi", "Wi-Fi screen");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawServerScreen() {
|
||||||
|
drawSimpleScreen("Server", "Server screen");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawAccountScreen() {
|
||||||
|
drawSimpleScreen("Account", "Account screen");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rebuildScreen() {
|
||||||
|
if (!gRoot) {
|
||||||
|
gRoot = lv_scr_act();
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_obj_clean(gRoot);
|
||||||
|
|
||||||
|
switch (gCurrentScreen) {
|
||||||
|
case SCREEN_HOME:
|
||||||
|
drawHome();
|
||||||
|
break;
|
||||||
|
case SCREEN_SETTINGS_MENU:
|
||||||
|
drawSettingsMenu();
|
||||||
|
break;
|
||||||
|
case SCREEN_WIFI:
|
||||||
|
drawWifiScreen();
|
||||||
|
break;
|
||||||
|
case SCREEN_SERVER:
|
||||||
|
drawServerScreen();
|
||||||
|
break;
|
||||||
|
case SCREEN_ACCOUNT:
|
||||||
|
drawAccountScreen();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void showScreen(Screen screen) {
|
||||||
|
gCurrentScreen = screen;
|
||||||
|
rebuildScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleHomeSwipe(SwipeDirection swipe) {
|
||||||
|
if (swipe == SWIPE_LEFT) {
|
||||||
|
showScreen(SCREEN_SETTINGS_MENU);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleSettingsSwipe(SwipeDirection swipe) {
|
||||||
|
if (swipe == SWIPE_RIGHT) {
|
||||||
|
showScreen(SCREEN_HOME);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (swipe == SWIPE_UP) {
|
||||||
|
gSettingsScrollIndex++;
|
||||||
|
if (gSettingsScrollIndex > static_cast<int>(kMenuCount) - 2) {
|
||||||
|
gSettingsScrollIndex = static_cast<int>(kMenuCount) - 2;
|
||||||
|
}
|
||||||
|
rebuildScreen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (swipe == SWIPE_DOWN) {
|
||||||
|
gSettingsScrollIndex--;
|
||||||
|
if (gSettingsScrollIndex < 0) {
|
||||||
|
gSettingsScrollIndex = 0;
|
||||||
|
}
|
||||||
|
rebuildScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleWifiSwipe(SwipeDirection swipe) {
|
||||||
|
if (swipe == SWIPE_RIGHT) {
|
||||||
|
showScreen(SCREEN_SETTINGS_MENU);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleServerSwipe(SwipeDirection swipe) {
|
||||||
|
if (swipe == SWIPE_RIGHT) {
|
||||||
|
showScreen(SCREEN_SETTINGS_MENU);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleAccountSwipe(SwipeDirection swipe) {
|
||||||
|
if (swipe == SWIPE_RIGHT) {
|
||||||
|
showScreen(SCREEN_SETTINGS_MENU);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleSwipe(SwipeDirection swipe) {
|
||||||
|
if (swipe == SWIPE_NONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (gCurrentScreen) {
|
||||||
|
case SCREEN_HOME:
|
||||||
|
handleHomeSwipe(swipe);
|
||||||
|
break;
|
||||||
|
case SCREEN_SETTINGS_MENU:
|
||||||
|
handleSettingsSwipe(swipe);
|
||||||
|
break;
|
||||||
|
case SCREEN_WIFI:
|
||||||
|
handleWifiSwipe(swipe);
|
||||||
|
break;
|
||||||
|
case SCREEN_SERVER:
|
||||||
|
handleServerSwipe(swipe);
|
||||||
|
break;
|
||||||
|
case SCREEN_ACCOUNT:
|
||||||
|
handleAccountSwipe(swipe);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);
|
||||||
|
|
||||||
|
gfx->begin();
|
||||||
|
gBus->writeC8D8(0x36, 0xA0);
|
||||||
|
gfx->setBrightness(220);
|
||||||
|
gfx->fillScreen(0x0000);
|
||||||
|
|
||||||
|
gTouch.setPins(PIN_TP_INT, -1);
|
||||||
|
gTouch.begin(Wire, CST92XX_SLAVE_ADDRESS, PIN_I2C_SDA, PIN_I2C_SCL);
|
||||||
|
gTouch.setMaxCoordinates(DISP_W, DISP_H);
|
||||||
|
gTouch.setSwapXY(true);
|
||||||
|
gTouch.setMirrorXY(true, false);
|
||||||
|
|
||||||
|
lv_init();
|
||||||
|
|
||||||
|
uint32_t screenWidth = gfx->width();
|
||||||
|
uint32_t screenHeight = gfx->height();
|
||||||
|
uint32_t bufPixels = screenWidth * 40;
|
||||||
|
gBuf1 = (lv_color_t *)heap_caps_malloc(bufPixels * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||||
|
gBuf2 = (lv_color_t *)heap_caps_malloc(bufPixels * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||||
|
lv_disp_draw_buf_init(&gDrawBuf, gBuf1, gBuf2, bufPixels);
|
||||||
|
|
||||||
|
static lv_disp_drv_t dispDrv;
|
||||||
|
lv_disp_drv_init(&dispDrv);
|
||||||
|
dispDrv.hor_res = screenWidth;
|
||||||
|
dispDrv.ver_res = screenHeight;
|
||||||
|
dispDrv.flush_cb = lvglFlushCb;
|
||||||
|
dispDrv.draw_buf = &gDrawBuf;
|
||||||
|
lv_disp_drv_register(&dispDrv);
|
||||||
|
|
||||||
|
static lv_indev_drv_t indevDrv;
|
||||||
|
lv_indev_drv_init(&indevDrv);
|
||||||
|
indevDrv.type = LV_INDEV_TYPE_POINTER;
|
||||||
|
indevDrv.read_cb = lvglTouchRead;
|
||||||
|
lv_indev_drv_register(&indevDrv);
|
||||||
|
|
||||||
|
const esp_timer_create_args_t tickArgs = {
|
||||||
|
.callback = &lvglTick,
|
||||||
|
.name = "lvgl_tick"};
|
||||||
|
esp_timer_handle_t tickTimer = nullptr;
|
||||||
|
esp_timer_create(&tickArgs, &tickTimer);
|
||||||
|
esp_timer_start_periodic(tickTimer, LVGL_TICK_MS * 1000);
|
||||||
|
|
||||||
|
rebuildScreen();
|
||||||
|
Serial.println("Minimal nav test ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
lv_timer_handler();
|
||||||
|
|
||||||
|
if (gPendingSwipe != SWIPE_NONE) {
|
||||||
|
SwipeDirection swipe = gPendingSwipe;
|
||||||
|
gPendingSwipe = SWIPE_NONE;
|
||||||
|
handleSwipe(swipe);
|
||||||
|
gBlockClick = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(5);
|
||||||
|
}
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <lvgl.h>
|
||||||
|
LV_FONT_DECLARE(lv_font_ru_18);
|
||||||
|
LV_FONT_DECLARE(lv_font_ru_24);
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,264 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <lvgl.h>
|
||||||
|
#include <Arduino_GFX_Library.h>
|
||||||
|
#include <TouchDrvCSTXXX.hpp>
|
||||||
|
#include "lv_font_ru.h"
|
||||||
|
|
||||||
|
#define PIN_LCD_CS 12
|
||||||
|
#define PIN_LCD_SCLK 38
|
||||||
|
#define PIN_LCD_D0 4
|
||||||
|
#define PIN_LCD_D1 5
|
||||||
|
#define PIN_LCD_D2 6
|
||||||
|
#define PIN_LCD_D3 7
|
||||||
|
#define PIN_LCD_RST 2
|
||||||
|
#define PIN_I2C_SDA 15
|
||||||
|
#define PIN_I2C_SCL 14
|
||||||
|
#define PIN_TP_INT 11
|
||||||
|
|
||||||
|
#define DISP_W 480
|
||||||
|
#define DISP_H 480
|
||||||
|
#define LVGL_TICK_MS 2
|
||||||
|
#define TEST_VERSION "v1-ru-font"
|
||||||
|
|
||||||
|
static lv_disp_draw_buf_t gDrawBuf;
|
||||||
|
static lv_color_t *gBuf1 = nullptr;
|
||||||
|
static lv_color_t *gBuf2 = nullptr;
|
||||||
|
static lv_obj_t *gStatusLabel = nullptr;
|
||||||
|
static lv_obj_t *gTouchLabel = nullptr;
|
||||||
|
static uint32_t gClickCount = 0;
|
||||||
|
static bool gTouchActive = false;
|
||||||
|
static int16_t gLastX = -1;
|
||||||
|
static int16_t gLastY = -1;
|
||||||
|
|
||||||
|
Arduino_DataBus *gBus = new Arduino_ESP32QSPI(
|
||||||
|
PIN_LCD_CS, PIN_LCD_SCLK, PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3);
|
||||||
|
Arduino_CO5300 *gfx = new Arduino_CO5300(
|
||||||
|
gBus, PIN_LCD_RST, 0, DISP_W, DISP_H, 0, 0, 0, 0);
|
||||||
|
TouchDrvCST92xx gTouch;
|
||||||
|
|
||||||
|
static void lvglFlushCb(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *colorP) {
|
||||||
|
uint32_t w = area->x2 - area->x1 + 1;
|
||||||
|
uint32_t h = area->y2 - area->y1 + 1;
|
||||||
|
#if (LV_COLOR_16_SWAP != 0)
|
||||||
|
gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&colorP->full, w, h);
|
||||||
|
#else
|
||||||
|
gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&colorP->full, w, h);
|
||||||
|
#endif
|
||||||
|
lv_disp_flush_ready(disp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lvglTick(void *arg) {
|
||||||
|
LV_UNUSED(arg);
|
||||||
|
lv_tick_inc(LVGL_TICK_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void updateTouchLabel() {
|
||||||
|
if (!gTouchLabel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char text[96];
|
||||||
|
if (gTouchActive) {
|
||||||
|
snprintf(text, sizeof(text), "Касание: x=%d y=%d", gLastX, gLastY);
|
||||||
|
} else {
|
||||||
|
snprintf(text, sizeof(text), "Касание: нет x=%d y=%d", gLastX, gLastY);
|
||||||
|
}
|
||||||
|
lv_label_set_text(gTouchLabel, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lvglTouchRead(lv_indev_drv_t *indevDrv, lv_indev_data_t *data) {
|
||||||
|
LV_UNUSED(indevDrv);
|
||||||
|
int16_t x = 0;
|
||||||
|
int16_t y = 0;
|
||||||
|
bool touching = gTouch.getPoint(&x, &y, 1) > 0;
|
||||||
|
|
||||||
|
if (touching) {
|
||||||
|
gTouchActive = true;
|
||||||
|
gLastX = x;
|
||||||
|
gLastY = y;
|
||||||
|
data->state = LV_INDEV_STATE_PR;
|
||||||
|
data->point.x = x;
|
||||||
|
data->point.y = y;
|
||||||
|
} else {
|
||||||
|
gTouchActive = false;
|
||||||
|
data->state = LV_INDEV_STATE_REL;
|
||||||
|
data->point.x = gLastX >= 0 ? gLastX : 0;
|
||||||
|
data->point.y = gLastY >= 0 ? gLastY : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTouchLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void buttonEventCb(lv_event_t *event) {
|
||||||
|
if (lv_event_get_code(event) != LV_EVENT_CLICKED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *name = (const char *)lv_event_get_user_data(event);
|
||||||
|
gClickCount++;
|
||||||
|
|
||||||
|
char text[128];
|
||||||
|
snprintf(text, sizeof(text), "Нажато: %s (%lu)", name, (unsigned long)gClickCount);
|
||||||
|
lv_label_set_text(gStatusLabel, text);
|
||||||
|
Serial.println(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
static lv_obj_t *makeButton(lv_obj_t *parent,
|
||||||
|
const char *labelText,
|
||||||
|
lv_coord_t x,
|
||||||
|
lv_coord_t y,
|
||||||
|
lv_coord_t w,
|
||||||
|
lv_coord_t h,
|
||||||
|
uint32_t bgColor) {
|
||||||
|
lv_obj_t *btn = lv_btn_create(parent);
|
||||||
|
lv_obj_set_size(btn, w, h);
|
||||||
|
lv_obj_set_pos(btn, x, y);
|
||||||
|
lv_obj_set_style_radius(btn, 16, 0);
|
||||||
|
lv_obj_set_style_bg_color(btn, lv_color_hex(bgColor), 0);
|
||||||
|
lv_obj_set_style_bg_opa(btn, LV_OPA_COVER, 0);
|
||||||
|
lv_obj_set_style_border_width(btn, 2, 0);
|
||||||
|
lv_obj_set_style_border_color(btn, lv_color_hex(0x6F8BA4), 0);
|
||||||
|
lv_obj_set_style_shadow_width(btn, 0, 0);
|
||||||
|
lv_obj_set_style_pad_all(btn, 4, 0);
|
||||||
|
lv_obj_add_event_cb(btn, buttonEventCb, LV_EVENT_CLICKED, (void *)labelText);
|
||||||
|
|
||||||
|
lv_obj_t *label = lv_label_create(btn);
|
||||||
|
lv_label_set_text(label, labelText);
|
||||||
|
lv_obj_set_width(label, w - 12);
|
||||||
|
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
|
||||||
|
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
|
||||||
|
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0);
|
||||||
|
lv_obj_set_style_text_font(label, &lv_font_ru_18, 0);
|
||||||
|
lv_obj_center(label);
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void createUi() {
|
||||||
|
lv_obj_t *screen = lv_scr_act();
|
||||||
|
lv_obj_set_style_bg_color(screen, lv_color_hex(0x08131E), 0);
|
||||||
|
lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, 0);
|
||||||
|
lv_obj_set_style_pad_all(screen, 0, 0);
|
||||||
|
lv_obj_set_scrollbar_mode(screen, LV_SCROLLBAR_MODE_OFF);
|
||||||
|
|
||||||
|
lv_obj_t *title = lv_label_create(screen);
|
||||||
|
lv_label_set_text(title, "Русский тест LVGL");
|
||||||
|
lv_obj_set_style_text_font(title, &lv_font_ru_24, 0);
|
||||||
|
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
|
||||||
|
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 10);
|
||||||
|
|
||||||
|
lv_obj_t *version = lv_label_create(screen);
|
||||||
|
lv_label_set_text(version, "Версия: " TEST_VERSION);
|
||||||
|
lv_obj_set_style_text_font(version, &lv_font_ru_18, 0);
|
||||||
|
lv_obj_set_style_text_color(version, lv_color_hex(0xD8E0EA), 0);
|
||||||
|
lv_obj_align(version, LV_ALIGN_TOP_MID, 0, 42);
|
||||||
|
|
||||||
|
lv_obj_t *hint = lv_label_create(screen);
|
||||||
|
lv_label_set_text(hint, "Проверка кириллицы, длинных слов, кнопок и нажатий.");
|
||||||
|
lv_obj_set_width(hint, 436);
|
||||||
|
lv_label_set_long_mode(hint, LV_LABEL_LONG_WRAP);
|
||||||
|
lv_obj_set_style_text_font(hint, &lv_font_ru_18, 0);
|
||||||
|
lv_obj_set_style_text_color(hint, lv_color_hex(0xD8E0EA), 0);
|
||||||
|
lv_obj_align(hint, LV_ALIGN_TOP_MID, 0, 72);
|
||||||
|
|
||||||
|
makeButton(screen, "Статус", 22, 126, 136, 62, 0x355C7D);
|
||||||
|
makeButton(screen, "Подключение", 172, 126, 136, 62, 0x2A9D8F);
|
||||||
|
makeButton(screen, "Кошелёк", 322, 126, 136, 62, 0x457B9D);
|
||||||
|
|
||||||
|
makeButton(screen, "Запросы", 22, 204, 136, 62, 0xE76F51);
|
||||||
|
makeButton(screen, "Настройки", 172, 204, 136, 62, 0x8D5A97);
|
||||||
|
makeButton(screen, "Регистрация", 322, 204, 136, 62, 0x6A994E);
|
||||||
|
|
||||||
|
makeButton(screen, "Разрешить", 22, 282, 136, 62, 0xBC6C25);
|
||||||
|
makeButton(screen, "Отклонить", 172, 282, 136, 62, 0xB56576);
|
||||||
|
makeButton(screen, "Назад", 322, 282, 136, 62, 0x6C757D);
|
||||||
|
|
||||||
|
makeButton(screen, "Проверка переноса русского текста", 22, 360, 436, 64, 0x1D6F42);
|
||||||
|
|
||||||
|
lv_obj_t *statusPanel = lv_obj_create(screen);
|
||||||
|
lv_obj_set_size(statusPanel, 436, 24);
|
||||||
|
lv_obj_set_pos(statusPanel, 22, 428);
|
||||||
|
lv_obj_set_style_radius(statusPanel, 10, 0);
|
||||||
|
lv_obj_set_style_bg_color(statusPanel, lv_color_hex(0x162435), 0);
|
||||||
|
lv_obj_set_style_bg_opa(statusPanel, LV_OPA_COVER, 0);
|
||||||
|
lv_obj_set_style_border_width(statusPanel, 1, 0);
|
||||||
|
lv_obj_set_style_border_color(statusPanel, lv_color_hex(0x4D6986), 0);
|
||||||
|
lv_obj_set_style_pad_all(statusPanel, 2, 0);
|
||||||
|
|
||||||
|
gStatusLabel = lv_label_create(statusPanel);
|
||||||
|
lv_label_set_text(gStatusLabel, "Нажато: ничего");
|
||||||
|
lv_obj_set_style_text_font(gStatusLabel, &lv_font_ru_18, 0);
|
||||||
|
lv_obj_set_style_text_color(gStatusLabel, lv_color_hex(0xFFFFFF), 0);
|
||||||
|
lv_obj_center(gStatusLabel);
|
||||||
|
|
||||||
|
lv_obj_t *touchPanel = lv_obj_create(screen);
|
||||||
|
lv_obj_set_size(touchPanel, 436, 24);
|
||||||
|
lv_obj_set_pos(touchPanel, 22, 454);
|
||||||
|
lv_obj_set_style_radius(touchPanel, 10, 0);
|
||||||
|
lv_obj_set_style_bg_color(touchPanel, lv_color_hex(0x162435), 0);
|
||||||
|
lv_obj_set_style_bg_opa(touchPanel, LV_OPA_COVER, 0);
|
||||||
|
lv_obj_set_style_border_width(touchPanel, 1, 0);
|
||||||
|
lv_obj_set_style_border_color(touchPanel, lv_color_hex(0x4D6986), 0);
|
||||||
|
lv_obj_set_style_pad_all(touchPanel, 2, 0);
|
||||||
|
|
||||||
|
gTouchLabel = lv_label_create(touchPanel);
|
||||||
|
lv_label_set_text(gTouchLabel, "Касание: нет x=-1 y=-1");
|
||||||
|
lv_obj_set_style_text_font(gTouchLabel, &lv_font_ru_18, 0);
|
||||||
|
lv_obj_set_style_text_color(gTouchLabel, lv_color_hex(0xD8E0EA), 0);
|
||||||
|
lv_obj_center(gTouchLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);
|
||||||
|
|
||||||
|
gfx->begin();
|
||||||
|
gBus->writeC8D8(0x36, 0xA0);
|
||||||
|
gfx->setBrightness(220);
|
||||||
|
gfx->fillScreen(0x0000);
|
||||||
|
|
||||||
|
gTouch.setPins(PIN_TP_INT, -1);
|
||||||
|
gTouch.begin(Wire, CST92XX_SLAVE_ADDRESS, PIN_I2C_SDA, PIN_I2C_SCL);
|
||||||
|
gTouch.setMaxCoordinates(DISP_W, DISP_H);
|
||||||
|
gTouch.setSwapXY(true);
|
||||||
|
gTouch.setMirrorXY(true, false);
|
||||||
|
|
||||||
|
lv_init();
|
||||||
|
|
||||||
|
uint32_t screenWidth = gfx->width();
|
||||||
|
uint32_t screenHeight = gfx->height();
|
||||||
|
uint32_t bufPixels = screenWidth * 40;
|
||||||
|
gBuf1 = (lv_color_t *)heap_caps_malloc(bufPixels * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||||
|
gBuf2 = (lv_color_t *)heap_caps_malloc(bufPixels * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||||
|
|
||||||
|
lv_disp_draw_buf_init(&gDrawBuf, gBuf1, gBuf2, bufPixels);
|
||||||
|
|
||||||
|
static lv_disp_drv_t dispDrv;
|
||||||
|
lv_disp_drv_init(&dispDrv);
|
||||||
|
dispDrv.hor_res = screenWidth;
|
||||||
|
dispDrv.ver_res = screenHeight;
|
||||||
|
dispDrv.flush_cb = lvglFlushCb;
|
||||||
|
dispDrv.draw_buf = &gDrawBuf;
|
||||||
|
lv_disp_drv_register(&dispDrv);
|
||||||
|
|
||||||
|
static lv_indev_drv_t indevDrv;
|
||||||
|
lv_indev_drv_init(&indevDrv);
|
||||||
|
indevDrv.type = LV_INDEV_TYPE_POINTER;
|
||||||
|
indevDrv.read_cb = lvglTouchRead;
|
||||||
|
lv_indev_drv_register(&indevDrv);
|
||||||
|
|
||||||
|
const esp_timer_create_args_t tickArgs = {
|
||||||
|
.callback = &lvglTick,
|
||||||
|
.name = "lvgl_tick"};
|
||||||
|
esp_timer_handle_t tickTimer = nullptr;
|
||||||
|
esp_timer_create(&tickArgs, &tickTimer);
|
||||||
|
esp_timer_start_periodic(tickTimer, LVGL_TICK_MS * 1000);
|
||||||
|
|
||||||
|
createUi();
|
||||||
|
Serial.println("Русский LVGL font test ready: " TEST_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
lv_timer_handler();
|
||||||
|
delay(5);
|
||||||
|
}
|
||||||
@ -1,2 +1,2 @@
|
|||||||
client.version=1.2.141
|
client.version=1.2.142
|
||||||
server.version=1.2.133
|
server.version=1.2.134
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user