Отображение информации это один из важнейших аспектов автоматизированных систем.
В 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 msn",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=%ldn",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 проигрыватель!
- Поддержка/коммунити довольно слабая, во всем приходится разбираться.
Но в целом панель просто огонь по соотношению возможности и цены. Пусть пока повисит в качестве метеостанции, но в дальнейшем планирую ей место пульта управления в умном доме.
Автор:
sav13