PANTALLAS OLED
Las pantallas OLED (Organic light-emitting diode o diodos emisores de luz orgánicos) son una excelente opción para mostrar datos en los proyectos de electrónica.
Son pantallas formadas por diodos (orgánicos) individuales. que emiten su propia luz. Esto las permite tener un alto contraste, un gran ángulo de visión y un reducido espesor.
MicroPython dispone de una librería (no estándar) para pantallas monocromas OLED con el controlador SSD1306 y bus de comunicación I2C o SPI.
Dado que el tipo más habitual de este modelo de pantallas que podemos encontrar en el mercado, son las de 128×64 pixels de resolución – 0.96″ (25×14 mm) y bus de comunicaciones el I2C, se va a desarrollar su uso para poder conocer las opciones de texto y gráficas (puntos, líneas, rectángulos e imágenes) que están disponibles en la librería.
LA LIBRERÍA SSD1306
La librería (no estándar) para pantallas monocromas OLED con el controladores SSD1306 y bus de comunicación I2C o SPI para MicroPython se puede localizar en GitHub:
https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py
Se utilizará grabándola con el el nombre ssd1306.py en el directorio /lib. Este directorio está destinado –por defecto– a guardar los módulos y librerías complementarias de una forma organizada.
Si no existe el directorio /lib (no aparece en el apartado Micropython device de la ventana Archivos) se puede crear con Thonny haciendo clic sobre ese apartado, con el botón derecho del ratón y después clic en Nuevo directorio…
A continuación, en la ventana de New directory se deberá escribir lib y hacer clic en OK.
Para subirla a la librería a memoria flash de la placa, lo más sencillo será copiar el código desde Github al editor de código de Thonny y una vez hecho guardarlo en el archivo ssd1306.py dentro del directorio /lib.
Para guardarlo se hará clic en Archivo/Guardar como…/MicroPython device. En la ventana Save to MicroPython device se seleccionará el directorio /lib y se rellenará el nombre del fichero (ssd1306.py). Por último se hará clic en OK.
CONEXIÓN I2C DE LAS PANTALLAS OLED CON EL MICROCONTROLADOR ESP32
Para conectar la pantalla se utilizan dos cables para la alimentación eléctrica (voltaje: VCC -3.3V- y tierra: GND -Ground-) y dos cables para el bus de comunicaciones I2C (datos: SDA –Serial Data– y reloj: SCL –Serial Clock-).
Los pines que se van a utilizar para el bus de comunicaciones I2C son el GPIO22-SDA y GPIO21-SCL, que son los pines habilitados por defecto para dicho bus en el microcontrolador ESP32.
COMPROBACIÓN DE LA INSTALACIÓN
Una forma sencilla de comprobar las conexiones y la instalación de la librería ssd1306.py es ejecutar un pequeño programa.
El programa que se va a utilizar servirá para iluminar toda la superficie de la pantalla, lo que también permitirá comprobar si todos los píxels funcionan correctamente:
from machine import Pin, I2C import ssd1306 i2c = I2C(-1, scl=Pin(22), sda=Pin(21)) # Asignación de pines para la conexión I2C oled = ssd1306.SSD1306_I2C(128, 64, i2c) # Dimensiones -en pixels- de la pantalla oled.fill(1) # Rellena la pantalla oled.show() # Muestra el resultado
LA PROGRAMACIÓN
La librería ssd1306.py utiliza la librería FrameBuff de MicroPython, ya que ésta permite crear un búferes eficientes de memoria para trabajar con vistas gráficas simples, por lo que es necesario desarrollar su funcionamiento.
FrameBuff dispone de los métodos para dibujar puntos, líneas, rectángulos, imágenes o escribir texto, así como desplazar imágenes por la pantalla.
Antes de comenzar…
- El sistema de coordenadas que utiliza la librería para localizar las representaciones es el cartesiano (unidades en –píxels–). Su eje horizontal es es el X y el eje vertical el Y.
- El origen (0,0) está en la esquina superior izquierda.
- El vértice opuesto en una pantalla de 128×64 píxels estará en la coordenada (127×63).
RELLENO DE PANTALLA |
.fill(color) |
Rellena la pantalla con el color indicado. Ejemplo: |
from machine import Pin, I2C import ssd1306 from time import sleep i2c = I2C(-1, scl=Pin(22), sda=Pin(21)) oled = ssd1306.SSD1306_I2C(128, 64, i2c) oled.fill(1) # Rellena la pantalla (la ilumina entera) sleep(2) # Espera 2 segundos oled.fill(0) # Rellena la pantalla (la apaga entera)
PUNTOS -PIXELS- |
.pixel(x0, y0 [, color]) |
Dibuja un punto (píxel) si se indican las coordenadas y el color. Obtiene el color de un punto (píxel) en la pantalla si se indican las coordenadas y no se indica el color. Ejemplo: |
from machine import Pin, I2C import ssd1306 from time import sleep i2c = I2C(-1, scl=Pin(22), sda=Pin(21)) oled = ssd1306.SSD1306_I2C(128, 64, i2c) oled.pixel(11, 5, 1) # Dibuja un píxel iluminado en (5,10) oled.show() # Muestra el resultado sleep(2) # Espera 2 segundos oled.fill(1) # Rellena la pantalla (la ilumina entera) oled.pixel(11, 5, 0) # Dibuja un píxel apagado en (10,5) oled.show() # Muestra el resultado
MATRICES DE PUNTOS (ICONOS) |
.pixel(x0, y0) |
El método .pixel(x0, y0) se puede utilizar también para dibujar una matriz de puntos en la pantalla. No existe ninguna limitación en cuanto al tamaño de la matriz (siempre y cuando no se supere el tamaño de la pantalla). Ejemplo: |
from machine import Pin, I2C import ssd1306 from time import sleep i2c = I2C(-1, scl=Pin(22), sda=Pin(21)) oled = ssd1306.SSD1306_I2C(128, 64, i2c) ICONO = [ # Matriz de puntos [ 0, 0, 1, 0, 0, 0, 1, 0, 0], [ 0, 0, 1, 0, 0, 0, 1, 0, 0], [ 0, 1, 1, 1, 1, 1, 1, 1, 0], [ 1, 1, 0, 0, 1, 0, 0, 1, 1], [ 1, 1, 1, 1, 1, 1, 1, 1, 1], [ 1, 0, 1, 0, 0, 0, 1, 0, 1], [ 1, 0, 1, 1, 1, 1, 1, 0, 1], [ 0, 0, 1, 1, 0, 1, 1, 0, 0], [ 0, 1, 1, 1, 0, 1, 1, 1, 0], ] for y, fila in enumerate(ICONO): # Dibuja los puntos de la matriz for x, c in enumerate(fila): oled.pixel(x, y, c) oled.show() # Muestra el resultado
LÍNEAS |
.line(x0, y0, x1, y1, color) |
Dibuja una línea con origen en (x0, y0) y final en (x1, y1) y color establecido. Ejemplo: |
from machine import Pin, I2C import ssd1306 from time import sleep i2c = I2C(-1, scl=Pin(22), sda=Pin(21)) oled = ssd1306.SSD1306_I2C(128, 64, i2c) oled.line(4, 5, 18, 9, 1) # Dibuja una línea iluminada. Origen (4, 5) y final (18, 9) oled.show() # Muestra el resultado sleep(2) # Espera 2 segundos oled.fill(1) # Rellena la pantalla (la ilumina entera) oled.line(4, 5, 18, 9, 0) # Dibuja una línea apagada. Origen (4, 5) y final (18, 9) oled.show() # Muestra el resultado
LÍNEAS HORIZONTALES |
.hline(x0, y0, ancho, color) |
Dibuja una línea horizontal de origen (x0, y0), anchura y color establecidos. Ejemplo: |
from machine import Pin, I2C import ssd1306 from time import sleep i2c = I2C(-1, scl=Pin(22), sda=Pin(21)) oled = ssd1306.SSD1306_I2C(128, 64, i2c) oled.hline(5, 4, 16, 1) # Dibuja una línea horizontal iluminada. Origen (5, 4) anchura 16 píxels oled.show() # Muestra el resultado sleep(2) # Espera 2 segundos oled.fill(1) # Rellena la pantalla (la ilumina entera) oled.hline(5, 4, 16, 0) # Dibuja una línea horizontal apagada. Origen (5, 4) anchura 16 píxels oled.show() # Muestra el resultado
LÍNEAS VERTICALES |
.vline(x0, y0, alto, color) |
Dibuja una línea vertical de origen (x0, y0), altura y color establecidos. Ejemplo: |
from machine import Pin, I2C import ssd1306 from time import sleep i2c = I2C(-1, scl=Pin(22), sda=Pin(21)) oled = ssd1306.SSD1306_I2C(128, 64, i2c) oled.vline(11, 5, 10, 1) # Dibuja una línea vertical iluminada. Origen (11, 5) altura 10 píxels oled.show() # Muestra el resultado sleep(2) # Espera 2 segundos oled.fill(1) # Rellena la pantalla (la ilumina entera) oled.hline(11, 5, 10, 0) # Dibuja una línea vertical apagada. Origen (11, 5) altura 10 píxels oled.show() # Muestra el resultado
RECTÁNGULOS |
.rect(x0, y0, ancho, alto, color) |
Dibuja un rectángulo con origen en (x0, y0), de anchura, altura y color establecidos. Ejemplo: |
from machine import Pin, I2C import ssd1306 from time import sleep i2c = I2C(-1, scl=Pin(22), sda=Pin(21)) oled = ssd1306.SSD1306_I2C(128, 64, i2c) oled.rect(5, 3, 14, 12, 1) # Dibuja un rectángulo iluminado. Origen (5, 3)y anchura x altura 14x12 píxels oled.show() # Muestra el resultado sleep(2) # Espera 2 segundos oled.fill(1) # Rellena la pantalla (la ilumina entera) oled.rect(5, 3, 14, 12 ,0) # Dibuja un rectángulo apagado. Origen (5, 3)y anchura x altura 14x12 píxels oled.show() # Muestra el resultado
RECTÁNGULOS RELLENOS |
.fill_rect(x0, y0, ancho, alto, color) |
Dibuja un rectángulo relleno con origen en (x0, y0), de anchura, altura y color establecidos. Ejemplo: |
from machine import Pin, I2C import ssd1306 from time import sleep i2c = I2C(-1, scl=Pin(22), sda=Pin(21)) oled = ssd1306.SSD1306_I2C(128, 64, i2c) oled.fill_rect(5, 3, 14, 12, 1) # Dibuja un rectángulo relleno iluminado. Origen (5, 3)y anchura x altura 14x12 píxels oled.show() # Muestra el resultado sleep(2) # Espera 2 segundos oled.fill(1) # Rellena la pantalla (la ilumina entera) oled.fill_rect(5, 3, 14, 12 ,0) # Dibuja un rectángulo relleno apagado. Origen (5, 3)y anchura x altura 14x12 píxels oled.show() # Muestra el resultado
TEXTO |
.text(texto, x0, y0 [, color]) |
Escribe un texto con punto de origen en (x0, y0), del color establecido. Si no se indica el color será iluminado (1). Todas las letras tendrán un tamaño de 8×8 píxels. Actualmente no hay forma de cambiar el tamaño. Ejemplo: |
from machine import Pin, I2C import ssd1306 from time import sleep i2c = I2C(-1, scl=Pin(22), sda=Pin(21)) oled = ssd1306.SSD1306_I2C(128, 64, i2c) oled.text("w", 6, 1, 1) # Escribe "w" iluminado. Origen (6, 1). oled.show() # Muestra el resultado sleep(2) # Espera 2 segundos oled.fill(1) # Rellena la pantalla (la ilumina entera) oled.text("Hola mundo!", 16, 28, 0) # Escribe "Hola mundo!" apagado. Origen (16, 28). oled.show() # Muestra el resultado
DESPLAZAMIENTOS |
.scroll(x0, y0) |
Desplaza el contenido de la pantalla desde el origen (0, 0) al punto (x0, y0). No se hace un borrado de la pantalla previo al desplazamiento, sino que la pantalla desplazada se superpone sobre la pantalla existente. Ejemplo: |
from machine import Pin, I2C import ssd1306 from time import sleep i2c = I2C(-1, scl=Pin(22), sda=Pin(21)) oled = ssd1306.SSD1306_I2C(128, 64, i2c) oled.text("W", 2, 2, 1) # Escribe "W" iluminado. Origen (2, 2). oled.rect(0, 0, 128, 64, 1) # Dibuja un rectángulo iluminado perimetral. oled.show() # Muestra el resultado for x in range (0, 5): # Desplaza la imagen a (20, 10) 5 veces. oled.scroll(20,10) sleep(0.5) oled.show() # Muestra el resultado
# REPRESENTACIÓN DE ONDA SENOIDAL (SENTIDO DE GIRO INVERSO) from machine import Pin, I2C import ssd1306 from time import sleep import math i2c = I2C(-1, scl=Pin(22), sda=Pin(21)) oled = ssd1306.SSD1306_I2C(128, 64, i2c) while True: for anguloGrados in range(0, 360, 5): # Ángulo de giro de la línea del eje (de 0º a 360º en intervalos de 5º) anguloRadianes = (math.pi*anguloGrados)/180 # Ángulo de giro de la línea del eje en radianes xLinea = int(math.cos(anguloRadianes)*24) # Coordenada x línea del eje yLinea = int(math.sin(anguloRadianes)*24) # Coordenada y línea del eje # Circunferencia. Radio 25 pixels. Centro (24, 32) for anguloGrados in range(0, 360, 5): # Ángulo de giro de cada pixel de la circunferencia en grados anguloRadianes = (math.pi*anguloGrados)/180 # Ángulo de giro de cada pixel de la circunferencia en radianes x = int(math.cos(anguloRadianes)*24) # Coordenada x pixel circunferencia y = int(math.sin(anguloRadianes)*24) # Coordenada y pixel circunferencia oled.pixel(x+24, y+32, 1) # Dibuja cada pixel de la circunferencia oled.hline(0, 32, 128, 1) # Dibuja la línea horizontal central oled.vline(49, 0, 64, 1) # Dibuja la línea vertical oled.line(24 , 32, xLinea+24, yLinea+32, 1) # Dibuja la línea del eje. De (24, 32) a (xLinea, yLinea) oled.hline(xLinea+24, yLinea+32, 27-xLinea,1) # Dibuja la línea horizontal giro oled.show() # Muestra el resultado oled.scroll(1, 0) # Desplaza imagen un pixel a la derecha oled.fill_rect(0, 0, 51, 64, 0) # Tapa desde origen hasta raya vertical... # ...toda la altura de la pantalla
|
IMÁGENES |
.blit(fbuf, x0, y0) |
Dibuja una imagen en la pantalla almacenada en un framebuffer , con punto de origen de representación en (x0, y0). Para crear el framebuffer se utiliza un fichero de imagen. La librería framebuf dispone de varios métodos para poder trabajar con diferentes formatos de imagen, pero por una cuestión práctica se va a utilizar el formato .pbm –portable bitmap– (formato para ficheros de imágenes en blanco y negro sin compresión), con los datos codificados en formato ASCII.
TRATAMIENTO DE LA IMAGEN (Aquí se pueden descargar los ficheros MicroPyton_logo.bmp, MicroPython_logo.jpg y MicroPython_logo.pbm para hacer pruebas) Se puede obtener una imagen con las características adecuadas (formato .pbm, codificación ASCII y dimensiones inferiores al tamaño de la pantalla -128×64 pixels-), con el editor de imágenes GIMP, ya que permite trabajar con la imagen y exportarla a dicho formato con la codificación ASCII. Otra opción, posiblemente más sencilla, es hacerlos en dos pasos. En el primero es trabajar con un editor de imágenes como paint.net, para hacer el procesamiento y ajuste de tamaño y en el segundo utilizar un conversor de formato de imagen como https://convertio.co/es. Con un editor de texto podremos ver el contenido del fichero de imagen .pbm. Deberá ser algo como lo siguiente: Consta de dos partes. La primera es el encabezado, que tiene como mínimo dos líneas:
La segunda son los datos de la imagen en formato ASCII.
COPIA DE LA IMAGEN A LA MEMORIA FLASH DE LA PLACA Se puede subir el fichero de imagen .pbm a la memoria flash de la placa utilizando Thonny. Para ello bastará con localizar en la ventana de Archivos el fichero, hacer clic sobre él con el botón derecho del ratón y hacer clic en Subir a/ para que se grabe en el directorio raíz del microcontrolador. Una vez hecho, se puede ejecutar el programa para visualizar la imagen: |
from machine import Pin, I2C import ssd1306, framebuf from time import sleep i2c = I2C(-1, scl=Pin(22), sda=Pin(21)) oled = ssd1306.SSD1306_I2C(128, 64, i2c) with open('MicroPython_logo.pbm', 'rb') as f: # Abre el fichero para lectura en modo binario f.readline() # Salta la línea del identificador mágico f.readline() # Salta la línea de las dimensiones de la imagen # f.readline() # Salta la línea de comentario (descomentar si existe) data = bytearray(f.read()) # Lee los datos de la imagen fbuf = framebuf.FrameBuffer(data, 64, 64, framebuf.MONO_HLSB) # Datos y tamaño de la imagen... oled.blit(fbuf, 32, 0) # Framebuffer y punto de inicio de representación oled.show() # Muestra el resultado
ANIMACIONES |
.blit(fbuf, x0, y0) |
Una animación es una secuencia de imágenes. Para poder realizarla se deben subir varias imágenes que formen una secuencia a la memoria flash de la placa, para que el microcontrolador las pueda leer, crear un framebuffer de cada una de ellas. Una vez hecho los framebuffers se muestran en la pantalla uno a continuación de otro, con un pequeño retardo (75-100 ms), de tal manera que se adecue la velocidad de lo que se muestra a su duración real, para generar la sensación de movimiento. Ejemplo: Las imágenes de la animación se pueden descargar desde aquí. Se deberán subir a la placa como se ha hecho con la imagen anterior. Si se quiere que queden organizadas en un directorio, se puede subir todo el directorio (/Corredor) al directorio raíz de MicroPython y modificar la línea 11 del programa de: with open(‘corredor_%s.pbm’ %n, ‘rb’) as f: a with open(‘/Corredor/corredor_%s.pbm’ %n, ‘rb’) as f:, para que las pueda localizar. |
import ssd1306, framebuf from machine import Pin, I2C from time import sleep i2c = I2C(-1, scl=Pin(22), sda=Pin(21)) # Asignación de pines del ESP32 (conex. I2C) oled = ssd1306.SSD1306_I2C(128, 64, i2c) # Pantalla 128x64-sssd1306 I2C imagenes = [] for n in range(1,13): # Bucle para leer todas las imágenes (12 imágenes) with open('corredor_%s.pbm' %n, 'rb') as f: # Abre cada fichero para lectura en modo binario f.readline() # Salta la línea del identificador mágico f.readline() # Salta la línea de las dimensiones de la imagen data = bytearray(f.read()) # Lee los datos de la imagen y los guarda en la variable "data" fbuf = framebuf.FrameBuffer(data, 128, 64, framebuf.MONO_HLSB) # Datos y tamaño de la imagen. imagenes.append(fbuf) # Crea un framebuffer con cada imagen oled.invert(1) # Invierte la imagen a mostrar while True: # Bucle infinito para visualizar la animación for i in imagenes: # Muestra la secuencia de imágenes oled.blit(i, 0, 0) # Framebuffer y punto de inicio de la representación oled.show() # Muestra el resultado sleep(0.075) # Para 75 ms entre imágenes
MÉTODOS PROPIOS DE LA LIBRERÍA ssd1306.py
La librería ssd1306.py. dispone de métodos propios, independientes a los que aporta la librería FrameBuff de MicroPython . Son los siguientes:
ENCENDIDO Y APAGADO DE LA PANTALLA |
.poweron() .poweroff() |
La pantalla se puede apagar y encender utilizando .poweron() y poweroff() respectivamente. |
REGULACIÓN DEL CONTRASTE DE LA PANTALLA (FONDO DE LA PANTALLA) |
.contrast(valor) |
Regula el contraste de la pantalla. Toma valores entre 0 y 255, siendo 0 apagada y 255 completamente iluminada. Ejemplo: |
from machine import Pin, I2C import ssd1306 from time import sleep i2c = I2C(-1, scl=Pin(22), sda=Pin(21)) oled = ssd1306.SSD1306_I2C(128, 64, i2c) oled.contrast(50) # Regula el contraste al 50% de la intensidad oled.show() # Muestra el resultado
INVERSIÓN DE LA IMAGEN A MOSTRAR |
.invert(1) invierte el resultado de la imagen a mostrar en la pantalla. Cambia los píxeles iluminados por apagados e inversa.
.invert(0) hace que la pantalla funciones normalmente. |
Ejemplo: |
from machine import Pin, I2C import ssd1306 from time import sleep i2c = I2C(-1, scl=Pin(22), sda=Pin(21)) oled = ssd1306.SSD1306_I2C(128, 64, i2c) oled.fill_rect(5, 3, 14, 12, 1) # Dibuja un rectángulo relleno iluminado. Origen (5, 3)y anchura x altura 14x12 píxels oled.inver(1) # Invierte lo que se va a mostrar oled.show() # Muestra el resultado
10 Comments
Leave your reply.