image
Отображение информации это один из важнейших аспектов автоматизированных систем.

В 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. В магазине есть ссылка на папку на гугл-диске с файлами для всей линейки дисплеев данного производителя.

В данном архиве кроме руководства и схем имеются бинарники прошивки, драйвера и библиотеки с примерами, к слову, довольно старые.


Последние версии ставятся из менеджера библиотек 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), на которой у меня крутится «умный дом». «Но это уже совсем другая история ©» которая выходит за рамки обзора данной панели.


Выводы



Недостатки/недоработки:


  1. В схемотехнике панели мне не понравилось, что подсветку можно только включать/выключать. Гораздо лучше, если бы при помощи ШИМ можно было бы регулировать яркость.
  2. Одноканальный ЦАП. А так хотелось бы сделать интернет радио или какой-нибудь MP3 проигрыватель!
  3. Поддержка/коммунити довольно слабая, во всем приходится разбираться.

Но в целом панель просто огонь по соотношению возможности и цены. Пусть пока повисит в качестве метеостанции, но в дальнейшем планирую ей место пульта управления в умном доме.

Данная статья в моем блоге

Комментарии (13)


  1. N-Cube
    15.07.2023 06:09

    Раз уж у вас девайс под рукой, не могли бы вы попробовать на MicroPython примеры: https://blog.lvgl.io/2019-02-20/micropython-bindings Уже смотрел на библиотеку LVGL и совместимые дисплеи, но не ясно, достаточно ли просто это для детей (пару лет назад библиотека вообще без бубна не собиралась, пока скомпилировал, уже некогда было тестировать).


    1. sav13 Автор
      15.07.2023 06:09
      +1

      Там нужно драйвер писать на Си для параллельного дисплея и встраивать его в сборку микропитона. Поковыряюсь, но это все не быстро


      1. N-Cube
        15.07.2023 06:09

        Но чем вас не устраивает готовый микропайтон драйвер, ссылку на который я вам прислал в сообщении выше?..


        1. sav13 Автор
          15.07.2023 06:09
          +1

          По ссылке я нашел только SPI под ESP32
          https://github.com/lvgl/lv_binding_micropython/tree/master/driver/esp32

          Нужен LCD драйвер RGB565 для ESP32S3
          Народ ковыряется в этом направлении, но все пока в процессе. Даже SDK они там под это патчили.
          https://github.com/adafruit/circuitpython/issues/6049


          1. N-Cube
            15.07.2023 06:09

            SPI отлично подходит, для игрушек обычно нужны не более чем 4.5’’ дисплеи (чаще меньше).


    1. zzyxy
      15.07.2023 06:09

      Похоже что micropython + LVGL для esp32-s3 еще не допилили: https://github.com/lvgl/lv_binding_micropython/issues/227


      1. N-Cube
        15.07.2023 06:09

        Уже давно можно было собрать для RPI Pico (кривовато сборка работала, но все же), сделанный из версии ESP32, так что должен работать.


  1. latonita
    15.07.2023 06:09

    А dma или i2s в дисплее нет? Ускорить процесс бы


    1. sav13 Автор
      15.07.2023 06:09

      А там итак DMA

      В ESP32 SDK там esp_lcd интерфейс


  1. NutsUnderline
    15.07.2023 06:09

    я не так давно копался в том каким образом подключаются дисплеи к ESP32 и всем многообразии библиотек. Статья об этом заброшена и вряд ли будет дописана, вот думаю куда бы ее тиснуть в недописаной форме.


    1. beho1der
      15.07.2023 06:09

      было бы интересно почитать


      1. NutsUnderline
        15.07.2023 06:09

        в песочнице


        1. beho1der
          15.07.2023 06:09
          +1

          Выводите)