Après la présentation de l’écran tactile circulaire équipé d’un ESP32 C3 que propose Elecrow, j’ai voulu tester l’afficheur en microPython, avec Thonny. Vous trouverez dans cet article les étapes de l’installation de microPython et les premiers tests.
Au sommaire :
Utilisation de l’écran rond CrowPanel IPS tactile Elecrow 1,28 pouce
Installation de microPython
On va commencer par effacer la mémoire Flash pour partir avec un ESP32-C3 « propre ». J’ai utilisé Python sur un PC sous Windows 11, ainsi que Thonny
J’ai créé un dossier crowpanel puis environnement virtuel Python dans ce dossier :
python3 -m venv crowpanel
On peut ensuite activer l’environnement virtuel
En fin d’utilisation on sortira proprement avec la commande deactivate, mais c’est pour plus tard, quand on aura fini.
Puis installer esptool dans cet environnement
(crowpanel) pi@framboise314:~ $ pip install –upgrade esptool
Ensuite on peut effacer la mémoire Flash de l’ESP32-C3
(crowpanel) D:\Elecrow_CrowPanel1.28>python -m esptool –port COM3 –chip esp32c3 erase_flash
Enfin installer microPython (j’ai utilisé la version fournie dans le dossier de Elecrow).
(crowpanel) D:\Elecrow_CrowPanel1.28>python -m esptool –port COM3 -b 460800 –before default_reset –after hard_reset –chip esp32c3 write_flash –flash_mode dio –flash_size detect –flash_freq 40m 0x0 MicroPython-1.28-Demo\firmware\esp32C3_1.2.4_micropython.bin
On a fini la préparation de l’ESP32-C3 en Python, vous pouvez sortir de l’environnement virtuel
(crowpanel) pi@framboise314:~ $ deactivate
Vous pouvez maintenant télécharger les démos
https://www.elecrow.com/download/product/CrowPanel/ESP32-HMI/1.28-DIS12824D/MicroPython-1.28-Demo.zip
puis décompresser l’archive
Le microPython avec Thonny
On peut alors lancer Thonny
Et on se retrouve avec le prompt du microPython qu’on vient d’installer sur l’ESP32c3, prêt à travailler…
Les exemples sont fournis par Elecrow et c’est ce que j’ai testé.
Test de l’horloge RTC avec affichage de l’heure dans la console. Pour info, arrivé de chez Elecrow depuis une bonne semaine, l’écran est toujours à l’heure exacte.
Test du bouton « utilisateur » la console indique quand le bouton est appuyé.
Test du buzzer. J’ai aussi testé le vibreur, puis écrit un programme pour personnaliser l’affichage :
Programme final
Dans la bibliothèque gc9a01.py vous pouvez rajouter les couleurs qui vous intéressent
self.pink = 0x10c6
self.yellow= 0xffe0
En final j’ai effacé l’affichage (écran noir) et utilisé le bouton sur la montre pour afficher l’heure à la demande…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
from machine import Pin,I2C,RTC from pi4ioe5v6408ztaex import PI4IOE5V6408 from gc9a01 import LCD_1inch28 from bm8563rtc import PCF8563 # Import the PCF8563 module for interfacing with the PCF8563 real-time clock import time # Import the time module for sleep functions # Set the GPIO pin number where the button is connected, GPIO 1 is used as an example button_pin = 1 # Initialize the button, set as input mode, and enable the internal pull-up resistor button = Pin(button_pin, Pin.IN, Pin.PULL_UP) # Initialize the I2C bus with the specified pins and frequency i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=400_000) # Create an instance of the PCF8563 real-time clock module bm = PCF8563(i2c) # Create an instance of the machine's RTC module rtc = RTC() # Create an instance of the PI4IOE5V6408 class with the I2C bus io_expander = PI4IOE5V6408(i2c) io_expander.write_pin(4, True) time.sleep(1) io_expander.write_pin(2, True) time.sleep(1) LCD = LCD_1inch28() #LCD.write_text("Framboise314",25,120,2,LCD.blue) #Eteindre l ecran LCD.fill(LCD.black) LCD.show() time.sleep(0.5) # Create an instance of the PCF8563 real-time clock module bm = PCF8563(i2c) # Create an instance of the machine's RTC module rtc = RTC() # Jours de la semaine week = ['Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi', 'Dimanche'] def Time(): global hour, minute, second, weekday, mday, month, year # Usage global des variables # Define a list of week days # Check if the year from the PCF8563 module is not the current year if bm.datetime()[0] != 2023: # Get the date and time from the machine's RTC module date = rtc.datetime() # Set the date and time to the bm8563 module # Note: The datetime tuple is in the format (year, month, day, hour, minute, second, weekday) bm.datetime((date[0], date[1], date[2], date[4], date[5], date[6], date[3])) # Wait for 1 second to allow the time to be set time.sleep(0.1) # Get the current year, month, day, hour, minute, second, and weekday from the bm8563 module year = bm.datetime()[0] month = bm.datetime()[1] mday = bm.datetime()[2] hour = bm.datetime()[3] minute = bm.datetime()[4] second = bm.datetime()[5] weekday = bm.datetime()[6] # Print the current date and time to the serial console print(year, month, mday, hour, minute, second, week[weekday]) # Wait for 1 second before printing the next time update while True: Time() heure = f"{hour:02}:{minute:02}:{second:02}" # Formater la date pour l'affichage date = f"{mday:02}/{month:02}/{year}" LCD.fill(LCD.blue) LCD.write_text("Framboise314",25,80,2,LCD.yellow) LCD.write_text(heure, 25, 110, 3, LCD.white) LCD.write_text(week[weekday], 80, 180, 1, LCD.green) LCD.write_text(date, 80, 190, 1, LCD.pink) if button.value() == 0: print("Button is currently pressed") # Additional code can be added here to handle the logic when the button is pressed # Afficher l'écran LCD.show() else: # Afficher l'écran LCD.fill(LCD.black) LCD.show() time.sleep(0.1) # Simple debounce delay |
C’est l’assemblage de plusieurs programmes de démo fournis par Elecrow, avec un petit aménagement maison… J’ai aussi ajouté quelques couleurs dans la bibli de l’écran, on est en BGR sur 16 bits 😉
Après on peut modifier le programme pour laisser l’écran allumé 2 ou 3 secondes quand on appuie… je vous laisse vous amuser.
Ajouter des images
Passer de BMP à RGB565
Le format BMP est plutôt complexe avec une entête et des couleurs codées sur 3 octets (RVB) soit 24 bits.
Pour afficher sur l’écran CrowPanel on a besoin d’un fichier de données brutes au format RGB565 sans entête.
Première étape préparer un fichier bmp de 240x240px (j’ai pris mon logo) et le transformer de .bmp en RGB565. J’ai utilisé ce programme https://github.com/liyanboy74/bmp24-to-rgb565 que j’ai compilé sur un Raspberry Pi 5 qui a servi aussi à convertir les fichiers en RGB565.
Il suffit ensuite de lancer le programme Bmp24ToRGB565. Saisir le nom du fichier .bmp SANS L’EXTENSION (ici bluemarble) puis n et il génère un fichier .h qui contient…
Ici bluemarble est un tableau (ou array) de valeurs de type uint16_t ( uint16_t est un type de donnée représentant des valeurs entières positives sur 16 bits).C’est un tableau de 240*240 éléments, ce qui signifie qu’il peut contenir 57 600 valeurs uint16_t.
Mais ce n’est pas ce dont on a besoin, il nous faut juste les données. En plus ici elles sont en caractères hexa. J’ai donc écrit un bout de programme en Python pour « nettoyer » ce fichier (ne garder que les octets) et les écrire en binaire dans un autre fichier qui sera celui qu’on enverra à l’écran, enfin au framebuffer de l’écran !
Programme conv_bmp_565.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# Programme de conversion d'un fichier structure C RGB565 en code # "brut" utilisable par l'écran du CrowPanel 1,28 pouce # Extrait les 240x240 mots de 16 bits et les envoie dans un fichier # Premier argument = nom du fishier à traiter # Second argument = nom du fichier de sortie # CC BY NC SA Framboise314 dec 2024 # Importer les bibliothèques utilisées import re import sys #Extraction des données dans le fichier de départ def extract_rgb565(input_file, output_file): # Lire le contenu du fichier d'entrée with open(input_file, 'r') as file: logo_data = file.read() # Extraire les valeurs hexadécimales hex_values = re.findall(r'0x[0-9A-Fa-f]+', logo_data) # Convertir les valeurs en binaire et les écrire dans un fichier with open(output_file, 'wb') as f: for value in hex_values: # Convertir chaque valeur hexadécimale en entier int_value = int(value, 16) # Convertir l'entier en bytes (2 octets, big-endian) f.write(int_value.to_bytes(2, byteorder='big')) print(f"Fichier binaire '{output_file}' créé avec succès.") if __name__ == "__main__": if len(sys.argv) != 3: print("Usage : python script.py <fichier_entrée> ") else: input_file = sys.argv[1] output_file = sys.argv[2] extract_rgb565(input_file, output_file) |
Pour exécuter ce programme :
python conv_bmp_565.py bluemarble.h bluemarble.bin
Fichier binaire ‘bluemarble.bin’ créé avec succès.
Enfin pour l’affichage de ce fichier sur l’écran, j’ai ajouté une fonction à la bibliothèque microPython fournie par Elecrow (gc9a01.py) dans le dossier démo microPython. Ajouter cette fonction dans la classe class LCD_1inch28(framebuf.FrameBuffer):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# Nouvelle méthode pour afficher un RGB565 def afficher_image(self, filename): # Taille des blocs à lire block_size = 240 * 20 * 2 # 20 lignes de pixels with open(filename, 'rb') as f: for y in range(0, self.height, 20): buffer = bytearray(block_size) f.readinto(buffer) # Vérification de la taille des données lues pour chaque bloc if len(buffer) != block_size: print(f"Erreur : Taille des données lues pour le bloc ({len(buffer)}) ne correspond pas à la taille attendue ({block_size}).") return for i in range(20): self.blit(framebuf.FrameBuffer(buffer[i*240*2:(i+1)*240*2], 240, 1, framebuf.RGB565), 0, y+i) self.show() |
L’affichage en une fois produisait une erreur (sans doute due à la taille de mémoire utilisée) en faisant un affichage par blocs ça fonctionne et c’est aussi rapide… Le programme suivant est identique au précédent, mais on affiche le logo et on passe à l’affichage de l’heure quand on appuie sur le bouton.
Le programme d’affichage devient :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
while True: Time() heure = f"{hour:02}:{minute:02}:{second:02}" # Formater la date pour l'affichage date = f"{mday:02}/{month:02}/{year}" LCD.fill(LCD.blue) LCD.write_text("Framboise314",25,80,2,LCD.yellow) LCD.write_text(heure, 25, 110, 3, LCD.white) LCD.write_text(week[weekday], 80, 180, 1, LCD.green) LCD.write_text(date, 80, 190, 1, LCD.pink) if button.value() == 0: print("Button is currently pressed") # Additional code can be added here to handle the logic when the button is pressed # Afficher l'écran LCD.show() else: # Afficher l'écran LCD.afficher_image('logo565.bin') LCD.show() time.sleep(0.1) |
et avec plusieurs images :
La fin du programme devient
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
... logos = ['logo565.bin', 'bigbuckbunny.bin', 'bluemarble.bin'] current_logo_index = 0 logo_display_time = 2.0 # Temps d'affichage de chaque logo en secondes check_interval = 0.1 # Intervalle de vérification en secondes while True: if button.value() == 0: Time() heure = f"{hour:02}:{minute:02}:{second:02}" date = f"{mday:02}/{month:02}/{year}" LCD.fill(LCD.blue) LCD.write_text("Framboise314", 25, 80, 2, LCD.yellow) LCD.write_text(heure, 25, 110, 3, LCD.white) LCD.write_text(week[weekday], 80, 180, 1, LCD.green) LCD.write_text(date, 80, 190, 1, LCD.pink) print("Le bouton est appuyé") LCD.show() else: # Afficher le logo actuel start_time = time.time() while time.time() - start_time < logo_display_time: # Vérifier périodiquement l'état du bouton # Si le bouton est appuyé on sort immédiatement if button.value() == 0: break LCD.afficher_image(logos[current_logo_index]) LCD.show() time.sleep(check_interval) # Passer au logo suivant si le bouton n'a pas été pressé if button.value() != 0: current_logo_index = (current_logo_index + 1) % len(logos) # Petite pause pour limiter l'utilisation du CPU time.sleep(check_interval) |
et on obtient :
Vidéo
Conclusion
Pour un écran qui coûte moins de 14$ on a des possibilités intéressantes. Je n’ai pas encore testé le Wifi ou le BT, mais on peut imaginer de se faire sa propre montre, chrono, d’afficher des infos etc… de quoi s’amuser pour pas cher.
Source
Wiki de l’écran CrowPanel 1,28 pouce rond
Démonstration des possibilités par Felix Biego
https://openclassrooms.com/fr/courses/6951236-mettez-en-place-votre-environnement-python/7014018-creez-votre-premier-environnement-virtuel
https://forum.mchobby.be/viewtopic.php?t=750
https://github.com/russhughes/gc9a01_mpy
https://github.com/liyanboy74/bmp24-to-rgb565