8 minute(s) de lecture

ESP32-DHT22 Toujours autant sympa ces petits écrans OLED: ils sont faciles à trouver, ne coûtent quasiment rien, très facile à interfacer en micro-python et d’une sobriété exemplaire comparé à un écran LCD ou TFT. Dans cet exemple nous allons utiliser un petit écran OLED I2C 128x64 pixels monochrome que nous allons interfacer avec un ESP32. J’utilise une version PYBSTICK ESP32-C3 mais n’importe quelle autre ESP32 conviendra, sous micro-python.

Circuit électronique

ESP32-DHT22 Avec un écran OLED 2IC il n’y a que 4 fils à relier à la carte: GND, VCC (+3.3v), SCI et SDA: rien de plus !

Code micropython

Bibliothèque ssd1306

Pour piloter l’écran en micropython sur un ESP32, il faut récupérer une bibliothèque très utile que l’on trouve ici sur le Github micropython-esp32: recopiez ce code à la racine de la mémoire flash de votre ESP32 et nommez-le bien ssd1306.py.

Afficher une image

Pour afficher des images sur l’écran il va falloir ruser un peu car la bibliothèque ssd1306 fonctionne avec des frame buffer, grâce auxquels on peut écrire du texte et dessiner quelques figures géométriques basiques (points, lignes, cercles, ellipses, rectangles, polygones). Pour afficher une image, il faudra la convertir en byte array (tableau de codes binaires) (noir et blanc) aux bonnes dimensions pour l’afficher sur l’écran OLED via un frame buffer.

La conversion d’une image (jpg ou png ou tout autre format) en byte array se fait tout simplement grâce à cet excellent convertisseur en ligne:

  • Vous chargez de préférence une image que vous avez déjà redimensionnée et seuillée en binaire noir & blanc via Gimp ou tout autre logiciel de retouche photo.
  • Dans les settings vous choisissez un background noir ou blanc, puis vous redimensionnez l’image au besoin, vous pouvez jouer avec des options de recadrage et rotations.
  • A ce stade vous devriez voir l’aperçu de l’image finale.
  • dans la section output, choisissez: plein bytes - Horizontal - 1 bite per pixel: c’est précisément le format attendu dans un frame buffer.

Il suffit alors de récupérer la longue liste des codes hexadécimaux et de les recopier dans la section ‘data’ des dictionnaires de la classe Logo du code micropython logo.py ci-dessous (à créer sur votre ESP32)

import framebuf

class Logo():
    def __init__(self):
        ''' contructor 
            https://javl.github.io/image2cpp/ --> convert image into bytearray
            output format: plein bytes - Horizontal - 1 bite per pixel
        '''
        
        # logo dictionary
        self.__logos = {           
            # 'nom_logo' : { 'size': (width, hight)
            #                 data : [ ... hex code 1 byte = 8 horizontal pixel ... ]
            #              }
            # self.__logos['nom_logo']['size'] retourne la taille sous forme de tuple (width, height)
            # self.__logos['nom_logo']['data'] retourne la liste des code hexa de l'image
            'papsdroid':
                {'size': (44,64),
                 'data':[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xcf, 0x9f, 0x00, 0x00, 0x00, 0x0f, 
                0xcf, 0x9f, 0x00, 0x00, 0x00, 0x0f, 0xcf, 0x9f, 0x00, 0x00, 0x00, 0x0f, 0xcf, 0x9f, 0x00, 0x00, 
                0x00, 0x0f, 0xcf, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 0x3e, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 0x3e, 0x00, 0x07, 0xcf, 
                0xcf, 0x9f, 0x3e, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 0x3e, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 0x3e, 0x00, 
                0x07, 0xcf, 0xcf, 0x9f, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x07, 0xc0, 0x0f, 0x80, 0x3e, 0x00, 0x07, 0xc0, 0x0f, 0x80, 0x3e, 0x00, 0x07, 0xc0, 
                0x0f, 0x80, 0x3e, 0x00, 0x07, 0xc0, 0x0f, 0x80, 0x3e, 0x00, 0x07, 0xc0, 0x0f, 0x80, 0x3e, 0x00, 
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 
                0x3e, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 0x3e, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 0x3e, 0x00, 0x07, 0xcf, 
                0xcf, 0x9f, 0x3e, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 0x3e, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 
                0x3e, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 0x3e, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 0x3e, 0x00, 0x07, 0xcf, 
                0xcf, 0x9f, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                0x07, 0xc0, 0x00, 0x00, 0x3e, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x3e, 0x00, 0x07, 0xc0, 0x00, 0x00, 
                0x3e, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x3e, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 0x3e, 0x00, 
                0x07, 0xcf, 0xcf, 0x9f, 0x3e, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 0x3e, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 
                0x3e, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 0x3e, 0x00, 0x07, 0xcf, 0xcf, 0x9f, 0x3e, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xcf, 0x9f, 0x00, 0x00, 
                0x00, 0x0f, 0xcf, 0x9f, 0x00, 0x00, 0x00, 0x0f, 0xcf, 0x9f, 0x00, 0x00, 0x00, 0x0f, 0xcf, 0x9f, 
                0x00, 0x00, 0x00, 0x0f, 0xcf, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]},
            
            'pi': {'size': (16,16),
                   'data':[0x0f, 0xfe, 0x3f, 0xff, 0x7f, 0xff, 0xff, 0xfe, 0x06, 0x70, 0x06, 0x60, 0x06, 0x60, 0x0e, 0x60, 
                    0x0e, 0x60, 0x0e, 0x60, 0x0e, 0x60, 0x1e, 0x71, 0x1e, 0x71, 0x1e, 0x7f, 0x1c, 0x3e, 0x00, 0x08]},
            
            'micropython':
                { 'size': (64,64),
                  'data': [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x50, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x3b, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x4c, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x7f, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x87, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x0c, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x41, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x3c, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf8, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xf0, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x19, 0xe1, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x35, 0xe7, 0xfc, 0x00, 0x00, 
                0x00, 0x00, 0x3f, 0xa5, 0xef, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc9, 0xff, 0xff, 0x80, 0x00, 
                0x00, 0x03, 0xff, 0xe5, 0xff, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0x8d, 0xff, 0xfc, 0x80, 0x00, 
                0x00, 0x07, 0xff, 0x81, 0xff, 0xf9, 0x80, 0x00, 0x00, 0x07, 0xff, 0xc3, 0xff, 0xe2, 0x80, 0x00, 
                0x00, 0x07, 0xbe, 0x3b, 0xff, 0xcd, 0xc0, 0x00, 0x00, 0x0f, 0x66, 0x03, 0xff, 0x37, 0xe0, 0x00, 
                0x00, 0x0f, 0x3e, 0x03, 0xff, 0xc7, 0xe0, 0x00, 0x00, 0x1f, 0xe3, 0x83, 0xd0, 0x9f, 0xf0, 0x00, 
                0x00, 0x0f, 0xfe, 0x7a, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x0f, 0xfe, 0x02, 0xff, 0xff, 0xf0, 0x00, 
                0x00, 0x1f, 0xfe, 0x01, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x7b, 0xff, 0x03, 0xff, 0xff, 0xdc, 0x00, 
                0x01, 0xfd, 0xff, 0xcd, 0x7f, 0xfb, 0x9f, 0x00, 0x0f, 0xff, 0x7f, 0x00, 0xff, 0xff, 0x3f, 0xc0, 
                0x3f, 0xff, 0x90, 0x80, 0xfa, 0x32, 0xff, 0xf8, 0x3f, 0xff, 0xf0, 0x7c, 0x74, 0x27, 0xff, 0xcc, 
                0x67, 0xff, 0xff, 0xf1, 0xff, 0xff, 0xfe, 0x7c, 0x7c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfc, 
                0x7f, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xfc, 0x7f, 0xe7, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xfc, 
                0x1f, 0xfc, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xa0, 0x0f, 0xff, 0x9f, 0xff, 0xff, 0xdf, 0xfc, 0x80, 
                0x0c, 0xff, 0xf3, 0xff, 0xfe, 0x7f, 0xf4, 0x70, 0x0e, 0xdf, 0xfc, 0x7f, 0xf3, 0xff, 0xa3, 0x60, 
                0x00, 0xcf, 0xff, 0x9f, 0xdf, 0xfc, 0x97, 0x00, 0x00, 0x6c, 0xbf, 0xf2, 0x7f, 0xf4, 0x30, 0x00, 
                0x00, 0x0c, 0xdf, 0xfc, 0xff, 0xa3, 0x20, 0x00, 0x00, 0x00, 0xcf, 0xff, 0xfc, 0xb3, 0x00, 0x00, 
                0x00, 0x00, 0x4c, 0xff, 0xfc, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x9f, 0xf1, 0x20, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x83, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]},
            }
        
    def get_logo(self, name:str) -> framebuf.FrameBuffer:
        ''' return logo by name into a framebuffer to display
        '''
        w,h = self.__logos[name]['size']
        data = bytearray(self.__logos[name]['data'])
        fbuf = framebuf.FrameBuffer(data, w, h, framebuf.MONO_HLSB)
        return fbuf

Ce code va permettre de transformer un byte array obtenu depuis ce convertisseur en frame buffer, afin d’être affiché directement sur l’écran OLED. Dans cet exemple j’ai 3 images:

  • logo papsdroid en 44x64 pixels
  • le logo de micropython en 64x64 pixels
  • le symbole pi en 16x16 pixels

Ce dictionnaire de logos peut être facilement modifié pour enregistrer plein d’autres images. Respectez bien la structure du dictionnaire avec ces 3 exemples.

Test d’affichage sur écran OLED

ce code va permettre de tester l’affichage sur votre écran OLED. Dans un premier temps il va afficher un rectangle plein tout blanc pendant 3 secondes: ceci permet de visuellement voir s’il y a des pixels morts à l’écran. Ensuite vont s’afficher 3 images définies dans la classe Logo et du texte, le tout encadré dans un rectangle.

Copier et coller ce code sur votre ESP32, nommez le test_oled.py. Attention l’exécution de ce code n’est possible que si vous avez bien préalablement créé les programmes ssd1306.py et logo.py:

from machine import Pin, SoftI2C
from ssd1306 import SSD1306_I2C
from time import sleep
from logo import Logo

class Oled(SSD1306_I2C):
    def __init__(self, scl_pin:int, sda_pin:int, width:int=128, height:int=64):
        self.i2c = SoftI2C(scl=Pin(scl_pin), sda=Pin(sda_pin))
        self.width = width
        self.height = height
        super().__init__(self.width, self.height, self.i2c)
        self.logos = Logo()
    
    def test_screen(self):
        ''' fill the full screen 2s : visual control of the dead pixel '''
        self.fill(1)
        self.show()
        sleep(3)
        self.fill(0)
        self.show()
    
    def display_logo(self, name_logo:str, x:int=0, y:int=0):
        ''' display 'logo' at position (x,y)) '''
        fbuf = self.logos.get_logo(name_logo)
        self.blit(fbuf, x, y)

        
if __name__ == "__main__":
    oled = Oled(scl_pin=1, sda_pin=0)
    oled.test_screen()  
    oled.display_logo('papsdroid',0,0)
    oled.display_logo('pi',112,40)
    oled.display_logo('micropython', 45 ,20)
    oled.text('papsdroid', 45, 8 )
    oled.rect(0,0,128,64,1)
    oled.show()

ESP32-DHT22