Estación meteorológica: Pantalla OLED

5 de Octubre, 2024 - Tiempo de lectura: 6 minutos

Sigo añadiendo elementos a mi estación meteorológica. Existen pantallas OLED muy económicas que se pueden incorporar a nuestro proyectos con facilidad. Con un poco de creatividad y con algunas herramientas adicionales se consiguen cosas vistosas.

En este caso elegí una pantalla LED de 128x32 píxeles que comunica por I2C, que emplea el chip SSD1306. Tiene dos pines para alimentación (3.3V) y dos pines para la comunicación I2C. Lo conecté a los pines GP8 y GP9 del microcontrolador. En la comunicación I2C, se conectan los pines SDA de cada elemento entre sí, al igual que los pines SCL.

Cableado OLED

A continuación vamos con la programación. Existe la librería micropython-ssd1306, que nos permite manejar la pantalla OLED fácilmente. Para mostrar texto en la pantalla, la forma más rápida es utilizar el método text(). Por ejemplo, esto mostraría el valor de temperatura y humedad:

oled.text("{}C {}%".format(datos_meteo['Temp_Int'],datos_meteo['Hum_Int']), 0, 10)

El único problema es que el tamaño del texto es muy pequeño. Para mostrar los números más grandes, creé una colección de imágenes (bmp monocromos) con los números del 0 al 9 y símbolos de grados y porcentaje de humedad a un tamaño ajustado a la pantalla:

Cero Uno Dos Tres Cuatro Cinco Seis Siete Ocho Nueve

Lo que nuestros chips entienden son "1s" y "0s", por lo que las imágenes deben ser traducidas a bytes que puedan ser enviados a la pantalla. Para este trabajo me ayudé de esta herramienta: image2cpp. Aunque está pensada para C++, nos vale para Python/MicroPython. Guardé los resultados en un fichero que llamé "rotulos.py".

Por ejemplo el número "2" es este array:

# 'Dos', 25x32px
Rotulo_Dos = bytearray([
    0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x03, 0xff, 0xc0, 0x00, 0x07, 0xff, 0xe0, 0x00, 
    0x0f, 0x83, 0xf0, 0x00, 0x0e, 0x00, 0xf0, 0x00, 0x0c, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x78, 0x00, 
    0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x78, 0x00, 
    0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x01, 0xe0, 0x00, 
    0x00, 0x03, 0xe0, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x0f, 0x00, 0x00, 
    0x00, 0x1e, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 
    0x01, 0xe0, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 
    0x1f, 0xff, 0xfc, 0x00, 0x1f, 0xff, 0xfc, 0x00, 0x1f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00
])

Después el truco es crear una tupla con todos los arrays y acceder a ellos de manera indexada, para componer la imagen:

# Tupla con imagenes de números
imgstack = (Rotulo_Cero, Rotulo_Uno, Rotulo_Dos, Rotulo_Tres, Rotulo_Cuatro, Rotulo_Cinco, Rotulo_Seis, Rotulo_Siete, Rotulo_Ocho, Rotulo_Nueve)

Por ejemplo, en el siguiente fragmento de código muestro la temperatura. Extraigo las decenas del valor leído del sensor y lo muestro a la izquierda del todo (posición 0). Después extraigo las unidades y lo muestro un poco más a la derecha (posición 26). Por último, muestro el símbolo de grados (posición 55). Compongo así la imagen y la muestro por la OLED:

# TEMP INT
# Decenas Temp Int
Decenas = int(datos_meteo['Temp_Int'] / 10)
print(Decenas)
if (Decenas > 0):
    fb = framebuf.FrameBuffer(imgstack[Decenas], 25, 32, framebuf.MONO_HLSB)
else:
    fb = framebuf.FrameBuffer(Rotulo_Blank, 25, 32, framebuf.MONO_HLSB)
oled.blit(fb, 0, 0)

# Unidades T Int
Unidades = datos_meteo['Temp_Int'] % 10
fb = framebuf.FrameBuffer(imgstack[Unidades], 25, 32, framebuf.MONO_HLSB)
oled.blit(fb, 26, 0)

# Grados
fb = framebuf.FrameBuffer(Rotulo_Grados, 45, 32, framebuf.MONO_HLSB)
oled.blit(fb, 55, 0)

oled.show()

Este es el resultado final:

OLED mostrando temperatura y humedad