Отображение информации это один из важнейших аспектов автоматизированных систем.
В IoT системах роль терминала чаще всего выполняет смартфон или компьютер. Но иногда и в умном доме удобно иметь автономную операторскую панель
На сайте Makerfabs в разделе OpenHardware появились интеллектуальные дисплеи Sunton 4.3″, 5″ и 7″ на базе ESP32S3. Из них и герой обзора — панель ESP32-8048S070, купленная на Алиэкспресс за 3000 руб. вместе с доставкой.
Чем мне приглянулось данное устройство:
- Использование нового ESP32S3 позволило подключить дисплей по аппаратному параллельному 16-ти битному интерфейсу, что позволило достичь неплохой скорости выдачи информации на экран (по сравнению с тем же SPI)
- В отличие тех же дисплеев Nextion, обязательно подключаемых к внешнему контроллеру, данный дисплей может работать как автономное устройство
- Открытая понятная схемотехника и программирование напрямую в Arduino IDE или PlatformIO (Те же Nextion или DWIN используют для программирования свой проприоритарный софт)
- Ну и немаловажный фактор для DIY, это цена.
Ну а что там с конкурентами?
Свел в табличку доступные аналогичные дисплеи. Первые два, Nextion и DWIN, программируются своим специализированным софтом и работают только с внешним контроллером по UART интерфейсу. Вывести на них произвольную картинку из интернета не так и просто.
Дисплей от ELECROW очень близок к обозреваемому по схемотехнике и функционалу. Только при покупке на Алиэкспресс дороже почти в полтора раза. И использует другую библиотеку LovyanGFX.
Производитель | NEXTION | DWIN | ELECROW | MAKERFABS |
Марка | NX8048T070 | DMG80480C070 | DIS06043H | ESP32-8048S070 |
Экран |
TFT 800x600 |
|||
Интерфейс | RGB565 16бит | RGB666 18бит | RGB565 16бит | RGB565 16бит |
Сенсор | Резистивный | Емкостной | Емкостной | Емкостной |
MPU | STM32 | T5L ASIC | ESP32S3 N4R8 | ESP32S3 N16R8 |
Программирование | Nextion Editor | DWIN UI Designer | LVGL, LovyanGFX | LVGL, Arduino GFX |
Цена на Али (*) | 9137,59 | 3427,80 | 4466,73 | 3151,68 |
(*) — Цена с доставкой в Пермь на момент написания статьи
Железо
- Микроконтроллер — ESP32-S3-WROOM-1-N16R8 (16Мб flash + 8Мб PSRAM)
- Сенсорный контроллер — емкостной пятиточечный GT911 с интерфейсом I2C распаянный на шлейфе сенсорной панели
- Дисплей TFT 7.0″ 65K цветов 800×480 с драйвером EK9716 c интерфейсом RGB565 16 бит
- ЦАП с усилителем MAX98357
- USB/UART CH320C (аналог «народного» CH340G но без внешнего кварца) с USB-C
- Три импульсных стабилизатора — 3.3В для MCU, питание дисплея, питания подсветки дисплея
Дополнительная периферия
- Разъем UART0 (Параллельно CH340) HC 1.25 x 4
- Разъем под TF карта (SPI IO10, IO11, IO12, IO13)
- Разъем SPI (IO10, IO11, IO12, IO13) HC 1.25 x 4
- Разъем UART (IO17, IO18) + USB (IO19, IO20) HC 1.25 x 4
- Разъем I2C (IO17, IO18) HC 1.25 x 4
- Разъем I2C (IO17, IO18) SH 1.0 x 4
- Разъем на внешний динамик от ЦАП JST 1.25 x 2
Принципиальная схема
Софт
Отдельного WYSISYG редактора интерфейса, как у NEXTION, у данного дисплея нет. Хотя можно использовать Squareline Studio, генерирующий код для фреймвока LVGL. Вместо этого предлагается программирование в среде Arduino IDE. В магазине есть ссылка на папку на гугл-диске с файлами для всей линейки дисплеев данного производителя.
В данном архиве кроме руководства и схем имеются бинарники прошивки, драйвера и библиотеки с примерами, к слову, довольно старые.
- Графическая библиотека RGB Parallel 16 bit Arduino_GFX от Moononournatio (форк Adafruit Arduino_GFX).
- Библиотека для работы с емкостным дисплеем — TAMC_GT911 или GT911 Touch library
- Библиотека для работы с ЦАП MAX98357 ESP32-audioI2S
- Ну и очень мощный фреймворк для разработки пользовательского интерфейса LVGL
Последние версии ставятся из менеджера библиотек Arduino IDE
Программирование
Для программирования нужно взять последнюю ESP32 Core for Arduino IDE с поддержкой новых модулей ESP32S2, ESP32S3 и ESP32C3
Если взять библиотеки из архива, то в принципе, примеры собираются и работают. Но когда я поставил последнюю версию библиотеки Arduino_GFX от Moononournatio, выяснилось, что формат функций работы с шиной RGB 16 bit поменялся. Можно конечно было остаться на старой библиотеке, но есть вероятность рано или поздно получить несовместимость. И придется программировать в старой IDE со старым набором библиотек. Лучше разобраться.
Итак, в старой версии мы имеем такое определение шины и экземпляра GFX с пинами нашего дисплея:
Arduino_ESP32RGBPanel *bus = new Arduino_ESP32RGBPanel(
GFX_NOT_DEFINED /* CS */, GFX_NOT_DEFINED /* SCK */, GFX_NOT_DEFINED /* SDA */,
41 /* DE */, 40 /* VSYNC */, 39 /* HSYNC */, 42 /* PCLK */,
14 /* R0 */, 21 /* R1 */, 47 /* R2 */, 48 /* R3 */, 45 /* R4 */,
9 /* G0 */, 46 /* G1 */, 3 /* G2 */, 8 /* G3 */, 16 /* G4 */, 1 /* G5 */,
15 /* B0 */, 7 /* B1 */, 6 /* B2 */, 5 /* B3 */, 4 /* B4 */
);
// option 1:
// 7寸 50PIN 800*480
Arduino_RPi_DPI_RGBPanel *gfx = new Arduino_RPi_DPI_RGBPanel(
bus,
800 /* width */, 0 /* hsync_polarity */, 210 /* hsync_front_porch */, 30 /* hsync_pulse_width */, 16 /* hsync_back_porch */,
480 /* height */, 0 /* vsync_polarity */, 22 /* vsync_front_porch */, 13 /* vsync_pulse_width */, 10 /* vsync_back_porch */,
1 /* pclk_active_neg */, 16000000 /* prefer_speed */, true /* auto_flush */);
В документации по нашему интерфейсу видим новый формат конструктора Arduino_ESP32RGBPanel. Ну и вместо класса Arduino_RPi_DPI_RGBPanel, используемого во всех примерах, нужно теперь применять Arduino_RGB_Display. Итоговое объявление получилось
таким:
Arduino_ESP32RGBPanel *rgbpanel = new Arduino_ESP32RGBPanel(
41 /* DE */, 40 /* VSYNC */, 39 /* HSYNC */, 42 /* PCLK */,
14 /* R0 */, 21 /* R1 */, 47 /* R2 */, 48 /* R3 */, 45 /* R4 */,
9 /* G0 */, 46 /* G1 */, 3 /* G2 */, 8 /* G3 */, 16 /* G4 */, 1 /* G5 */,
15 /* B0 */, 7 /* B1 */, 6 /* B2 */, 5 /* B3 */, 4 /* B4 */,
0 /* hsync_polarity */, 210 /* hsync_front_porch */, 30 /* hsync_pulse_width */, 16 /* hsync_back_porch */,
0 /* vsync_polarity */, 22 /* vsync_front_porch */, 13 /* vsync_pulse_width */, 10 /* vsync_back_porch */);
Arduino_RGB_Display *gfx = new Arduino_RGB_Display(800, 480, rgbpanel);
Теперь любой пример можно собрать заменив параметры конструктора класса дисплея.
Для сборки всех скетчев Arduino для данного дисплея нужно выбрать в менеджере плат ESP32S3 Dev Module, а в меню модуля выбрать Flash Size — 16MB (128Mb) и PSRAM — OPI PSRAM
Примеры и LvglWidgets и lvgl_music_gt911_7.0 собираются только после того как папка demos библиотеки LVGL помещается внутрь src. Ну и файл lv_cinfig.h из каталога с примером нужно поместить в корневой каталог всех библиотек Arduino
Отображения картинки на ArduinoGFX
Для начала нужно преобразовать саму картинку в двухмерную матрицу (768000 байт на картинку 800х460 16бит/пиксел). Для этого рyководство AdafruitGFX отсылает на онлайновый сервис Image2Cpp.
Полученный результат нужно вставить в файл Img.h И положить его рядом с файлом скетча. Сам скетч предельно простой
Скетч загрузки картинки
#include <Arduino_GFX_Library.h>
#include "Img.h" //Файл с картинкой
#define TFT_BL 2
Arduino_ESP32RGBPanel *rgbpanel;
Arduino_RGB_Display *gfx;
void setup() {
Serial.begin(115200);
delay(1000);
// Инициализация дисплея
rgbpanel = new Arduino_ESP32RGBPanel(
41 /* DE */, 40 /* VSYNC */, 39 /* HSYNC */, 42 /* PCLK */,
14 /* R0 */, 21 /* R1 */, 47 /* R2 */, 48 /* R3 */, 45 /* R4 */,
9 /* G0 */, 46 /* G1 */, 3 /* G2 */, 8 /* G3 */, 16 /* G4 */, 1 /* G5 */,
15 /* B0 */, 7 /* B1 */, 6 /* B2 */, 5 /* B3 */, 4 /* B4 */,
0 /* hsync_polarity */, 210 /* hsync_front_porch */, 30 /* hsync_pulse_width */, 16 /* hsync_back_porch */,
0 /* vsync_polarity */, 22 /* vsync_front_porch */, 13 /* vsync_pulse_width */, 10 /* vsync_back_porch */);
gfx = new Arduino_RGB_Display(800, 480, rgbpanel);
gfx->begin();
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
gfx->fillScreen(BLACK);
// Выдача картинки с подсчетом затраченного времени
uint32_t ms = millis();
gfx->draw16bitRGBBitmap(0,0,(uint16_t *)sav001,800,480);
ms = millis() - ms;
Serial.printf("Load IMG %ld ms\n",ms);
}
void loop() {
delay(1000);
}
Выдача картинки записанной флэш-памяти ESP32 происходит за 61мс, что дает скорость выдачи примерно 12 кадров/сек
Проверка сенсорного экрана
Данный скетч реализует пять простейших экранных кнопок с фиксацией нажатия
Скетч тестирования сенсорного экрана
#include <Arduino_GFX_Library.h>
#include <Wire.h>
#include <TAMC_GT911.h>
#define TFT_BL 2
#define BTN_HEIGHT 100
#define BTN_WIDTH 150
struct btn_t {
int x;
int y;
char text[10];
bool enable;
} btns[] = {
{0,100,"BTN01",false},
{160,100,"BTN02",false},
{320,100,"BTN03",false},
{480,100,"BTN04",false},
{640,100,"BTN05",false}
};
int btn_n = 5;
Arduino_ESP32RGBPanel *rgbpanel;
Arduino_RGB_Display *gfx;
TAMC_GT911 tp(19, 20, -1, 38, 800, 480);
void setup() {
Serial.begin(115200); //Инициализация порта отладки
delay(1000);
tp.begin(); // Инициализация сенсорного экрана
tp.setRotation(ROTATION_INVERTED);
// Инициализация дисплея
rgbpanel = new Arduino_ESP32RGBPanel(
41 /* DE */, 40 /* VSYNC */, 39 /* HSYNC */, 42 /* PCLK */,
14 /* R0 */, 21 /* R1 */, 47 /* R2 */, 48 /* R3 */, 45 /* R4 */,
9 /* G0 */, 46 /* G1 */, 3 /* G2 */, 8 /* G3 */, 16 /* G4 */, 1 /* G5 */,
15 /* B0 */, 7 /* B1 */, 6 /* B2 */, 5 /* B3 */, 4 /* B4 */,
0 /* hsync_polarity */, 210 /* hsync_front_porch */, 30 /* hsync_pulse_width */, 16 /* hsync_back_porch */,
0 /* vsync_polarity */, 22 /* vsync_front_porch */, 13 /* vsync_pulse_width */, 10 /* vsync_back_porch */);
gfx = new Arduino_RGB_Display(800, 480, rgbpanel);
gfx->begin();
pinMode(TFT_BL, OUTPUT); //Включение дисплея
digitalWrite(TFT_BL, HIGH);
gfx->fillScreen(BLACK);
for( int i=0; i<btn_n; i++)displayBTN(i); //Начальная отрисовка кнопок
}
uint32_t ms0 = 0;
void loop() {
uint32_t ms = millis();
tp.read();
if( ms0 == 0 || ms - ms0 > 500 ){ // Убираем дребезг виртуальных кнопок (задержка 0.5 сек)
if (tp.isTouched){
ms0 = ms;
int x = tp.points[0].x;
int y = tp.points[0].y;
int n_new = -1;
int n_old = -1;
for( int i=0; i<btn_n; i++){
if( btns[i].enable )n_old = i;
if( x > btns[i].x && x < btns[i].x+BTN_WIDTH && y > btns[i].y && y < btns[i].y+BTN_HEIGHT )n_new = i;
}
Serial.printf("x=%d y=%d old=%d new=%d ms=%ld\n",x,y,n_old,n_new,ms0);
if( n_new >= 0 && n_old == n_new ){ //Если нажата та же кнопка, инвертируем состояние
btns[n_new].enable = !btns[n_new].enable;
displayBTN(n_new);
}
else if( n_new >=0 ){ //Фиксируем нажатую кнопку
btns[n_new].enable = true;
displayBTN(n_new);
if( n_old >= 0 ){ //Убираем предыдущую кнопку
btns[n_old].enable = false;
displayBTN(n_old);
}
}
}
}
}
// Отображение одной кнопки из структуры
void displayBTN(int i){
if( i<0 || i>= btn_n )return;
struct btn_t btn = btns[i];
uint16_t color_back = BLACK, color_border = LIGHTGREY, color_text = DARKGREEN;
if( btn.enable ){
color_back = DARKGREEN;
color_border = LIGHTGREY;
color_text = WHITE;
}
gfx->fillRect(btn.x,btn.y,BTN_WIDTH,BTN_HEIGHT,color_back);
gfx->drawRect(btn.x,btn.y,BTN_WIDTH,BTN_HEIGHT,color_border);
gfx->drawRect(btn.x+1,btn.y+1,BTN_WIDTH-2,BTN_HEIGHT-2,color_border);
gfx->setCursor(btn.x+20, btn.y+30);
gfx->setTextSize(4);
gfx->setTextColor(color_text);
gfx->print(btn.text);
}
Что-то полезное
В интернете ходит выражение «Что вы вы не делали из ESP, получится метеостанция» )))
Вот и у меня получилась метеостанция. Корпус на 3Д принтере:
и скетч выдающий на экран время, дату, пробки в городе, температуру в комнате, за окном и прогноз. Приводить его здесь не буду, так как все это завязано на IoT систему ThingBoard (открывается только через VPN или Proxy), на которой у меня крутится «умный дом». «Но это уже совсем другая история ©» которая выходит за рамки обзора данной панели.
Выводы
Недостатки/недоработки:
- В схемотехнике панели мне не понравилось, что подсветку можно только включать/выключать. Гораздо лучше, если бы при помощи ШИМ можно было бы регулировать яркость.
- Одноканальный ЦАП. А так хотелось бы сделать интернет радио или какой-нибудь MP3 проигрыватель!
- Поддержка/коммунити довольно слабая, во всем приходится разбираться.
Но в целом панель просто огонь по соотношению возможности и цены. Пусть пока повисит в качестве метеостанции, но в дальнейшем планирую ей место пульта управления в умном доме.
Данная статья в моем блоге
Комментарии (13)
NutsUnderline
15.07.2023 06:09я не так давно копался в том каким образом подключаются дисплеи к ESP32 и всем многообразии библиотек. Статья об этом заброшена и вряд ли будет дописана, вот думаю куда бы ее тиснуть в недописаной форме.
N-Cube
Раз уж у вас девайс под рукой, не могли бы вы попробовать на MicroPython примеры: https://blog.lvgl.io/2019-02-20/micropython-bindings Уже смотрел на библиотеку LVGL и совместимые дисплеи, но не ясно, достаточно ли просто это для детей (пару лет назад библиотека вообще без бубна не собиралась, пока скомпилировал, уже некогда было тестировать).
sav13 Автор
Там нужно драйвер писать на Си для параллельного дисплея и встраивать его в сборку микропитона. Поковыряюсь, но это все не быстро
N-Cube
Но чем вас не устраивает готовый микропайтон драйвер, ссылку на который я вам прислал в сообщении выше?..
sav13 Автор
По ссылке я нашел только SPI под ESP32
https://github.com/lvgl/lv_binding_micropython/tree/master/driver/esp32
Нужен LCD драйвер RGB565 для ESP32S3
Народ ковыряется в этом направлении, но все пока в процессе. Даже SDK они там под это патчили.
https://github.com/adafruit/circuitpython/issues/6049
N-Cube
SPI отлично подходит, для игрушек обычно нужны не более чем 4.5’’ дисплеи (чаще меньше).
zzyxy
Похоже что micropython + LVGL для esp32-s3 еще не допилили: https://github.com/lvgl/lv_binding_micropython/issues/227
N-Cube
Уже давно можно было собрать для RPI Pico (кривовато сборка работала, но все же), сделанный из версии ESP32, так что должен работать.