Publié le 3 décembre 2024 - par

Utilisation de l’écran rond CrowPanel IPS tactile Elecrow 1,28 pouce ESP32

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.

Cliquer pour plus d’infos

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 = 0x10c6self.yellow= 0xffe0

En final j’ai effacé l’affichage (écran noir) et utilisé le bouton sur la montre pour afficher l’heure à la demande…

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

# 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):

    # 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 :

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

...
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

Acheter cet écranWiki de l’écran CrowPanel 1,28 pouce rondDé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

 

À propos François MOCQ

Électronicien d'origine, devenu informaticien, et passionné de nouvelles technologies, formateur en maintenance informatique puis en Réseau et Télécommunications. Dès son arrivée sur le marché, le potentiel offert par Raspberry Pi m’a enthousiasmé j'ai rapidement créé un blog dédié à ce nano-ordinateur (www.framboise314.fr) pour partager cette passion. Auteur de plusieurs livres sur le Raspberry Pi publiés aux Editions ENI.

4 réflexions au sujet de « Utilisation de l’écran rond CrowPanel IPS tactile Elecrow 1,28 pouce ESP32 »

  1. Ping : Utilisation de l’écran rond CrowPanel IPS tactile Elecrow 1,28 pouce ESP32

  2. sophie

    Merci Francois pour ce retour d’expérience qui donne très envies de ‘titiller’ la bête !

    Une idée de la consommation ?  j’avais cru lire que c’était plutôt décevant…

    Cordialement

    Répondre
  3. phil

    Article top et qui m’a donné envie !
    J’ai donc acheté et commencé à tester.

    Je bloque au moment de basculer sous Thonny : impossible d’afficher le repl : il indique de stopper (ou ctrl C), mais cela ne fonctionne pas et je ne reprend pas la main.

    Avez-vous rencontré ce problème ?

    (J’ai reflashé avec le firmware d’origine : tout passe et l’écran fonctionne à nouveau, je n’ai donc pas de pb de port ou de câble)

     

     

    Répondre

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur la façon dont les données de vos commentaires sont traitées.