ESP32-2432S028 mit 2.8" Touchscreen (Cheap Yellow Display)
LVGL-Dialog RGB-LED Color Changer
Bevor ihr mit diesem Artikel startet, solltet ihr euch zuerst ein wenig einlesen und die Hardware und das Pinout des TouchScreen-ESP32 ESP32-2432S028R-kennenlernen.
Außerdem solltet ihr euch eine Entwicklungsumgebung mit PlatformIO einrichten, wie in diesem Artikel besprochen. Danach solltet ihr noch den Artikel Programmierung des Displays mit der TFT_eSPI-Library lesen, in dem ihr lernt, wie wir etwas auf dem Display ausgeben.
Sowie den Artikel über die Programmierung des Touchscreen Digitizer XPT2046, der im Cheap Yellow Display steckt.
Damit wäre dann die Hardware-Ansteuerung von Display und Touchscreen bewerkstelligt. Nun kommt als Dialogsystem die LGVL Library obendrauf. In diesem Artikel erfahrt ihr, wie man diese für die weitere Verwendung einbindet und mit der Standard-LVGL-Demo (die, mit dem das Gerät ausgeliefert wurde) testet.
Im letzten Artikel haben wir dann gelernt, wie man LVGL-Dialoge entwirft und mittels Events steuert.
Aber das war eher langweilige Theorie. Dennoch: mit dem ganzen Wissen sind wir jetzt endlich soweit, unsere eigenen Anwendungen mit LVGL-Dialogen zu kreieren. Ich gebe zu, das war ein langer Anlauf, aber dafür sollten wir in Zukunft viel Zeit sparen, wenn unsere Anwendung Dialoge beinhaltet.
Doc Cool's Utilities
Um die LVGL-Widgets (Begriffserklärungen hier) näher kennenzulernen, will ich im Folgenden einen umgekehrten Ansatz verfolgen: anstatt Widgets und Dialoge laut LVGL-Anleitung nachzubauen, will ich mir eine kleine nützliche Anwendung überlegen und welche Dialogelemente dafür nötig sind. Und dann in der Dokumentation nachschlagen, wie diese eingebunden werden müssen.
"Learning by doing", wie es so schön heißt. Das prägt sich besser ein und hat außerdem den Vorteil, das man am Ende etwas bekommt, was man auch gebrauchen kann.
Diese kleinen nützlichen Programme werden ich unter dem Namen "Doc Cool's Utitlities" zusammenfassen und über einen Menü-Screen zugänglich machen. Da ich bemerkt habe, das es auch einige englischsprachige Leser gibt, versuche ich, die Dialog im Programm in (einfachem) Englisch zu halten, damit mehr Leute etwas davon haben, denn die fertige Firmware wird es wieder zum Download geben.
Außerdem spare ich mir, den kompletten Source-Code immer wieder zu wiederholen. Der steht ja schon in den vorhergehenden Artikeln. Ich werde also nur noch auf die neu hinzugekommenen Sachen eingehen. Das macht die ganzen Erklärungen kürzer und übersichtlicher.
Los geht's: Der erste Bildschirm ist die Titel-Screen. Dieser zeigt den Anwendungsnamen an und einen Info-Hinweis an. Das hatten wir ja schon. Dieser Screen wird ausschließlich mit TFT_eSPI gezeichnet. Die Logo-Grafik ist als 2-Bit-XBitmap im Flash-Speicher definiert und kann mit TFT_eSPI in beliebiger Farbe mit transparentem Hintergrund gezeichnet werden.
Klickt man einmal irgendwo auf den Screen, kommt man zum Menu- oder Auswahl-Screen.
Ab jetzt wird übrigens alles hochkant. Ich habe mich für den Portrait-Modus entschieden, weil sich das Gerät so einfach besser halten lässt. Und es ist so ziemlich die einzige Möglichkeit, die WLAN-Antenne nicht durch die Hand abzudecken, nämlich wenn diese nach oben rausschaut. Dadurch geht der USB-Port nach unten weg. Das ist zwar nicht optimal, aber man kann das CYD schon so halten, dass einen der USB-Stecker nicht stört.
Weitere Antennen kann man über der WLAN-Antenne anbringen. Außerdem kann man natürlich das Gehäuse beliebig breit machen, um "etwas in der Hand zu haben". Sogar ein konkaves Design an den Seiten ist möglich, um das Endgerät besser halten zu können.
Ein Smartphone hält man üblicherweise auch hochkant. Deswegen ab jetzt hochkant. Auch wenn das evtl. den Nachteil hat, dass man nicht so viel nebeneinander darstellen kann, aber das kann man dann ja auch untereinander schreiben.
Auswahl bzw. Menu-Screen
Dieser Screen dient dazu, die einzelnen Utilities aufzuzählen und auswählbar zu machen. Der Einfachheit halber erst einmal als Buttons, solange Platz ist. In der ersten Version haben wir natürlich nur ein Utility, den RGB-LED-Color Changer.
Der Screen besteht aus einer eingeblendeten PNG-Grafik in Graustufen oben links, und der großer Überschrift "Doc Cool's Utitlities" daneben.
Darunter folgen die Buttons zur Auswahl.
Grafiken in LVGL aus dem Flash-Speicher einbinden
Das c-o-o-l-Logo habe ich als PNG-Datei mit 4 Graustufen und einer Größe von 64 x 50 Pixeln erstellt. Nur 4 Farben und auch nur so klein, weil im Flash gespeicherte Bilder natürlich immens Speicher belegen, der dann nicht mehr für Programme zur Verfügung steht. So benötigt die Grafik lediglich 64 x 50 / 8 x 2 = 800 Bytes, plus 16 Bytes für die Palette, macht insgesamt nur 816 Bytes.Die Grafik einzubinden stellte sich als gar nicht so einfach heraus, auch wenn das in der LVGL-Dokumentation so einfach liest. Demnach soll man einfach den Online Image Converter benutzen und den dadurch generierten Code einfügen.
Es scheint auch wirklich sehr einfach: Man lädt seine Grafik-Datei von der Festplatte hoch ("Browse"), woraushin das Filename-Feld ausgefüllt wird, welches man gegebenenfalls aber noch verkürzen sollte, denn dies wird Teil des C-Codes, der generiert wird. Dann wählt man noch das richtige Color-Format aus, wählt als Output "C-Array" und schon wird bei Klick auf "Convert" der C-Code generiert.
Allerdings gibt es zwei Probleme dabei: 1. Welches Color-Format brauche ich und 2. Der Code funktioniert leider nicht einfach so.
Zu 1.: "Alpha" meint, dass das Bild nur Transparent-Daten enthält, was man wohl selten brauchen wird. "CF_INDEXED_?_BIT" steht für Bilder mit einer definierten Farbpalette mit 2 Farben (1_BIT), 4 Farben (2_BIT), 16 Farben (4_BIT) oder 256 Farben (8_BIT). Diese finde ich empfehlenswert, weil sie pro Bildpixel nur maximal 1 Byte (256 Farben, 8_BIT) benötigen. Und bei 2 Farben (1_BIT) passen sogar gleich 8 Pixel in ein Byte. Und mit 256 Farben sehen Grafiken schon recht gut aus. CF_TRUE_COLOR, was 3 Bytes pro Pixel benötigt ist für unser Diplay in jedem Fall zu viel, weil die Hardware maximal 65536 Farben (16 Bit) darstellen kann. Diese erreichen wir schon mit CF_RGB565A8, wobei ich mir nicht nicht komplett sicher bin, ob dies 2 oder 3 Byte pro Pixel benötigt. 2 Byte für die 16-Bit-RGB565-Farbe, aber evtl. noch ein zusätzliches Byte für den Alpha-Kanal für die Transparenz (A8).
Am besten wählt man "CF_INDEXED_?_BIT", wobei man in seinem Grafikprogramm ausprobiert, mit wievielen Bits die Grafik noch halbwegs gut aussieht. Und dran denken: Auf dem kleinen Display des CYD sehen die Farben und die Darstellung eh nicht so knackig und scharf aus wie auf dem PC-Monitor. Dann wählt man die niedrigste Bit-Rate, um Speicher zu sparen, denn der ist kostbar.
Zu 2., der generierten C-Datei. Diese enthält am Anfang einen Include-Abschnitt, dann folgt eine lange Map-Definition mit vielen Hex-Werten, die die Bilddaten ansich, also deren Pixel und Farben enthält. Und am Schluss eine Bilddefinition für LVGL, und bei der liegt das Problem:
const lv_img_dsc_t logo64x50 = {
.header.cf = LV_IMG_CF_INDEXED_2BIT,
.header.always_zero = 0,
.header.reserved = 0,
.header.w = 64,
.header.h = 50,
.data_size = 816,
.data = logo64x50_map,
};
Der Bezeichner ""LV_IMG_CF_INDEXED_2BIT"" ist nicht definiert C/C++(20)
Ein doppelter Kennzeichner ist nicht zulässig. C/C++(2906)
"Klasse "lvjmage_header_t"" hat kein Feld ,,”always_zero',,,. C/C++(136)
Ein doppelter Kennzeichner ist nicht zulässig. C/C++(2906)
"Klasse "lvjmage_header_t"" hat kein Feld ""reserved"". C/C++(136)
Ein doppelter Kennzeichner ist nicht zulässig. C/C++(2906)
Ein doppelter Kennzeichner ist nicht zulässig. C/C++(2906)
Abhilfe schafft da nur, im Source-Code von LVGL zu graben. Ich habe folgendes ausgraben können:
lv_image_dsc.h
typedef struct {
lv_image_header_t header; // A header describing the basics of the image
uint32_t data_size; // Size of the image in bytes
const uint8_t * data; // Pointer to the data of the image
const void * reserved; // A reserved field to make it has same size as lv_draw_buf_t
} lv_image_dsc_t;
typedef struct {
uint32_t magic: 8; // Magic number. Must be LV_IMAGE_HEADER_MAGIC
uint32_t cf : 8; // Color format: See `lv_color_format_t`
uint32_t flags: 16; // Image flags, see `lv_image_flags_t`
uint32_t w: 16;
uint32_t h: 16;
uint32_t stride: 16; // Number of bytes in a row
uint32_t reserved_2: 16; // Reserved to be used later
} lv_image_header_t;
lvcolor.h
enum _lv_color_format_t {
LV_COLOR_FORMAT_UNKNOWN = 0,
LV_COLOR_FORMAT_RAW = 0x01,
LV_COLOR_FORMAT_RAW_ALPHA = 0x02,
/*<=1 byte (+alpha) formats*/
LV_COLOR_FORMAT_L8 = 0x06,
LV_COLOR_FORMAT_I1 = 0x07,
LV_COLOR_FORMAT_I2 = 0x08,
LV_COLOR_FORMAT_I4 = 0x09,
LV_COLOR_FORMAT_I8 = 0x0A,
LV_COLOR_FORMAT_A8 = 0x0E,
/*2 byte (+alpha) formats*/
LV_COLOR_FORMAT_RGB565 = 0x12,
LV_COLOR_FORMAT_ARGB8565 = 0x13, /**< Not supported by sw renderer yet. */
LV_COLOR_FORMAT_RGB565A8 = 0x14 /**< Color array followed by Alpha array*/,
/*3 byte (+alpha) formats*/
LV_COLOR_FORMAT_RGB888 = 0x0F,
LV_COLOR_FORMAT_ARGB8888 = 0x10,
LV_COLOR_FORMAT_XRGB8888 = 0x11,
/*Formats not supported by software renderer but kept here so GPU can use it*/
LV_COLOR_FORMAT_A1 = 0x0B,
LV_COLOR_FORMAT_A2 = 0x0C,
LV_COLOR_FORMAT_A4 = 0x0D,
/* reference to https://wiki.videolan.org/YUV/ */
/*YUV planar formats*/
LV_COLOR_FORMAT_YUV_START = 0x20,
LV_COLOR_FORMAT_I420 = LV_COLOR_FORMAT_YUV_START, /*YUV420 planar(3 plane)*/
LV_COLOR_FORMAT_I422 = 0x21, /*YUV422 planar(3 plane)*/
LV_COLOR_FORMAT_I444 = 0x22, /*YUV444 planar(3 plane)*/
LV_COLOR_FORMAT_I400 = 0x23, /*YUV400 no chroma channel*/
LV_COLOR_FORMAT_NV21 = 0x24, /*YUV420 planar(2 plane), UV plane in 'V, U, V, U'*/
LV_COLOR_FORMAT_NV12 = 0x25, /*YUV420 planar(2 plane), UV plane in 'U, V, U, V'*/
/*YUV packed formats*/
LV_COLOR_FORMAT_YUY2 = 0x26, /*YUV422 packed like 'Y U Y V'*/
LV_COLOR_FORMAT_UYVY = 0x27, /*YUV422 packed like 'U Y V Y'*/
LV_COLOR_FORMAT_YUV_END = LV_COLOR_FORMAT_UYVY,
/*Color formats in which LVGL can render*/
#if LV_COLOR_DEPTH == 8
LV_COLOR_FORMAT_NATIVE = LV_COLOR_FORMAT_L8,
#elif LV_COLOR_DEPTH == 16
LV_COLOR_FORMAT_NATIVE = LV_COLOR_FORMAT_RGB565,
LV_COLOR_FORMAT_NATIVE_WITH_ALPHA = LV_COLOR_FORMAT_RGB565A8,
#elif LV_COLOR_DEPTH == 24
LV_COLOR_FORMAT_NATIVE = LV_COLOR_FORMAT_RGB888,
LV_COLOR_FORMAT_NATIVE_WITH_ALPHA = LV_COLOR_FORMAT_ARGB8888,
#elif LV_COLOR_DEPTH == 32
LV_COLOR_FORMAT_NATIVE = LV_COLOR_FORMAT_XRGB8888,
LV_COLOR_FORMAT_NATIVE_WITH_ALPHA = LV_COLOR_FORMAT_ARGB8888,
#endif
};
Und statt des LV_IMG_CF_INDEXED_2BIT des Online-Converters konnte ich ein LV_COLOR_FORMAT_I2 finden, das für ein 2 Bit-Image (gleich 4 Farben) steht.
Außerdem macht die Art der Angabe der Datenfeld-Inhalte Probleme. Aber die kann man ja auch weglassen, wenn man dann die Reihenfolge einhält. Richtig muss der Abschnitt also heißen:
const lv_image_dsc_t logo64x50 = {
{ LV_IMAGE_HEADER_MAGIC,
LV_COLOR_FORMAT_I2,
0,
64,
50 } ,
.data_size = 816,
.data = logo64x50_map,
};
Das alle auszugraben und herauszufinden hat mich Stunden gekostet. Sowas ist natürlich sehr ärgerlich, die Zeit gibt einem niemand zurück und es hätte ja auch einfach von LVGL 9.1 richtig ausgetestet und aktualisiert werden können. Mittlerweile frage ich mich, ob es nicht besser gewesen wäre, eine ältere Version, die mehr in der freien Wildbahn getestet ist, zu benutzen wie die 8.4.
Zum Glück hatte ich aber im weiteren Verlauf mit den Widgets nicht mehr so massive Probleme, so dass der Rest relativ flott ging.
Überschrift mit anderer Schriftgröße
Die Montserrat Schriftart gefällt mir sehr gut. Ich mag Sans Serif Schriftarten, also welche ohne Serifen (Schnörkel). Ich finde, die sind klarer zu erkennen auf dem Bildschirm und deshalb auch besser und schneller zu lesen. Die alte Papierdruck-Regel "Mit Serifen kann man einen Text besser und schneller lesen" gilt meines Erachtens nicht für Bildschirmtexte, sondern vielleicht eher für Zeitungen mit kleinen Buchstaben und vielen Zeilen und Spalten, aus den man beim Lesen nicht verrutschen sollte. Da helfen eventuell die im Gesamteindruck durch die Serifen entstehende horizontale Linien.Nach dem Motto "Auf dem Bildschirm kostet kein Papier" kann ich auch alle paar Zeilen einen Absatz einfügen, was die Lesbarkeit erhöht und die Gefahr, in den Zeilen zu verrutschen, deutlich verringert. Auf dem Bildschirm muss ich nicht darauf achten, ob der Roman 500 oder 550 Seiten hat. Das kostet nichts extra.
Standardmäßig gibt es bei LVGL nur eine Schriftgröße, nämlich die mit 14pt. Für Labels und Beschreibungstexte ist die Größe gut, aber für Überschriften ist mir 14pt zu klein. Wollen wir größere Schrift benutzen, dann müssen wir LVGL zuallererst anweisen, diese in den Speicher zu laden. Ohne dem können wir sie nicht benutzen. Dies geschieht über die lv_conf.h, einfach alle Schriftarten auf 1 setzen, die wir benutzen wollen:
/*Montserrat fonts with ASCII range and some symbols using bpp = 4
*https://fonts.google.com/specimen/Montserrat*/
#define LV_FONT_MONTSERRAT_8 0
#define LV_FONT_MONTSERRAT_10 0
#define LV_FONT_MONTSERRAT_12 0
#define LV_FONT_MONTSERRAT_14 1
#define LV_FONT_MONTSERRAT_16 1
#define LV_FONT_MONTSERRAT_18 1
#define LV_FONT_MONTSERRAT_20 1
#define LV_FONT_MONTSERRAT_22 1
#define LV_FONT_MONTSERRAT_24 1
#define LV_FONT_MONTSERRAT_26 1
#define LV_FONT_MONTSERRAT_28 1
#define LV_FONT_MONTSERRAT_30 0
#define LV_FONT_MONTSERRAT_32 0
#define LV_FONT_MONTSERRAT_34 0
#define LV_FONT_MONTSERRAT_36 0
#define LV_FONT_MONTSERRAT_38 0
#define LV_FONT_MONTSERRAT_40 0
#define LV_FONT_MONTSERRAT_42 0
#define LV_FONT_MONTSERRAT_44 0
#define LV_FONT_MONTSERRAT_46 0
#define LV_FONT_MONTSERRAT_48 0
Jetzt können wir die geladenen Schriftgrößen zuweisen, zum Beispiel dem Überschriften-Label:
label = lv_label_create(lv_screen_active());
lv_label_set_text(label, "Doc Cool's\nUtilities");
lv_obj_set_style_text_font(label, &lv_font_montserrat_28, 0);
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_LEFT, 0);
lv_obj_align(label, LV_TEXT_ALIGN_LEFT, 85, 5);
Und schon erscheint der Text größer. Außerdem habe ich diesmal den Text im Label linksbündig (LV_TEXT_ALIGN_LEFT) ausgerichtet und das Label selbst mit dem Nullpunkt oben links (LV_TEXT_ALIGN_LEFT).
Buttons mit eigener Farbe und Symbol
Auch gibt es eine schöne Kantenglättung (Antialiasing) bei dieser Schriftart bei LVGL. Die führt allerdings dazu, dass weißer Text auf hellblauem Button, so wie es Standard ist, wenig Kontrast hat. Die weiße Schrift wird an den Rändern durch graue Antialiasing-Punkte mit dem Hintergrund "verschmolzen". Das sieht toll und weniger pixelig aus, aber der Kontrast leidet. Spätestens beim Dokumentieren meiner Demo mit meiner Videokamera fiel mir der geringe Kontrast auf.
Ich sag immer: "Beim Lesen geht Kontrast über alles". Das schließt dann wohl auch ein, dass Lesbarkeit über Fancyness geht. Darum ist es auch nicht schlimm, wenn unser Button mit schwarzer Schrift auf hellgrauem Hintergrund daherkommt. Reinweiß als Hintergrund hätte einen noch höheren Kontrast gehabt, aber den maximalen Kontrast will ich mir aufheben, um besonders wichtige Dinge hervorzuheben.
Damit wir nicht jedem Button einzeln Vorder- und Hintergrundfarbe zuweisen müssen, definieren wir uns einen Style für Buttons, den wir in der globalen Objekt-Variablen style_btn speichern wollen:
static lv_style_t style_btn;
void set_style_btn() {
lv_style_init(&style_btn);
lv_style_set_bg_color(&style_btn, lv_color_hex(0xe8e8e8));
lv_style_set_text_color(&style_btn, lv_color_hex(000000));
}
void set_styles() {
set_style_btn();
}
Im setup() brauche ich so nur einmal set_styles() aufrufen. Hier sammle ich alle Style-Formatierungen. Bisher haben wir ja nur die für den Button. Aber es immer gut, wenn man ein Konzept hat, wo man Code-Erweiterungen hinschreiben soll.In show_screen1(), also der Funktion, in der wir das Aussehen des Menü-Bildschirms definieren, schreiben wir dann:
lv_obj_t *btn1 = lv_button_create(lv_screen_active());
lv_obj_add_event_cb(btn1, evt_men_rgb, LV_EVENT_ALL, NULL);
lv_obj_align(btn1, LV_ALIGN_CENTER, 0, -40);
lv_obj_remove_flag(btn1, LV_OBJ_FLAG_PRESS_LOCK);
lv_obj_add_style(btn1, &style_btn, 0);
label = lv_label_create(btn1);
lv_label_set_text(label, LV_SYMBOL_EYE_OPEN " RGB-LED");
lv_obj_center(label);
Man kann in den Labels von Buttons übrigens auch eine Auswahl von Symbolen verwenden. Diese sind ansich Schriftzeichen, sozusagen Sonderzeichen mit eine bestimmten Aussehen, dass sich der Schriftgröße anpasst. Zur Auswahl stehen die in der LVGL-Dokumentation unter fonts aufgeführten Symbole:
Ich habe das Symbol für das geöffnete Auge benutzt. Den Symbolnamen schreibt man einfach neben den Text wie bei lv_label_set_text(label, LV_SYMBOL_EYE_OPEN " RGB-LED"); und schon erscheint das Symbol im Label.
Screen RGB-LED-Color Changer
Der Aufbau bei diesem Screen ist wie folgt: oben wieder Logo und Überschrift, darunter jeweils für rot (R), grün (G) und blau (B) ein Label und einen Slider. Darunter über die komplette Breite eine Anzeige der gewählten Mischfarbe und darunter 9 Buttons zur direkten Farbwahl von rot, grün, blau, gelb, magenta, cyan, weiß, orange und violett. Und ganz unten rechts einen Back-Button, um ins Menü zurückzukommen.Über den Titelbereich brauche ich wohl nicht viele Worte verlieren, er ist wie im Menü-Screen, nur dass die Schriftart kleiner gewählt ist, weil die Überschrift länger ist.
Slider (Schieberegler) und farbige Labels
Die Slider sind Schiebe-Potentiometer aus dem echten Leben nachempfunden. Man findet sie zum Beispiel in Mischpults und sie werden benutzt, um Werte einzustellen. Je weiter rechts der runde Knopf des Sliders auf der Schiene ist, desto höher ist der eingestellte Wert. Ein Slider besitzt immer einen Wert zwischen 0 (ganz links) und 100 (ganz rechts), er hat also sozusagen immer einen Prozentwert, egal wie breit er ist. Ich finde diese Lösung gut.
Hier beispielhaft die Definition des Rot-Sliders in screen2():
sli_rgb_r = lv_slider_create(lv_screen_active());
lv_obj_add_event_cb (sli_rgb_r, evt_rgbled_sli_r, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_align (sli_rgb_r, LV_ALIGN_TOP_LEFT, 50, 80);
lv_obj_set_style_bg_color(sli_rgb_r, lv_color_hex(0xff0000), 0);
lv_obj_set_width (sli_rgb_r, 175);
Er hat die Hintergrundfarbe rot (lv_color_hex(0xff0000)) bekommen. Das ändert die Farbe der Schiene, auf dem sich der Knopf bewegt. Der Knopf ansich bleibt hellblau. Die Positionierung ist so, dass links Platz für das Label für den Wert bleibt und der Slider die maximal mögliche Breite hat.Die Anzeige der jeweiligen Werte erfolgt links neben der Slider, und zwar mit dem Abkürzung für die Farbe rot, grün oder blau und dem Wert, der allerdings nicht von 1 bis 100 geht, sondern von 0 bis 255 und hexadezimal dargestellt wird (also von 0x00 bis 0xff). Die hexadezimale Darstellung habe ich deswegen gewählt, weil sie einen Vorteil bietet: liest man die Hex - R, G, B - Werte hintereinander hat man gleich die HTML-Farbdefinition, die man mit einem "#" davor in HTML benutzen kann, oder mit einem "0x" davor für die Farben in LVGL.
So kann man den praktischen Zusatznutzen, sich Farben für HTML oder LVGL zusammenzumixen, zu sehen, wie diese auf dem CYD-Bildschirm aussehen und wie die RGB-LED in Wirklichkeit aussieht. Dazu im untenstehenden Demo-Video mehr.
Außerdem sind die Label in der Farbe gehalten, die sie ändern. Damit sollten selbst Menschen, die nicht die lateinische Schriften benutzen schnell dahinterkommen, welchen Slider welche Farbe ändert.
lab_rgb_r = lv_label_create(lv_screen_active());
lv_label_set_text(lab_rgb_r, "R=00");
lv_obj_set_style_text_color(lab_rgb_r, lv_color_hex(0xff0000), 0);
lv_obj_set_style_text_align(lab_rgb_r, LV_TEXT_ALIGN_LEFT, 0);
lv_obj_align(lab_rgb_r, LV_ALIGN_TOP_LEFT, 0, 80);
static void evt_rgbled_sli_r(lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t *obj = (lv_obj_t *) lv_event_get_target(e);
int v=lv_slider_get_value(obj);
v=v*255/100;
val_rgb_r = v;
update_RGB_LED();
}
uint32_t vCol;
lv_color_t col;
ledRGB(val_rgb_r, val_rgb_g, val_rgb_b);
lv_label_set_text_fmt(lab_rgb_r, "R=%02x",val_rgb_r);
lv_label_set_text_fmt(lab_rgb_g, "G=%02x",val_rgb_g);
lv_label_set_text_fmt(lab_rgb_b, "B=%02x",val_rgb_b);
vCol = val_rgb_r*65536 + val_rgb_g*256 + val_rgb_b;
col = lv_color_hex(vCol);
lv_obj_set_style_line_color(lin_rgb_color, col, 0);
Danach wird die Mischfarbe aus R, G und B ausgerechnet und dem Objekt lin_rgb_color übergeben.
Eine Linie als Farbanzeigefeld
Ich habe ein bisschen nachdenken und suchen müssen, um das richtige Dialogelement für einen bunten Kasten zu finden, der einfach nur die Mischfarbe anzeigen soll. Und das bitte schön in genügend großer Größe, dass man sich einen Eindruck über die Farbe machen kann. Als erstes kam mir ein Button in den Sinn, aber da hätte doch irgendwie die Animation gestört, wenn diese geklickt wird. Und die runden Ecken waren ja auch nicht nötig.
Als zweites kam mir ein mit TFT_eSPI gezeichnetes Rechteck in den Sinn. Aber ich wollte eigentlich einen reinen LVGL-Dialog. Der Mischbetrieb von TFT_eSPI und LVGL ist nicht ganz problemlos, siehe letzter Artikel.
Man kann auch in LVGL zeichnen, muss dazu aber eine Zeichenfläche (Canvas) definieren, in die man dann zeichnet. Das wäre eine Lösung, aber es geht noch einfacher.
Uns zwar mittels eine Linie (Line). Die erzeugt selbständig einen Hintergrund, auf den sie sich - je nach Größe zeichnet - und den muss man dann nur noch positionieren. Bei Line denkt man natürlich an dünne Striche, aber es spricht auch nichts dagegen, richtig dicke Striche zu malen - und schwupps, fertig ist das bunte Rechteck:
// Create an array for the points of the line
static lv_point_precise_t line_points[] = { {0, 0}, {240, 0} };
lin_rgb_color = lv_line_create(lv_screen_active());
lv_obj_set_style_line_color(lin_rgb_color, lv_color_hex(0x000000), 0);
lv_obj_set_style_line_width(lin_rgb_color, 50, 0);
lv_line_set_points(lin_rgb_color, line_points, 2); // Set the points
lv_obj_align(lin_rgb_color, LV_ALIGN_TOP_LEFT, 0, 190);
Ein paar zusätzliche Farbauswahl-Buttons
Da noch ein bisschen Platz unter dem Farbanzeigefeld ist füge ich noch ein paar Buttons ein, dass direkt Farbwerte setzen kann für 9 bestimmte Farben.
Als Farben wähle ich die Grundfarben rot, grün und blau, die weiteren Grundmischfarben gelb, magenta, cyan und weiß und zudem noch orange und lila.
Für die Farbauswahl-Buttons nehme ich einfache Buttons ohne Label her und setze deren Hintergrundfarbe auf die Farben, die sie einstellen sollen:
int x = 0;
int y = 225;
int w = 40;
int h = 40;
int abst = 10;
// rot
lv_obj_t *btn1 = lv_button_create(lv_screen_active());
lv_obj_add_event_cb (btn1, evt_rgbled_red, LV_EVENT_ALL, NULL);
lv_obj_remove_flag (btn1, LV_OBJ_FLAG_PRESS_LOCK);
lv_obj_set_style_bg_color (btn1, lv_color_hex(0xff0000), 0);
lv_obj_set_width (btn1, w);
lv_obj_set_height (btn1, h);
lv_obj_align (btn1, LV_ALIGN_TOP_LEFT, x, y);
x = x+w+abst; if (x+w > 240) {x=0; y=y+h+abst;}
Jeder Button bekommt natürlich wieder seinen eigenen Event. Hier hätte man vielleicht auch nur einen einzigen Event erstellen, der das Target-Objekt auswertet, aber ob ich nun x Events oder x "If Objekt = xxx" - Abfragen habe, ist dann auch ziemlich egal. Die erst Variante erschien mir simpler:
static void evt_rgbled_red (lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_CLICKED) {
val_rgb_r = 255; val_rgb_g = 0; val_rgb_b = 0;
update_RGB_Slider();
update_RGB_LED();
}
}
void update_RGB_Slider() {
int r, g, b;
r=val_rgb_r*100/255;
g=val_rgb_g*100/255;
b=val_rgb_b*100/255;
if (sli_rgb_r == NULL || sli_rgb_g == NULL || sli_rgb_b == NULL ) return;
lv_slider_set_value(sli_rgb_r, r, LV_ANIM_OFF);
lv_slider_set_value(sli_rgb_g, g, LV_ANIM_OFF);
lv_slider_set_value(sli_rgb_b, b, LV_ANIM_OFF);
}
beschrieben.
Ganz rechts unten schließt sich dann noch der Zurück-Button an, um ins Utilities-Auswahl-Menü zurückzukommen.
Firmware Download via ESP Web Tools
Du möchtest die Firmware schon einmal ausprobieren, ohne die Entwicklungsumgebung zu installieren oder zu programmieren?
Kein Problem. Schließe einfach dein ESP32 an einen USB-Port deines PC an und klicke auf den deinem Gerät entsprechenden Button:
Demo und Video
Ich habe wieder ein kleine Demonstrations-Video gemacht, dass den RGB-LED-Farbauswahl-Dialog in Aktion zeigt:Mein Fazit zu dieser Programmier-Episode
Das der Online-Converter noch nicht für die Version 9.1 angepasst ist und ich den LVGL-Source-Code genau untersuchen musste, um darauf zu kommen, wie es richtig sein muss, ist eine schwache Leistung. Außerdem habe ich mitbekommen, dass der Colorpicker, den es noch in V 8.4 gab, in V 9.1 ersatzlos rausgeflogen ist. Das wäre doch eine tolle Möglichkeit zur Farbauswahl gewesen. Man entwickelt etwas neues, besseres, dass in eine zukünftigen Version kommt, so heißt es. Aber warum dann nicht das alte Widget solange drinlassen? Wenn ich mir vornehme, ein neues, supertolles Auto zu kaufen, dann benutze ich doch auch mein altes Auto solange weiter und gehe nicht zu Fuß, bis ich das neue dann habe... Das hat den professionellen Eindruck von LVGL, den ich anfangs hatte, doch ein wenig getrübt.Der Rest ging gut von der Hand. Die Slider, der Style, die Objekteigenschaften waren alle nachvollziehbar, aber wenn ich manchmal ein wenig in der Dokumentation suchen musste. Häufig wird auf Überobjekte verwiesen, die dann auf anderen Seiten stehen und man muss sich Infos für den vorliegenden Fall hier und da zusammenklauben. Alternative: man arbeitet sich Ebene für Ebene durch die Objekt-Layer und lernt die quasi auswendig. Das ist mir allerdings zu trocken und zu langwierig. Ich bevorzuge schnelle Ergebnisse für das was ich gerade brauche. Aber auch aus diesem Grund schreibe ich auch diese Tutorials: damit man die Dinge, die man braucht, beisammen hat und nicht von x Dokuseiten zusammensuchen muss. Übrigens war die Doku-Seite bei der Entwicklung mal für ein paar Stunden weg und lieferte überall nur 404er, sodass ich auf archive.org zurückgreifen musste, um weiterprogrammieren zu können. Noch ein trübender Tropfen für die LVGL-Professionalität.
Aber jetzt weiß ich (und ihr) ja, wie es geht und die Lösung der Problemchen steht hier. Das heißt: in Zukunft sollte die Dialog-Entwicklung flotter gehen.
Und von der Bedienbarkeit sind die Widgets hervorragend. Die Slider fühlen sich geschmeidig an. Die Buttons haben eine kleine Animation, sehen gut aus, kriegen mit, wenn man von ihnen "abrutscht" und so weiter. Wie bei eurem "großen" Dialogsystem wie Windows oder Ubuntu.
Weitere Aussichten
Sobald der Bedarf entsteht, oder ich eine nette Idee für das nächste Utility habe, geht es mit dem nächsten Beispiel hier weiter. Dann werden Doc Cool's Utilities um ein weiteres Tool erweitert und es gibt wieder Erklärung und Anleitung, wie man das selbst machen kann.Im nächsten Artikel werden die Utilities um einen Screen Test auf tote Pixel, eine Helligkeitseinstellung und ein Touch-Test und Kalibrierung erweitert.
Quellen, Literaturverweise und weiterführende Links
(1) ESP32-2432S028 mit 2.8" Touchscreen (Cheap Yellow Display) - Vorstellung Hardware und Pinout
(2) ESP32-2432S028 mit 2.8" Touchscreen (Cheap Yellow Display) - Erste Schritte Programmierung der RGB LED
(3) ESP32-2432S028 mit 2.8" Touchscreen (Cheap Yellow Display) - Programmierung des Displays mit der TFT_eSPI-Library
(4) ESP32-2432S028 mit 2.8" Touchscreen (Cheap Yellow Display) - Programmierung des Touchscreen Digitizer XPT2046
(5) ESP32-2432S028 mit 2.8" Touchscreen (Cheap Yellow Display) - Einbinden der LVGL Library
(6) ESP32-2432S028 mit 2.8" Touchscreen (Cheap Yellow Display) - Dialoge entwerfen mit der LVGL Library
(7) LVGL-Dokumentation für Version 9.1
(2) ESP32-2432S028 mit 2.8" Touchscreen (Cheap Yellow Display) - Erste Schritte Programmierung der RGB LED
(3) ESP32-2432S028 mit 2.8" Touchscreen (Cheap Yellow Display) - Programmierung des Displays mit der TFT_eSPI-Library
(4) ESP32-2432S028 mit 2.8" Touchscreen (Cheap Yellow Display) - Programmierung des Touchscreen Digitizer XPT2046
(5) ESP32-2432S028 mit 2.8" Touchscreen (Cheap Yellow Display) - Einbinden der LVGL Library
(6) ESP32-2432S028 mit 2.8" Touchscreen (Cheap Yellow Display) - Dialoge entwerfen mit der LVGL Library
(7) LVGL-Dokumentation für Version 9.1