Heute möchten wir euch das CrowPanel Pico Entwicklungsboard vorstellen, das einen Raspberry Pi RP2040 kombiniert hat mit einem 3.5 Zoll Touchdisplay. Es ist ein vielseitiges HMI-Modul mit ausreichend Schnittstellen, das als ideal als Human-Machine Interface für Hobbyelektroniker, Bastler und Programmierer für viele Projekte und Tüfteleien interessant sein kann.
Es ist ein Produkt das mir von Elecrow zu Verfügung gestellt wurde, über das ich frei verfügen und berichten darf. Mal schauen was es zu berichten gibt:
Beim betrachten der Hauptmerkmale des Entwicklungsboards, fällt als erstes das 3,5-Zoll große TFT-LCD-Touchscreen mit einer Auflösung von 480×320 Pixeln auf.
Angetrieben wird das Board mit dem bekannten leistungsstarken Raspberry Pi RP2040-Chip, der eine Dual-Core-32-Bit-ARM-Cortex-M0+-CPU mit einer Taktfrequenz von bis zu 133 MHz beinhaltet.
Da 14 GPIO-Verbindungen von außen per Dupont Buchsen erreichbar sind, kann ganz bequem verschiedene Sensoren und Ausgabegeräte zum Testen angeschlossen werden. GPIO 20 und 21 sind noch als zusätzlicher I2C Anschluss vorhanden. Details siehe Pin-Belegungsplan.
Es verfügt über einen SD Card Slot, einen Buzzer und hat ein Batterie Anschluss.
Das Board unterstützt verschiedene Programmiersprachen wie C/C++ und MicroPython und wurde umfassend Dokumentiert für einfachen Einstieg und Benutzung.
Unterstützt wird LVGL (Light and Versatile Graphics Library). Mit LVGL kannst du ansprechende und interaktive Benutzeroberflächen für deine Projekte erstellen, ohne dass du tiefgehende Kenntnisse in Grafikprogrammierung benötigst.
Hardware:
CrowPanel Pico 3.5 Zoll
Spezifikation
Main Chip | RP2040 |
Processor | Dual-core 32-bit ARM Cortex-M0+ @ 133MHz |
Memory | 264kB on-chip SRAM (supports up to 16MB of off-chip flash memory) |
Size | 3.5 inch |
Auflösung | 480*320 |
Signal Interface | SPI |
Touch Type | Resistive Touch |
Panel Type | TFT LCD |
Brightness | 250~350 cd/m² (Typ.) |
Betrachtungswinkel | 70/70/70/70(L/R/U/D) |
Interface | USB, I2C, UART, IO, SD Card Slot und Battery interface |
Leistungseingang | 5V-2A |
Aktiver Bereich | 53.64*71.52mm(W*H) |
Abmessungen | 62.5*104.5*13.5mm(W*H*D) |
Vorbereitung für Arduino IDE 2:
Dann will ich das gute Stück mal testen. Wie immer schreibe ich die einzelnen Schritte hier für euch auf.
Unter Einstellungen zusätzliche Boardverwaltungs URL´s diese Zeile anfügen:
https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
dann unter Boardverwaltung die Library „Raspberry Pi Pico/RP2040“ von Earle F. Philhower suchen und installieren
Nach der Installation dann das Board Raspberry Pi Pico auswählen
Funktionstest
An der Seite gibt es GPIO Pin´s an die ich an Pin 6 eine LED mit der Anode (Langes Beinchen) anklemme, an den vorletzten GND einen 220 Ohm Widerstand das dann am anderen Ende die Katode der LED verbindet.
Das Sketch ist die allezeit bewährte Blink Vorlage. Hier sollte man einen Blick in die Pinbelegung werfen. Da ist zu entnehmen das am Anschluss 6 der GPIO Pin 5 ist!
Pin Belegungsplan:
P1 | GP0/UART0 TX | P9 | GP19 |
P2 | GP1/UART0 RX | P10 | GP20/I2C0 SDA |
P3 | GP2/I2C1 SDA | P11 | GP21/I2C0 SCL |
P4 | GP3/I2C1 SCL | P12 | GP26/ADC1/I2C1 SDA |
P5 | GP4/UART1 TX | P13 | GP27/ADC0/I2C1 SCL |
P6 | GP5/UART1 TX | P14 | GP28/ADC2 |
P7 | GP6/I2C1 SDA | P15 | GND |
P8 | GP7/I2C1 SCL | P16 | VCC 3V3 |
Somit sieht der Test Quellcode so aus:
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(5, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(5, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(5, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
Upload:
Um nun auf das Board diese Sketch aufzuspielen, muss es in den U-Disk Mode (USB-Flash-Laufwerk) gebracht werden.
Verbinde das USB-Kabel mit PC und Board. Drücke auf dem Board die BOOT- und die RESET-Taste gleichzeitig und lasse erst RESET-Taste los und etwas später auch die BOOT-Taste.
In der Arduino IDE kann nun der Port ausgewählt werden.
Erster Test ist bestanden wenn danach die LED wie gewünscht Blinkt 🙂
Verwendung des Bibliotheken Sammlung des Herstellers:
Um in den folgenden Tests das Display inklusive Touchfunktion ansprechen zu können, verwende ich die bereitgestellten Bibliotheken.
Benötigte Links sind auch hier zu finden: CrowPanel PICO HMI 3,5“ Display – Elecrow Wiki
Ich verwende Libraries (Zip) und für abschließenden Test auch schonmal die UI Code&Material (Zip)
Die Bibliotheken Sammlung kommt in den Ordner in dem eure Sketche abgelegt werden und dort unter libraries. Aus der UI Code Zip-Datei entnehme ich die 3.5_UI_Code.zip und entpacke diese in den Sketch Ordner.
Wer nicht genau weiß wo sich dieser Ordner in der Arduino IDE befindet schaue unter Datei / Einstellungen / Pfad für Sketchbook.
Beispiel 1 mit LVGL (Nur Text)
Mit diesem Sketch wird verdeutlicht wie per LVGL Text und Hintergrundfarbe erzeugt wird.
Sketch / Quellcode < > anzeigen
#include <Arduino.h>
#include <lvgl.h>
#include <TFT_eSPI.h>
int led;
//TFT-Hintergrundbeleuchtung-Pin
#define LCD_BL 18
static const uint16_t screenWidth = 480;
static const uint16_t screenHeight = 320;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[ screenWidth * screenHeight / 10 ];
TFT_eSPI tft = TFT_eSPI(); /* TFT instance screenWidth, screenHeight */
/* Display flushing */
void my_disp_flush( lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p )
{
uint32_t w = ( area->x2 - area->x1 + 1 );
uint32_t h = ( area->y2 - area->y1 + 1 );
tft.startWrite();
tft.setAddrWindow( area->x1, area->y1, w, h );
tft.pushColors( ( uint16_t * )&color_p->full, w * h, true );
tft.endWrite();
lv_disp_flush_ready( disp_drv );
}
void setup()
{
Serial.begin( 115200 );
lv_init();
tft.begin(); /* TFT init */
tft.setRotation( 1); /* Landscape orientation, flipped */
tft.invertDisplay(false);/* This line of code turns off the invert function of the display. Normally, the invert function inverts the colors on the screen, i.e. black to white and white to black. In this case, it is set to false, which means that no inversion is performed. */
tft.fillScreen(TFT_BLACK);
// #if LV_USE_LOG != 0
// lv_log_register_print_cb( my_print ); /* register print function for debugging */
// #endif
lv_disp_draw_buf_init( &draw_buf, buf, NULL, screenWidth * screenHeight / 10 );
/*Initialize the display*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init( &disp_drv );
/*Change the following line to your display resolution*/
disp_drv.hor_res = screenWidth;
disp_drv.ver_res = screenHeight;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register( &disp_drv );
tft.fillScreen(TFT_BLACK);
delay(200);
pinMode(LCD_BL,OUTPUT);
// analogWrite(LCD_BL,220);
digitalWrite(LCD_BL, HIGH);
delay(100);
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hallo Prilchen!");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x13b594), 0); // Hintergrund Blau in Hexadezimal
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0); // Schriftfarbe Weiß in Hexadezimal
lv_obj_set_style_text_font(label, &lv_font_montserrat_24, 0); // Schriftgröße 24
}
void loop()
{
lv_timer_handler(); /* let the GUI do its work */
delay( 10 );
}
Ergebnis:
Der Text ist wie gewünscht im Center des Displays
Alles bleibt Schwarz?
Wenn Display nichts anzeigt, die GPIO PIN´s in der Bibliothek anpassen:
Prüfen ob die Anbindung Pico / TFT mit Plan mit der libraries / TFT_eSPI / User_Setup.h übereinstimmt:
Beispiel 2 mit LVGL (Text und Sensordaten)
In diesem Beispiel wird ein DHT20 für I2C an
P10 für Pin 20 für SDA und
P11 für Pin 21 für SCL anschließen
oder fest an die I2C Schnittstelle gesteckt (GND,3,3V,SDA,SCL)
Sketch / Quellcode < > anzeigen
#include <Arduino.h>
#include <lvgl.h>
#include <TFT_eSPI.h>
#include <Wire.h>
#include "DHT20.h"
int led;
//TFT-Hintergrundbeleuchtung-Pin
#define LCD_BL 18
static const uint16_t screenWidth = 480;
static const uint16_t screenHeight = 320;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[screenWidth * screenHeight / 10];
TFT_eSPI tft = TFT_eSPI(); /* TFT instance screenWidth, screenHeight */
DHT20 DHT(&Wire);
#define I2C0_SDA 20
#define I2C0_SCL 21
/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
tft.startWrite();
tft.setAddrWindow(area->x1, area->y1, w, h);
tft.pushColors((uint16_t *)&color_p->full, w * h, true);
tft.endWrite();
lv_disp_flush_ready(disp_drv);
}
// LVGL Label-Objekte
lv_obj_t *label_temp;
lv_obj_t *label_humid;
// Funktion zum Update der DHT20-Daten
void update_dht_data() {
DHT.read(); // Sensor-Daten auslesen
// Werte auf den Labels aktualisieren
char temp_str[32];
snprintf(temp_str, sizeof(temp_str), "Temperatur: %.1f°C", DHT.getTemperature());
lv_label_set_text(label_temp, temp_str);
char humid_str[32];
snprintf(humid_str, sizeof(humid_str), "Feuchtigkeit: %.1f%%", DHT.getHumidity());
lv_label_set_text(label_humid, humid_str);
}
void setup() {
Serial.begin(115200);
// DHT20 initialisieren
Wire.setSDA(I2C0_SDA);
Wire.setSCL(I2C0_SCL);
Wire.begin();
DHT.begin();
lv_init();
tft.begin(); /* TFT init */
tft.setRotation(1); /* Landscape orientation, flipped */
tft.invertDisplay(false); /* This line of code turns off the invert function of the display. Normally, the invert function inverts the colors on the screen, i.e. black to white and white to black. In this case, it is set to false, which means that no inversion is performed. */
tft.fillScreen(TFT_BLACK);
// #if LV_USE_LOG != 0
// lv_log_register_print_cb( my_print ); /* register print function for debugging */
// #endif
lv_disp_draw_buf_init(&draw_buf, buf, NULL, screenWidth * screenHeight / 10);
/*Initialize the display*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
/*Change the following line to your display resolution*/
disp_drv.hor_res = screenWidth;
disp_drv.ver_res = screenHeight;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
tft.fillScreen(TFT_BLACK);
delay(200);
pinMode(LCD_BL, OUTPUT);
// analogWrite(LCD_BL,220);
digitalWrite(LCD_BL, HIGH);
delay(100);
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Sensordaten:");
lv_obj_align(label, LV_ALIGN_CENTER, 0, -100);
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x13b594), 0); // Hintergrund Blau in Hexadezimal
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0); // Schriftfarbe Weiß in Hexadezimal
lv_obj_set_style_text_font(label, &lv_font_montserrat_24, 0); // Schriftgröße 24
// LVGL Label-Objekte erstellen
label_temp = lv_label_create(lv_scr_act());
lv_label_set_text(label_temp, "Temperatur: --.-°C");
lv_obj_align(label_temp, LV_ALIGN_TOP_MID, 0, 150);
label_humid = lv_label_create(lv_scr_act());
lv_label_set_text(label_humid, "Feuchtigkeit: --.-%");
lv_obj_align(label_humid, LV_ALIGN_TOP_MID, 0, 200);
}
void loop() {
static unsigned long last_update = 0;
if (millis() - last_update > 2000) {
last_update = millis();
update_dht_data();
}
lv_timer_handler(); /* let the GUI do its work */
delay(10);
}
Ergebnis:
Dieser Test Upload zeigt jetzt eine Grafik, die nun die Temperatur und Luftfeuchtigkeit anzeigt und umrahmt.
Beispiel 3 mit LVGL (Text, Sensordaten und Touch Funktion die LED steuert)
Die LED die anfangs an PIN 5 angeschlossen wurde, kann nun mit diesem Sketch per Touch gesteuert werden.
Sketch / Quellcode < > anzeigen
#include <Arduino.h>
#include <lvgl.h>
#include <TFT_eSPI.h>
#include <Wire.h>
#include "DHT20.h"
// Pin für die LED
#define LED_PIN 5
//TFT-Hintergrundbeleuchtung-Pin
#define LCD_BL 18
static const uint16_t screenWidth = 480;
static const uint16_t screenHeight = 320;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[screenWidth * screenHeight / 10];
TFT_eSPI tft = TFT_eSPI(); /* TFT instance screenWidth, screenHeight */
DHT20 DHT(&Wire);
#define I2C0_SDA 20
#define I2C0_SCL 21
/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
tft.startWrite();
tft.setAddrWindow(area->x1, area->y1, w, h);
tft.pushColors((uint16_t *)&color_p->full, w * h, true);
tft.endWrite();
lv_disp_flush_ready(disp_drv);
}
uint16_t touchX, touchY; // Touch-Koordinaten
/*Read Touchpad*/
void my_touchpad_read( lv_indev_drv_t * indev_driver, lv_indev_data_t * data )
{
// data->state = LV_INDEV_STATE_REL;
bool touched = tft.getTouch( &touchX, &touchY, 600);
if ( !touched )
{
data->state = LV_INDEV_STATE_REL;
}
else
{
data->state = LV_INDEV_STATE_PR;
/*Setting the coordinates*/
data->point.x = touchX;
data->point.y = touchY;
Serial1.print( "Data x " );
Serial1.println( touchX );
Serial1.print( "Data y " );
Serial1.println( touchY);
}
}
uint16_t calData[5] = { 192, 3590, 335, 3362, 1 }; // Kalibrierungsdaten für den Touchscreen
// LVGL Label-Objekte
lv_obj_t *label_temp;
lv_obj_t *label_humid;
lv_obj_t *button;
// Funktion zum Schalten der LED
void toggle_led(lv_event_t *e) {
static bool led_state = false; // LED-Zustand, initial aus
led_state = !led_state; // LED-Zustand umkehren
if (led_state) {
digitalWrite(LED_PIN, HIGH); // LED einschalten
} else {
digitalWrite(LED_PIN, LOW); // LED ausschalten
}
// Optional: Text im Button ändern
lv_obj_t *btn_label = lv_obj_get_child(button, NULL);
if (led_state) {
lv_label_set_text(btn_label, "LED Aus");
} else {
lv_label_set_text(btn_label, "LED Ein");
}
}
// Funktion zum Update der DHT20-Daten
void update_dht_data() {
DHT.read(); // Sensor-Daten auslesen
// Werte auf den Labels aktualisieren
char temp_str[32];
snprintf(temp_str, sizeof(temp_str), "Temperatur: %.1f°C", DHT.getTemperature());
lv_label_set_text(label_temp, temp_str);
char humid_str[32];
snprintf(humid_str, sizeof(humid_str), "Feuchtigkeit: %.1f%%", DHT.getHumidity());
lv_label_set_text(label_humid, humid_str);
}
void setup() {
Serial.begin(115200);
// Pin für die LED als Ausgang setzen
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW); // Initial die LED aus
// DHT20 initialisieren
Wire.setSDA(I2C0_SDA);
Wire.setSCL(I2C0_SCL);
Wire.begin();
DHT.begin();
lv_init();
tft.begin(); /* TFT init */
tft.setRotation(1); /* Landscape orientation, flipped */
tft.invertDisplay(false); /* This line of code turns off the invert function of the display. Normally, the invert function inverts the colors on the screen, i.e. black to white and white to black. In this case, it is set to false, which means that no inversion is performed. */
tft.fillScreen(TFT_BLACK);
lv_disp_draw_buf_init(&draw_buf, buf, NULL, screenWidth * screenHeight / 10);
/*Initialize the display*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
/*Change the following line to your display resolution*/
disp_drv.hor_res = screenWidth;
disp_drv.ver_res = screenHeight;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
/*Initialize the (dummy) input device driver*/
static lv_indev_drv_t indev_drv;
lv_indev_drv_init( &indev_drv );
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = my_touchpad_read;
lv_indev_drv_register( &indev_drv );
tft.fillScreen(TFT_BLACK);
delay(200);
pinMode(LCD_BL, OUTPUT);
// analogWrite(LCD_BL,220);
digitalWrite(LCD_BL, HIGH);
delay(100);
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Sensordaten:");
lv_obj_align(label, LV_ALIGN_CENTER, 0, -100);
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x13b594), 0); // Hintergrund Blau in Hexadezimal
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0); // Schriftfarbe Weiß in Hexadezimal
lv_obj_set_style_text_font(label, &lv_font_montserrat_24, 0); // Schriftgröße 24
// LVGL Label-Objekte erstellen
label_temp = lv_label_create(lv_scr_act());
lv_label_set_text(label_temp, "Temperatur: --.-°C");
lv_obj_align(label_temp, LV_ALIGN_TOP_MID, 0, 150);
label_humid = lv_label_create(lv_scr_act());
lv_label_set_text(label_humid, "Feuchtigkeit: --.-%");
lv_obj_align(label_humid, LV_ALIGN_TOP_MID, 0, 200);
// Button erstellen
button = lv_btn_create(lv_scr_act());
lv_obj_align(button, LV_ALIGN_BOTTOM_MID, 0, -20); // Button unten mittig platzieren
lv_obj_set_size(button, 150, 50); // Button-Größe
// Text im Button hinzufügen
lv_obj_t *btn_label = lv_label_create(button);
lv_label_set_text(btn_label, "LED Ein/Aus");
// Event-Callback für Button (LED schalten)
lv_obj_add_event_cb(button, toggle_led, LV_EVENT_CLICKED, NULL);
}
void loop() {
static unsigned long last_update = 0;
if (millis() - last_update > 2000) {
last_update = millis();
update_dht_data();
}
lv_timer_handler(); /* let the GUI do its work */
delay(10);
}
Ergebnis:
Die LED kann nun mit antippen auf on / off geschaltet werden.
Beispiel 4: Abschließende Lösung mit individuellen Hintergrund des Herstellers
Die detailierte Anleitung hat der Hrsteller hier zu verfügung gestellt:
Pico HMI 3.5-inch Arduino Tutorial – Elecrow Wiki
Nun wird die Anfangs mit heruntergeladene UI Code&Material (Zip) benötigt.
Im entpackten CrowPanel_PICO_3_5 Ordner, den Sketch CrowPanel_PICO_3_5.ino laden
In diesem abschließendem Beispiel wird wieder der Sensor DHT20 für I2C ausgelesen und gut sichtbar im Display angezeigt und die LED kann auch wieder mit antippen auf on / off geschaltet werden.
Interessant hierbei ist die Grafik Lösung die ohne Programmierung zu einer ansehnlichen Oberfläche verhilft.
LVGL (Light and Versatile Graphics Library)
ist eine kostenlose und quelloffene Grafikbibliothek, die speziell für eingebettete Systeme entwickelt wurde. Sie ermöglicht die Erstellung ansprechender und interaktiver grafischer Benutzeroberflächen (GUIs) mit minimalem Speicherbedarf. LVGL unterstützt eine Vielzahl von Widgets, Animationen, Eingabegeräten und mehrsprachigen Texten. Die Bibliothek ist hardwareunabhängig und kann auf verschiedenen Mikrocontrollern und Displays verwendet werden. Dank ihrer Skalierbarkeit und Anpassungsfähigkeit ist LVGL ideal für Projekte, die eine effiziente und flexible GUI-Lösung benötigen.
Wer sich tiefer in das spannende Thema LVGL einlesen möchte sollte die Projektseite besuchen:
Introduction — LVGL documentation
Dokumentationen:
Produkt:
CrowPanel Pico Display-3,5 Zoll 320 * 480 HMI-Modul TFT LCD Touchscreen mit RP2040 Unterstützung C / C ++ / MicroPython / LVGL
Wiki:
https://www.elecrow.com/wiki/CrowPanel_Pico_HMI_Display-3.5.html
Schema:
CrowPanel Pico Display-3.5_V1.0+240412(1).sch
Fazit:
Das CrowPanel Pico Entwicklungsboard ist eine gute Empfehlung für alle, die mit RP2040 und TFT-LCD-Touchscreen Lösungen erstellen wollen. Die Herstellerinformationen sind umfangreich und erleichtern die Erstellung erheblich. Wie man hier step by step sehen kann, geht die Umsetzung dadurch leicht von der Hand.
Mit seinem leistungsstarken Prozessor, flexiblen Programmierungsoptionen und erweiterbaren Funktionen ist es ein kompaktes Entwicklungsboard das auch für kleine Bedienungskonsolen oder lokalen Informationspanel sich gut eignet.
Und das beste ist, das diese kompakte Microcontrollerlösung mit Touchdisplay weniger kostet als ein einzelnes Display 🙂
Es gibt auch alternativ eine ESP32 Version die hier zu finden ist: ELECROW ESP32 Display
Video: