ESP32: зафиксировать тесты и нерабочий LVGL/UI вариант

This commit is contained in:
AidarKC 2026-06-08 15:35:27 +04:00
parent 66975862f7
commit ad0edf3c88
15 changed files with 921 additions and 34 deletions

View File

@ -5,7 +5,7 @@
- что именно проверять:
1. Прошить режим `subserver-ui` и дождаться открытия главного экрана без PIN.
2. Проверить, что русский текст в заголовках, кнопках и статусах отображается корректно, без кракозябр и замены на английский.
2. Проверить, что текст в заголовках, кнопках и статусах отображается читаемо; в текущей временной сборке допускается ASCII-транслитерация русского текста.
3. Открыть `Настройки` и убедиться, что показывается пометка о временно отключённом входе по PIN.
4. Открыть `Подключение -> Wi-Fi`, ввести `SSID` и пароль, нажать `Проверить`, дождаться реального подключения, затем перезагрузить устройство и проверить, что значения сохранились.
5. Открыть `Подключение -> Серверы`, проверить или изменить `API/RPC/WS`, нажать `Проверить` и убедиться, что показываются реальные статусы доступности, затем перезагрузить устройство и проверить сохранение значений.
@ -20,7 +20,7 @@
14. Выполнить `Полный сброс` и убедиться, что все поля, секрет, баланс, онлайн и регистрация очищаются.
- ожидаемый результат:
новый `ESP32`-скетч стабильно запускается, показывает нормальный русский интерфейс, сохраняет данные во внутренней памяти устройства, реально подключается к `Wi-Fi`, реально проверяет `API/RPC/WS`, реально читает баланс из `Solana RPC`, рисует рабочий `QR` для `solana:`-URI и позволяет вручную пройти полный сценарий on-chain регистрации сабсервера.
новый `ESP32`-скетч стабильно запускается, показывает читаемый интерфейс хотя бы в ASCII-транслитерации, сохраняет данные во внутренней памяти устройства, реально подключается к `Wi-Fi`, реально проверяет `API/RPC/WS`, реально читает баланс из `Solana RPC`, рисует рабочий `QR` для `solana:`-URI и позволяет вручную пройти полный сценарий on-chain регистрации сабсервера.
- статус:
pending

View File

@ -0,0 +1,14 @@
# ESP32 тест рендера текста
- краткое описание фичи:
добавлен отдельный диагностический скетч `text_render_test`, который показывает один экран с несколькими вариантами вывода текста: встроенный шрифт `Arduino_GFX`, `U8g2` ASCII, `U8g2` кириллица и кнопки с подписями. Скрипт нужен для изоляции проблемы, когда на экране видны только цветные кнопки и блоки, но не видно ни одной буквы.
- что именно проверять:
1. Прошить режим `text-test`.
2. Проверить, виден ли заголовок `TEXT TEST 123`.
3. Проверить, видны ли строки `A`, `B`, `C`, `D`.
4. Проверить, видны ли подписи на трёх нижних кнопках: `BTN 1`, `abc123`, `Русский`.
5. Сравнить, какой из способов вывода реально отображается, а какой нет.
- ожидаемый результат:
хотя бы один вариант вывода текста становится видим на экране, что позволяет локализовать проблему до конкретного шрифта или способа рендера.
- статус:
pending

View File

@ -0,0 +1,13 @@
# ESP32 папка тестовых скетчей
- краткое описание фичи:
добавлена отдельная папка `test_sketches/` с изолированными диагностическими скетчами для экрана `ESP32-S3-Touch-AMOLED-2.16`: тест рендера текста через `Arduino_GFX`, тест геометрии кнопок и минимальный тест `LVGL`.
- что именно проверять:
1. Запустить `./burn.sh gfx-text-test` и убедиться, что прошивается тест текста из новой папки.
2. Запустить `./burn.sh gfx-layout-test` и проверить нижние ряды кнопок.
3. Запустить `./burn.sh lvgl-basic-test` и проверить, что `LVGL` показывает текст и кнопки.
4. Убедиться, что новая папка не мешает сборке `subserver-ui`.
- ожидаемый результат:
тестовые скетчи лежат отдельно от основного UI, шьются отдельными режимами и позволяют быстро проверять разные гипотезы по экрану без правок в `shine_subserver_ui`.
- статус:
pending

View File

@ -0,0 +1,14 @@
# ESP32 LVGL interaction test
- краткое описание фичи:
добавлен отдельный скетч `lvgl_interaction_test` на `LVGL`: экран с 9 кнопками, touch-вводом и нижней статусной строкой. При нажатии на кнопку на экране и в `Serial` показывается, какая именно кнопка нажата и сколько нажатий уже было.
- что именно проверять:
1. Прошить режим `lvgl-interaction-test`.
2. Убедиться, что виден заголовок, подзаголовок, 9 кнопок и нижняя статусная панель.
3. Поочерёдно нажать разные кнопки.
4. Проверить, что нижняя строка меняется на `Pressed: <button> (#N)`.
5. Проверить, что touch устойчиво работает по всей сетке кнопок.
- ожидаемый результат:
`LVGL` стабильно рисует плотный экран с множеством кнопок, а нажатия корректно обрабатываются и визуально подтверждаются без глюков позиционирования.
- статус:
pending

View File

@ -18,7 +18,7 @@
- локальный UI на тач-экране;
- хранение настроек и секретов во внутренней памяти `ESP32` через `NVS`;
- русский текст на экране через `UTF-8` + кириллический шрифт `U8g2`;
- русский текст в логике интерфейса; в текущей временной сборке отображение на экране идёт через ASCII-транслитерацию, потому что `U8g2`-шрифты на устройстве временно не рисуются;
- экран пополнения с реальным `solana:` URI и рисованием QR-кода;
- реальное подключение к `Wi-Fi` по сохранённым `SSID/паролю`;
- реальная проверка доступности `API`, `RPC` и `WS`-адресов;
@ -107,8 +107,8 @@
- Верхняя строка всегда показывает краткий статус устройства:
`PIN`, `Wi-Fi`, `сервер`, `регистрация`.
- Основной язык прототипа: русский.
- Для вывода текста используется `UTF-8`.
- Для кириллицы используется `U8g2`-шрифт с поддержкой `Cyrillic`.
- Для вывода текста в текущей временной сборке используется стандартный шрифт `Arduino_GFX`.
- Русские строки на экране временно показываются в ASCII-транслитерации.
- Кнопки крупные, с тач-ориентированным размером.
- Опасные действия подтверждаются отдельным диалогом.
- После изменения данных конфигурация сразу сохраняется в `NVS`.
@ -452,23 +452,21 @@ QR должен быть сканируемым, а не декоративны
4. нажать `Разрешить` или `Отклонить`;
5. убедиться, что статус запроса изменился.
## Технические требования к кириллице
## Временный режим текста
Для корректного русского текста скетч обязан:
В текущей диагностической версии:
- хранить строковые литералы в `UTF-8`;
- вызывать `gfx->setUTF8Print(true)`;
- использовать шрифт `U8g2` с поддержкой `Cyrillic`;
- не полагаться на стандартный `ASCII`-шрифт `Arduino_GFX` для русского текста.
Если эти условия нарушены, UI считается сломанным даже при правильной логике экранов.
- строковые литералы в коде остаются русскими и в `UTF-8`;
- перед выводом на экран они временно транслитерируются в ASCII;
- рендер выполняется стандартным шрифтом `Arduino_GFX`;
- это обходной режим, пока `U8g2`-шрифты на устройстве не начнут рисоваться стабильно.
## Критерии ручной проверки
Минимально нужно проверить:
1. устройство загружается и сразу открывает `HOME`; экран блокировки временно отключён;
2. русский текст отображается без кракозябр;
2. текст отображается читаемо хотя бы в ASCII-транслитерации;
3. ввод по экранной клавиатуре работает;
4. после перезагрузки сохранённые поля остаются в памяти;
5. секрет и адрес кошелька сохраняются на устройстве;

View File

@ -11,6 +11,11 @@
- `simple` — простой кастомный тест: экран + touch + запись/проигрывание + наклон (IMU)
- `argon2` — генерация masterSecret через Argon2id с SD-картой как памятью (тест скорости)
- `subserver-ui` — основной UI-прототип сабсервера SHiNE: NVS, PIN, Wi-Fi, серверы, кошелёк, QR, запросы
- `text-test` — диагностический экран рендера текста: default font, U8g2 ASCII, U8g2 кириллица, кнопки с подписями
- `gfx-text-test` — тот же тест рендера текста, но уже внутри новой папки `test_sketches/`
- `gfx-layout-test` — тест геометрии и нижних рядов кнопок
- `lvgl-basic-test` — минимальный экран на `LVGL` с текстом и кнопками
- `lvgl-interaction-test` — экран на `LVGL` с большим числом кнопок и сообщением о нажатой кнопке
Запуск:
@ -19,4 +24,9 @@
- `./burn.sh hello`
- `./burn.sh simple`
- `./burn.sh subserver-ui`
- `./burn.sh text-test`
- `./burn.sh gfx-text-test`
- `./burn.sh gfx-layout-test`
- `./burn.sh lvgl-basic-test`
- `./burn.sh lvgl-interaction-test`
- `./flash_shine_subserver_ui.sh` - автоматически находит USB-порт и заливает `shine_subserver_ui`

View File

@ -17,9 +17,14 @@ case "${MODE}" in
simple) SKETCH_DIR="${ROOT_DIR}/simple_av_test" ;;
argon2) SKETCH_DIR="${ROOT_DIR}/argon2_sd_test" ;;
subserver-ui) SKETCH_DIR="${ROOT_DIR}/shine_subserver_ui" ;;
text-test) SKETCH_DIR="${ROOT_DIR}/text_render_test" ;;
gfx-text-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/gfx_text_render_test" ;;
gfx-layout-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/gfx_button_layout_test" ;;
lvgl-basic-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_basic_test" ;;
lvgl-interaction-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_interaction_test" ;;
*)
echo "Unknown mode: ${MODE}" >&2
echo "Use one of: hello, widgets, audio, simple, argon2, subserver-ui" >&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" >&2
exit 2
;;
esac

View File

@ -299,44 +299,187 @@ static String normalizeLogin(const String &value) {
return out;
}
static void appendAsciiChar(String &out, char c) {
if (c == '\n' || c == '\r' || c == '\t') {
out += c;
return;
}
uint8_t uc = (uint8_t)c;
if (uc >= 32 && uc <= 126) {
out += c;
return;
}
out += '?';
}
static void appendTranslit(String &out, const char *value) {
out += value;
}
static void appendUtf8Translit(String &out, uint8_t first, uint8_t second) {
if (first == 0xD0) {
switch (second) {
case 0x81: appendTranslit(out, "Yo"); return;
case 0x90: appendTranslit(out, "A"); return;
case 0x91: appendTranslit(out, "B"); return;
case 0x92: appendTranslit(out, "V"); return;
case 0x93: appendTranslit(out, "G"); return;
case 0x94: appendTranslit(out, "D"); return;
case 0x95: appendTranslit(out, "E"); return;
case 0x96: appendTranslit(out, "Zh"); return;
case 0x97: appendTranslit(out, "Z"); return;
case 0x98: appendTranslit(out, "I"); return;
case 0x99: appendTranslit(out, "Y"); return;
case 0x9A: appendTranslit(out, "K"); return;
case 0x9B: appendTranslit(out, "L"); return;
case 0x9C: appendTranslit(out, "M"); return;
case 0x9D: appendTranslit(out, "N"); return;
case 0x9E: appendTranslit(out, "O"); return;
case 0x9F: appendTranslit(out, "P"); return;
case 0xA0: appendTranslit(out, "R"); return;
case 0xA1: appendTranslit(out, "S"); return;
case 0xA2: appendTranslit(out, "T"); return;
case 0xA3: appendTranslit(out, "U"); return;
case 0xA4: appendTranslit(out, "F"); return;
case 0xA5: appendTranslit(out, "Kh"); return;
case 0xA6: appendTranslit(out, "Ts"); return;
case 0xA7: appendTranslit(out, "Ch"); return;
case 0xA8: appendTranslit(out, "Sh"); return;
case 0xA9: appendTranslit(out, "Sch"); return;
case 0xAA: appendTranslit(out, ""); return;
case 0xAB: appendTranslit(out, "Y"); return;
case 0xAC: appendTranslit(out, ""); return;
case 0xAD: appendTranslit(out, "E"); return;
case 0xAE: appendTranslit(out, "Yu"); return;
case 0xAF: appendTranslit(out, "Ya"); return;
case 0xB0: appendTranslit(out, "a"); return;
case 0xB1: appendTranslit(out, "b"); return;
case 0xB2: appendTranslit(out, "v"); return;
case 0xB3: appendTranslit(out, "g"); return;
case 0xB4: appendTranslit(out, "d"); return;
case 0xB5: appendTranslit(out, "e"); return;
case 0xB6: appendTranslit(out, "zh"); return;
case 0xB7: appendTranslit(out, "z"); return;
case 0xB8: appendTranslit(out, "i"); return;
case 0xB9: appendTranslit(out, "y"); return;
case 0xBA: appendTranslit(out, "k"); return;
case 0xBB: appendTranslit(out, "l"); return;
case 0xBC: appendTranslit(out, "m"); return;
case 0xBD: appendTranslit(out, "n"); return;
case 0xBE: appendTranslit(out, "o"); return;
case 0xBF: appendTranslit(out, "p"); return;
default: break;
}
} else if (first == 0xD1) {
switch (second) {
case 0x80: appendTranslit(out, "r"); return;
case 0x81: appendTranslit(out, "s"); return;
case 0x82: appendTranslit(out, "t"); return;
case 0x83: appendTranslit(out, "u"); return;
case 0x84: appendTranslit(out, "f"); return;
case 0x85: appendTranslit(out, "kh"); return;
case 0x86: appendTranslit(out, "ts"); return;
case 0x87: appendTranslit(out, "ch"); return;
case 0x88: appendTranslit(out, "sh"); return;
case 0x89: appendTranslit(out, "sch"); return;
case 0x8A: appendTranslit(out, ""); return;
case 0x8B: appendTranslit(out, "y"); return;
case 0x8C: appendTranslit(out, ""); return;
case 0x8D: appendTranslit(out, "e"); return;
case 0x8E: appendTranslit(out, "yu"); return;
case 0x8F: appendTranslit(out, "ya"); return;
case 0x91: appendTranslit(out, "yo"); return;
default: break;
}
}
out += '?';
}
static String toDisplayAscii(const String &text) {
String out;
out.reserve(text.length() * 2);
for (size_t i = 0; i < text.length(); i++) {
uint8_t c = (uint8_t)text[i];
if (c < 0x80) {
appendAsciiChar(out, (char)c);
continue;
}
if ((c == 0xD0 || c == 0xD1) && i + 1 < text.length()) {
appendUtf8Translit(out, c, (uint8_t)text[i + 1]);
i++;
continue;
}
out += '?';
}
return out;
}
static uint8_t fontScale(const uint8_t *font) {
if (font == (const uint8_t *)FONT_HEAD) {
return 2;
}
if (font == (const uint8_t *)FONT_BODY) {
return 2;
}
return 1;
}
static int defaultFontCharWidth(uint8_t scale) {
return 6 * scale;
}
static int defaultFontHeight(uint8_t scale) {
return 8 * scale;
}
static void setFont(const uint8_t *font) {
gfx->setTextSize(1);
gfx->setFont(font);
gfx->setFont();
gfx->setTextSize(fontScale(font));
}
static void drawText(int x, int y, const String &text, uint16_t color, const uint8_t *font = (const uint8_t *)FONT_BODY) {
String display = toDisplayAscii(text);
setFont(font);
gfx->setTextColor(color);
gfx->setCursor(x, y);
gfx->print(text);
gfx->print(display);
}
static void drawTextCentered(int x, int y, int w, int h, const String &text, uint16_t color, const uint8_t *font = (const uint8_t *)FONT_BODY) {
String display = toDisplayAscii(text);
uint8_t scale = fontScale(font);
setFont(font);
gfx->setTextColor(color);
int16_t x1 = 0;
int16_t y1 = 0;
uint16_t textW = 0;
uint16_t textH = 0;
gfx->getTextBounds(text, 0, 0, &x1, &y1, &textW, &textH);
int textX = x + (w - (int)textW) / 2 - x1;
int textY = y + (h - (int)textH) / 2 - y1;
int textW = display.length() * defaultFontCharWidth(scale);
int textH = defaultFontHeight(scale);
int textX = x + (w - textW) / 2;
int textY = y + (h + textH) / 2 - 2;
if (textX < x + 6) {
textX = x + 6;
}
gfx->setCursor(textX, textY);
gfx->print(text);
gfx->print(display);
}
static void drawWrappedText(int x, int y, int maxChars, int lineHeight, const String &text, uint16_t color, const uint8_t *font = (const uint8_t *)FONT_BODY) {
String display = toDisplayAscii(text);
uint8_t scale = fontScale(font);
int effectiveLineHeight = lineHeight;
int minLineHeight = defaultFontHeight(scale) + 4;
if (effectiveLineHeight < minLineHeight) {
effectiveLineHeight = minLineHeight;
}
setFont(font);
gfx->setTextColor(color);
String line;
int lineY = y;
for (size_t i = 0; i < text.length(); i++) {
char c = text[i];
for (size_t i = 0; i < display.length(); i++) {
char c = display[i];
if (c == '\n') {
gfx->setCursor(x, lineY);
gfx->print(line);
line = "";
lineY += lineHeight;
lineY += effectiveLineHeight;
continue;
}
line += c;
@ -344,7 +487,7 @@ static void drawWrappedText(int x, int y, int maxChars, int lineHeight, const St
gfx->setCursor(x, lineY);
gfx->print(line);
line = "";
lineY += lineHeight;
lineY += effectiveLineHeight;
}
}
if (line.length() > 0) {
@ -466,8 +609,26 @@ static void drawNoticeBox() {
if (gNotice.length() == 0) {
return;
}
drawPanel(20, 420, 440, 42, C_CARD, C_BORDER, 10);
drawWrappedText(32, 444, 52, 16, gNotice, C_MUTE, (const uint8_t *)FONT_SMALL);
const int noticeX = 20;
const int noticeW = 440;
const int noticeH = 42;
int noticeY = 420;
for (uint8_t i = 0; i < gButtonCount; i++) {
const Button &btn = gButtons[i];
bool overlapX = btn.x < (noticeX + noticeW) && (btn.x + btn.w) > noticeX;
bool overlapY = btn.y < (noticeY + noticeH) && (btn.y + btn.h) > noticeY;
if (overlapX && overlapY) {
int candidateY = btn.y - noticeH - 6;
if (candidateY < noticeY) {
noticeY = candidateY;
}
}
}
if (noticeY < 82) {
noticeY = 82;
}
drawPanel(noticeX, noticeY, noticeW, noticeH, C_CARD, C_BORDER, 10);
drawWrappedText(noticeX + 12, noticeY + 24, 52, 16, gNotice, C_MUTE, (const uint8_t *)FONT_SMALL);
}
static const char *base58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

View File

@ -0,0 +1,21 @@
# Test Sketches
Набор отдельных диагностических скетчей для `Waveshare ESP32-S3-Touch-AMOLED-2.16`.
Скетчи в этой папке нужны для быстрой проверки конкретных гипотез без влияния на основной `shine_subserver_ui`.
## Список
- `gfx_text_render_test/` - проверка рендера текста через `Arduino_GFX` и сравнение с `U8g2`
- `gfx_button_layout_test/` - проверка геометрии кнопок, особенно нижних рядов и широких кнопок
- `lvgl_basic_test/` - минимальный тест `LVGL` с заголовком, текстом и кнопками
- `lvgl_interaction_test/` - расширенный тест `LVGL` с 9 кнопками, touch-вводом и статусом нажатия
## Запуск
Использовать через `burn.sh`:
- `./burn.sh gfx-text-test`
- `./burn.sh gfx-layout-test`
- `./burn.sh lvgl-basic-test`
- `./burn.sh lvgl-interaction-test`

View File

@ -0,0 +1,93 @@
#include <Arduino.h>
#include <Wire.h>
#include <Arduino_GFX_Library.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 DISP_W 480
#define DISP_H 480
#define C_BG 0x0841u
#define C_PANEL 0x1082u
#define C_CARD 0x18C3u
#define C_BORDER 0x39C7u
#define C_TEXT 0xFFFFu
#define C_OK 0x3666u
#define C_WARN 0xECA0u
#define C_BUTTON 0x2145u
#define C_BUTTON2 0x3186u
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);
static void drawPanel(int x, int y, int w, int h, uint16_t fill, uint16_t border, int radius = 10) {
gfx->fillRoundRect(x, y, w, h, radius, fill);
gfx->drawRoundRect(x, y, w, h, radius, border);
}
static void drawText(int x, int y, const char *text, uint16_t color, uint8_t size = 2) {
gfx->setFont();
gfx->setTextSize(size);
gfx->setTextColor(color);
gfx->setCursor(x, y);
gfx->print(text);
}
static void drawButton(int x, int y, int w, int h, uint16_t fill, const char *label, uint8_t size = 2) {
drawPanel(x, y, w, h, fill, C_BORDER, 12);
gfx->setFont();
gfx->setTextSize(size);
gfx->setTextColor(C_TEXT);
int textW = strlen(label) * 6 * size;
int textX = x + (w - textW) / 2;
if (textX < x + 8) {
textX = x + 8;
}
int textY = y + (h + 8 * size) / 2 - 2;
gfx->setCursor(textX, textY);
gfx->print(label);
}
static void drawScreen() {
gfx->fillScreen(C_BG);
drawPanel(12, 12, 456, 456, C_PANEL, C_BORDER, 16);
drawText(24, 42, "BUTTON LAYOUT TEST", C_TEXT, 2);
drawText(24, 70, "Check bottom and wide buttons", C_WARN, 1);
drawButton(20, 110, 136, 52, C_BUTTON, "STATUS");
drawButton(172, 110, 136, 52, C_BUTTON, "CONNECT");
drawButton(324, 110, 136, 52, C_BUTTON, "ACCOUNT");
drawButton(20, 180, 136, 52, C_BUTTON, "WALLET");
drawButton(172, 180, 136, 52, C_BUTTON, "REQUESTS");
drawButton(324, 180, 136, 52, C_BUTTON, "SETTINGS");
drawButton(20, 270, 212, 48, C_BUTTON2, "BACK");
drawButton(248, 270, 212, 48, C_OK, "REFRESH");
drawButton(20, 338, 440, 40, C_BUTTON2, "FULL WIDTH BUTTON", 2);
drawButton(20, 390, 440, 56, C_OK, "REGISTER DEVICE", 2);
}
void setup() {
Serial.begin(115200);
Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);
gfx->begin();
gBus->writeC8D8(0x36, 0xA0);
gfx->setBrightness(220);
drawScreen();
}
void loop() {
delay(1000);
}

View File

@ -0,0 +1,104 @@
#include <Arduino.h>
#include <Wire.h>
#include <Arduino_GFX_Library.h>
#include <U8g2lib.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 DISP_W 480
#define DISP_H 480
#define C_BG 0x0841u
#define C_PANEL 0x1082u
#define C_CARD 0x18C3u
#define C_BORDER 0x39C7u
#define C_TEXT 0xFFFFu
#define C_MUTE 0xBDF7u
#define C_OK 0x3666u
#define C_WARN 0xECA0u
#define C_BUTTON 0x2145u
#define C_BUTTON2 0x3186u
#define FONT_BODY u8g2_font_9x15_t_cyrillic
#define FONT_SMALL u8g2_font_6x13_t_cyrillic
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);
static void drawPanel(int x, int y, int w, int h, uint16_t fill, uint16_t border, int radius = 10) {
gfx->fillRoundRect(x, y, w, h, radius, fill);
gfx->drawRoundRect(x, y, w, h, radius, border);
}
static void drawDefaultText(int x, int y, const char *text, uint16_t color, uint8_t size = 1) {
gfx->setFont();
gfx->setTextSize(size);
gfx->setTextColor(color);
gfx->setCursor(x, y);
gfx->print(text);
}
static void drawU8Text(int x, int y, const String &text, uint16_t color, const uint8_t *font) {
gfx->setTextSize(1);
gfx->setFont(font);
gfx->setTextColor(color);
gfx->setCursor(x, y);
gfx->print(text);
}
static void drawButton(int x, int y, int w, int h, uint16_t fill, const String &label, const uint8_t *font, bool useDefaultFont = false) {
drawPanel(x, y, w, h, fill, C_BORDER, 12);
if (useDefaultFont) {
drawDefaultText(x + 14, y + 30, label.c_str(), C_TEXT, 2);
return;
}
drawU8Text(x + 14, y + 32, label, C_TEXT, font);
}
static void drawScreen() {
gfx->fillScreen(C_BG);
drawPanel(12, 12, 456, 456, C_PANEL, C_BORDER, 16);
drawDefaultText(28, 42, "TEXT TEST 123", C_TEXT, 2);
drawU8Text(28, 72, "Default ASCII above", C_MUTE, (const uint8_t *)FONT_SMALL);
drawPanel(24, 90, 432, 72, C_CARD, C_BORDER, 12);
drawDefaultText(38, 118, "A: Default font ABC123", C_TEXT, 2);
drawDefaultText(38, 146, "Visible? then base path OK", C_WARN, 1);
drawPanel(24, 174, 432, 84, C_CARD, C_BORDER, 12);
drawU8Text(38, 204, "B: U8g2 ASCII abc123", C_TEXT, (const uint8_t *)FONT_BODY);
drawU8Text(38, 230, "C: Русский текст 123", C_OK, (const uint8_t *)FONT_BODY);
drawPanel(24, 270, 432, 84, C_CARD, C_BORDER, 12);
drawU8Text(38, 298, "D: Мелкий шрифт кнопок", C_TEXT, (const uint8_t *)FONT_SMALL);
drawU8Text(38, 320, "Если это видно, FONT_SMALL жив", C_MUTE, (const uint8_t *)FONT_SMALL);
drawButton(24, 368, 128, 72, C_BUTTON, "BTN 1", nullptr, true);
drawButton(176, 368, 128, 72, C_OK, "abc123", (const uint8_t *)FONT_BODY);
drawButton(328, 368, 128, 72, C_BUTTON2, "Русский", (const uint8_t *)FONT_BODY);
}
void setup() {
Serial.begin(115200);
Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);
gfx->begin();
gBus->writeC8D8(0x36, 0xA0);
gfx->setBrightness(220);
gfx->setUTF8Print(true);
drawScreen();
}
void loop() {
delay(1000);
}

View File

@ -0,0 +1,125 @@
#include <Arduino.h>
#include <Wire.h>
#include <lvgl.h>
#include <Arduino_GFX_Library.h>
#include "pin_config.h"
#define EXAMPLE_LVGL_TICK_PERIOD_MS 2
static lv_disp_draw_buf_t gDrawBuf;
static lv_color_t *gBuf1 = nullptr;
static lv_color_t *gBuf2 = nullptr;
Arduino_DataBus *gBus = new Arduino_ESP32QSPI(
LCD_CS, LCD_SCLK, LCD_SDIO0, LCD_SDIO1, LCD_SDIO2, LCD_SDIO3);
Arduino_CO5300 *gfx = new Arduino_CO5300(
gBus, LCD_RESET, 0, LCD_WIDTH, LCD_HEIGHT, 0, 0, 0, 0);
static void lvglRounderCb(lv_disp_drv_t *dispDrv, lv_area_t *area) {
LV_UNUSED(dispDrv);
if (area->x1 % 2 != 0) area->x1--;
if (area->y1 % 2 != 0) area->y1--;
if (area->x2 % 2 == 0) area->x2++;
if (area->y2 % 2 == 0) area->y2++;
}
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(EXAMPLE_LVGL_TICK_PERIOD_MS);
}
static void createUi() {
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x0B1320), 0);
lv_obj_set_style_bg_opa(lv_scr_act(), LV_OPA_COVER, 0);
lv_obj_t *title = lv_label_create(lv_scr_act());
lv_label_set_text(title, "LVGL BASIC TEST");
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_text_font(title, &lv_font_montserrat_22, 0);
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 18);
lv_obj_t *subtitle = lv_label_create(lv_scr_act());
lv_label_set_text(subtitle, "If this text is visible, LVGL path works.");
lv_obj_set_width(subtitle, 420);
lv_label_set_long_mode(subtitle, LV_LABEL_LONG_WRAP);
lv_obj_set_style_text_color(subtitle, lv_color_hex(0xD5DCE5), 0);
lv_obj_set_style_text_font(subtitle, &lv_font_montserrat_16, 0);
lv_obj_align(subtitle, LV_ALIGN_TOP_MID, 0, 58);
const char *labels[3] = {"Button One", "Second Button", "Bottom Action"};
const lv_color_t colors[3] = {
lv_color_hex(0x2B4C7E),
lv_color_hex(0x2E8B57),
lv_color_hex(0xB45F06),
};
const lv_coord_t ys[3] = {150, 236, 340};
const lv_coord_t hs[3] = {58, 58, 64};
for (int i = 0; i < 3; i++) {
lv_obj_t *btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, 360, hs[i]);
lv_obj_align(btn, LV_ALIGN_TOP_MID, 0, ys[i]);
lv_obj_set_style_radius(btn, 14, 0);
lv_obj_set_style_bg_color(btn, colors[i], 0);
lv_obj_set_style_border_width(btn, 2, 0);
lv_obj_set_style_border_color(btn, lv_color_hex(0x7F96B0), 0);
lv_obj_t *label = lv_label_create(btn);
lv_label_set_text(label, labels[i]);
lv_obj_set_style_text_font(label, &lv_font_montserrat_20, 0);
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0);
lv_obj_center(label);
}
}
void setup() {
Serial.begin(115200);
Wire.begin(IIC_SDA, IIC_SCL);
gfx->begin();
gBus->writeC8D8(0x36, 0xA0);
gfx->setBrightness(220);
lv_init();
uint32_t screenWidth = gfx->width();
uint32_t screenHeight = gfx->height();
gBuf1 = (lv_color_t *)heap_caps_malloc(screenWidth * screenHeight / 4 * sizeof(lv_color_t), MALLOC_CAP_DMA);
gBuf2 = (lv_color_t *)heap_caps_malloc(screenWidth * screenHeight / 4 * sizeof(lv_color_t), MALLOC_CAP_DMA);
lv_disp_draw_buf_init(&gDrawBuf, gBuf1, gBuf2, screenWidth * screenHeight / 4);
static lv_disp_drv_t dispDrv;
lv_disp_drv_init(&dispDrv);
dispDrv.hor_res = screenWidth;
dispDrv.ver_res = screenHeight;
dispDrv.flush_cb = lvglFlushCb;
dispDrv.rounder_cb = lvglRounderCb;
dispDrv.draw_buf = &gDrawBuf;
lv_disp_drv_register(&dispDrv);
const esp_timer_create_args_t lvglTickTimerArgs = {
.callback = &lvglTick,
.name = "lvgl_tick"};
esp_timer_handle_t lvglTickTimer = nullptr;
esp_timer_create(&lvglTickTimerArgs, &lvglTickTimer);
esp_timer_start_periodic(lvglTickTimer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000);
createUi();
Serial.println("LVGL basic test ready");
}
void loop() {
lv_timer_handler();
delay(5);
}

View File

@ -0,0 +1,214 @@
#include <Arduino.h>
#include <Wire.h>
#include <lvgl.h>
#include <Arduino_GFX_Library.h>
#include "pin_config.h"
#include "TouchDrvCSTXXX.hpp"
#define EXAMPLE_LVGL_TICK_PERIOD_MS 2
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 uint32_t gPressCount = 0;
static TouchDrvCST92xx gTouch;
static int16_t gTouchX[5];
static int16_t gTouchY[5];
Arduino_DataBus *gBus = new Arduino_ESP32QSPI(
LCD_CS, LCD_SCLK, LCD_SDIO0, LCD_SDIO1, LCD_SDIO2, LCD_SDIO3);
Arduino_CO5300 *gfx = new Arduino_CO5300(
gBus, LCD_RESET, 0, LCD_WIDTH, LCD_HEIGHT, 0, 0, 0, 0);
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(EXAMPLE_LVGL_TICK_PERIOD_MS);
}
static void lvglTouchRead(lv_indev_drv_t *indevDrv, lv_indev_data_t *data) {
LV_UNUSED(indevDrv);
uint8_t touched = gTouch.getPoint(gTouchX, gTouchY, gTouch.getSupportTouchPoint());
if (touched > 0) {
data->state = LV_INDEV_STATE_PR;
data->point.x = gTouchX[0];
data->point.y = gTouchY[0];
return;
}
data->state = LV_INDEV_STATE_REL;
}
static void buttonEventCb(lv_event_t *event) {
if (lv_event_get_code(event) != LV_EVENT_CLICKED) {
return;
}
lv_obj_t *btn = lv_event_get_target(event);
const char *name = (const char *)lv_event_get_user_data(event);
gPressCount++;
lv_obj_set_style_outline_width(btn, 4, 0);
lv_obj_set_style_outline_color(btn, lv_color_hex(0xFFF3B0), 0);
lv_obj_set_style_outline_pad(btn, 2, 0);
String status = "Pressed: ";
status += name;
status += " (#";
status += String(gPressCount);
status += ")";
lv_label_set_text(gStatusLabel, status.c_str());
Serial.println(status);
}
static void createButton(lv_obj_t *parent, const char *labelText, lv_coord_t col, lv_coord_t row, uint32_t colorHex) {
static const lv_coord_t kStartX = 22;
static const lv_coord_t kStartY = 122;
static const lv_coord_t kButtonW = 134;
static const lv_coord_t kButtonH = 64;
static const lv_coord_t kGapX = 18;
static const lv_coord_t kGapY = 16;
lv_obj_t *btn = lv_btn_create(parent);
lv_obj_set_size(btn, kButtonW, kButtonH);
lv_obj_set_pos(btn, kStartX + col * (kButtonW + kGapX), kStartY + row * (kButtonH + kGapY));
lv_obj_set_style_radius(btn, 16, 0);
lv_obj_set_style_bg_color(btn, lv_color_hex(colorHex), 0);
lv_obj_set_style_border_width(btn, 2, 0);
lv_obj_set_style_border_color(btn, lv_color_hex(0x7F96B0), 0);
lv_obj_set_style_shadow_width(btn, 0, 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_style_text_font(label, &lv_font_montserrat_18, 0);
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0);
lv_obj_center(label);
}
static void createUi() {
lv_obj_t *screen = lv_scr_act();
lv_obj_set_style_bg_color(screen, lv_color_hex(0x0B1320), 0);
lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, 0);
lv_obj_t *title = lv_label_create(screen);
lv_label_set_text(title, "LVGL INTERACTION TEST");
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_text_font(title, &lv_font_montserrat_22, 0);
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 14);
lv_obj_t *subtitle = lv_label_create(screen);
lv_label_set_text(subtitle, "Tap buttons. Bottom label must change on every press.");
lv_obj_set_width(subtitle, 420);
lv_label_set_long_mode(subtitle, LV_LABEL_LONG_WRAP);
lv_obj_set_style_text_color(subtitle, lv_color_hex(0xD5DCE5), 0);
lv_obj_set_style_text_font(subtitle, &lv_font_montserrat_14, 0);
lv_obj_align(subtitle, LV_ALIGN_TOP_MID, 0, 48);
createButton(screen, "Status", 0, 0, 0x355C7D);
createButton(screen, "Connect", 1, 0, 0x2A9D8F);
createButton(screen, "Wallet", 2, 0, 0x457B9D);
createButton(screen, "Requests", 0, 1, 0xE76F51);
createButton(screen, "Settings", 1, 1, 0x8D5A97);
createButton(screen, "Register", 2, 1, 0x6A994E);
createButton(screen, "Approve", 0, 2, 0xBC6C25);
createButton(screen, "Reject", 1, 2, 0xB56576);
createButton(screen, "Back", 2, 2, 0x6C757D);
lv_obj_t *statusPanel = lv_obj_create(screen);
lv_obj_set_size(statusPanel, 436, 56);
lv_obj_align(statusPanel, LV_ALIGN_BOTTOM_MID, 0, -16);
lv_obj_set_style_radius(statusPanel, 14, 0);
lv_obj_set_style_bg_color(statusPanel, lv_color_hex(0x162033), 0);
lv_obj_set_style_border_width(statusPanel, 2, 0);
lv_obj_set_style_border_color(statusPanel, lv_color_hex(0x415A77), 0);
lv_obj_set_style_pad_all(statusPanel, 10, 0);
gStatusLabel = lv_label_create(statusPanel);
lv_label_set_text(gStatusLabel, "Pressed: none");
lv_obj_set_style_text_font(gStatusLabel, &lv_font_montserrat_18, 0);
lv_obj_set_style_text_color(gStatusLabel, lv_color_hex(0xFFFFFF), 0);
lv_obj_center(gStatusLabel);
}
void setup() {
Serial.begin(115200);
Wire.begin(IIC_SDA, IIC_SCL);
pinMode(TP_RST, OUTPUT);
pinMode(TP_INT, INPUT);
digitalWrite(TP_RST, LOW);
delay(30);
digitalWrite(TP_RST, HIGH);
delay(50);
delay(1000);
gTouch.setPins(TP_RST, TP_INT);
bool touchOk = gTouch.begin(Wire, 0x5A, IIC_SDA, IIC_SCL);
if (!touchOk) {
Serial.println("Touch init failed");
} else {
Serial.print("Touch model: ");
Serial.println(gTouch.getModelName());
gTouch.sleep();
gTouch.reset();
gTouch.setMaxCoordinates(480, 480);
gTouch.setSwapXY(true);
gTouch.setMirrorXY(true, false);
}
gfx->begin();
gBus->writeC8D8(0x36, 0xA0);
gfx->setBrightness(220);
gfx->fillScreen(0x0000);
lv_init();
uint32_t screenWidth = gfx->width();
uint32_t screenHeight = gfx->height();
gBuf1 = (lv_color_t *)heap_caps_malloc(screenWidth * screenHeight / 4 * sizeof(lv_color_t), MALLOC_CAP_DMA);
gBuf2 = (lv_color_t *)heap_caps_malloc(screenWidth * screenHeight / 4 * sizeof(lv_color_t), MALLOC_CAP_DMA);
lv_disp_draw_buf_init(&gDrawBuf, gBuf1, gBuf2, screenWidth * screenHeight / 4);
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 lvglTickTimerArgs = {
.callback = &lvglTick,
.name = "lvgl_tick"};
esp_timer_handle_t lvglTickTimer = nullptr;
esp_timer_create(&lvglTickTimerArgs, &lvglTickTimer);
esp_timer_start_periodic(lvglTickTimer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000);
createUi();
Serial.println("LVGL interaction test ready");
}
void loop() {
lv_timer_handler();
delay(5);
}

View File

@ -0,0 +1,115 @@
#include <Arduino.h>
#include <Wire.h>
#include <Arduino_GFX_Library.h>
#include <U8g2lib.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 DISP_W 480
#define DISP_H 480
#define C_BG 0x0841u
#define C_PANEL 0x1082u
#define C_CARD 0x18C3u
#define C_BORDER 0x39C7u
#define C_TEXT 0xFFFFu
#define C_MUTE 0xBDF7u
#define C_OK 0x3666u
#define C_WARN 0xECA0u
#define C_BAD 0xD145u
#define C_BUTTON 0x2145u
#define C_BUTTON2 0x3186u
#define FONT_HEAD u8g2_font_10x20_t_cyrillic
#define FONT_BODY u8g2_font_9x15_t_cyrillic
#define FONT_SMALL u8g2_font_6x13_t_cyrillic
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);
static void drawPanel(int x, int y, int w, int h, uint16_t fill, uint16_t border, int radius = 10) {
gfx->fillRoundRect(x, y, w, h, radius, fill);
gfx->drawRoundRect(x, y, w, h, radius, border);
}
static void drawDefaultText(int x, int y, const char *text, uint16_t color, uint8_t size = 1) {
gfx->setFont();
gfx->setTextSize(size);
gfx->setTextColor(color);
gfx->setCursor(x, y);
gfx->print(text);
}
static void drawU8Text(int x, int y, const String &text, uint16_t color, const uint8_t *font) {
gfx->setTextSize(1);
gfx->setFont(font);
gfx->setTextColor(color);
gfx->setCursor(x, y);
gfx->print(text);
}
static void drawButton(int x, int y, int w, int h, uint16_t fill, const String &label, const uint8_t *font, bool useDefaultFont = false) {
drawPanel(x, y, w, h, fill, C_BORDER, 12);
if (useDefaultFont) {
drawDefaultText(x + 14, y + 30, label.c_str(), C_TEXT, 2);
return;
}
drawU8Text(x + 14, y + 32, label, C_TEXT, font);
}
static void drawScreen() {
gfx->fillScreen(C_BG);
drawPanel(12, 12, 456, 456, C_PANEL, C_BORDER, 16);
drawDefaultText(28, 42, "TEXT TEST 123", C_TEXT, 2);
drawU8Text(28, 72, "Default ASCII above", C_MUTE, (const uint8_t *)FONT_SMALL);
drawPanel(24, 90, 432, 72, C_CARD, C_BORDER, 12);
drawDefaultText(38, 118, "A: Default font ABC123", C_TEXT, 2);
drawDefaultText(38, 146, "Visible? then base path OK", C_WARN, 1);
drawPanel(24, 174, 432, 84, C_CARD, C_BORDER, 12);
drawU8Text(38, 204, "B: U8g2 ASCII abc123", C_TEXT, (const uint8_t *)FONT_BODY);
drawU8Text(38, 230, "C: Русский текст 123", C_OK, (const uint8_t *)FONT_BODY);
drawPanel(24, 270, 432, 84, C_CARD, C_BORDER, 12);
drawU8Text(38, 298, "D: Мелкий шрифт кнопок", C_TEXT, (const uint8_t *)FONT_SMALL);
drawU8Text(38, 320, "Если это видно, FONT_SMALL жив", C_MUTE, (const uint8_t *)FONT_SMALL);
drawButton(24, 368, 128, 72, C_BUTTON, "BTN 1", nullptr, true);
drawButton(176, 368, 128, 72, C_OK, "abc123", (const uint8_t *)FONT_BODY);
drawButton(328, 368, 128, 72, C_BUTTON2, "Русский", (const uint8_t *)FONT_BODY);
}
void setup() {
Serial.begin(115200);
Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);
gfx->begin();
gBus->writeC8D8(0x36, 0xA0);
gfx->setBrightness(220);
gfx->setUTF8Print(true);
drawScreen();
Serial.println();
Serial.println("=== text_render_test ===");
Serial.println("A: default Arduino_GFX font");
Serial.println("B: U8g2 ASCII");
Serial.println("C: U8g2 Cyrillic");
Serial.println("Buttons: default / ASCII / Cyrillic");
}
void loop() {
delay(1000);
}

View File

@ -1,2 +1,2 @@
client.version=1.2.139
server.version=1.2.131
client.version=1.2.140
server.version=1.2.132