Mostrando entradas con la etiqueta Adafruit. Mostrar todas las entradas
Mostrando entradas con la etiqueta Adafruit. Mostrar todas las entradas

viernes, 20 de febrero de 2026

Ojos animados en OLED con Raspberry Pi: Tutorial Python con expresiones

Muy buenas a todos y todas!!!

Ya hemos visto como configurar una pantalla OLED de 128x64 en nuestra Raspberry Pi junto con la librería Adafruit.

Y como veía mi cluster Pi un poco "triste" decidí animarlo un poco con una pantalla OLED de tipo I2c y algunas animaciones programadas en Python que la hacen más "friendly".


Expresiones y dirección de la mirada

Podemos alternar entre las diferentes expresiones:

  • Normal
  • Contento
  • Triste
  • Sorpresa
  • Dormido

También podemos dirigir la mirada en las siguientes direcciones:

  • Izquierda
  • Centro
  • Derecha



Pero también podemos llamar de manera independiente a cada una de las expresiones con la función "crear_ojos" y "animar_parpadeo"

def crear_ojos(expresion="normal", direccion_mirada="centro", estado_parpadeo=1.0):
    """
    Ojos con sistema de parpadeo realista
    estado_parpadeo: 1.0 = totalmente abierto, 0.0 = totalmente cerrado
    """

Para crear la animación de parpadeo:


def animar_parpadeo(expresion="normal", direccion_mirada="centro"):
    """Animación completa de parpadeo"""
    

Programa completo en python:



# Simulación de ojos con expresiones en OLED 128x64

import time
import random
import board
import adafruit_ssd1306
from PIL import Image, ImageDraw, ImageFont

# Use for I2C.
i2c = board.I2C()  # uses board.SCL and board.SDA

try:
        disp = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c, addr=0x3C)
        print("\nAdafruit SSD1306 ok")

except Exception as errors:
        print("\nError Adafruit SSD1306:",str(errors), "\nPlease check Raspi-config\n")
        exit()


def crear_ojos(expresion="normal", direccion_mirada="centro", estado_parpadeo=1.0):
    """
    Ojos con sistema de parpadeo realista
    estado_parpadeo: 1.0 = totalmente abierto, 0.0 = totalmente cerrado
    """
    # Crear imagen evitando la franja amarilla superior
    img = Image.new('1', (128, 64), 0)
    draw = ImageDraw.Draw(img)
    
    # Posiciones fijas
    centro_y = 40
    ojo_izq_x = 45
    ojo_der_x = 83
    
    # Tamaño máximo reducido a 40
    max_tam = 40
    
    # Radio de redondeo
    radio = 15
    
    # Determinar tamaños según dirección de mirada
    if direccion_mirada == "izquierda":
        ancho_izq, alto_izq = max_tam, max_tam
        ancho_der, alto_der = 30, 30
    elif direccion_mirada == "derecha":
        ancho_izq, alto_izq = 30, 30
        ancho_der, alto_der = max_tam, max_tam
    else:  # centro
        ancho_izq, alto_izq = 35, 40
        ancho_der, alto_der = 35, 40
    
    def dibujar_ojo(x, y, ancho, alto, radio, apertura=1.0):
        """Dibuja un ojo con sistema de parpadeo"""
        if apertura >= 1.0:
            # Ojo totalmente abierto
            draw.rounded_rectangle([
                (x - ancho//2, y - alto//2),
                (x + ancho//2, y + alto//2)
            ], radius=radio, outline=1, fill=1)
        elif apertura <= 0.0:
            # Ojo totalmente cerrado (línea)
            draw.line([
                (x - ancho//2, y),
                (x + ancho//2, y)
            ], fill=1, width=3)
        else:
            # Ojo parcialmente cerrado - calcular altura visible
            alto_visible = int(alto * apertura)
            if alto_visible < 2:
                alto_visible = 2
            
            # Dibujar el ojo recortado según la apertura
            draw.rounded_rectangle([
                (x - ancho//2, y - alto_visible//2),
                (x + ancho//2, y + alto_visible//2)
            ], radius=min(radio, alto_visible//2), outline=1, fill=1)
    
    # Aplicar expresiones (modifican la apertura base)
    apertura_base = estado_parpadeo
    
    if expresion == "normal":
        apertura = apertura_base
    elif expresion == "contento":
        apertura = apertura_base * 0.7  # Ojos más cerrados
    elif expresion == "triste":
        apertura = apertura_base * 0.9  # Ojos casi normales pero caídos
    elif expresion == "sorpresa":
        apertura = min(apertura_base * 1.2, 1.0)  # Ojos más abiertos
    elif expresion == "dormido":
        apertura = apertura_base * 0.3  # Ojos casi cerrados
    
    # Dibujar ojos con la apertura calculada
    dibujar_ojo(ojo_izq_x, centro_y, ancho_izq, alto_izq, radio, apertura)
    dibujar_ojo(ojo_der_x, centro_y, ancho_der, alto_der, radio, apertura)
    
    return img

def animar_parpadeo(expresion="normal", direccion_mirada="centro"):
    """Animación completa de parpadeo"""
    frames = []
    
    # Fases del parpadeo (apertura de 1.0 a 0.0 y vuelta)
    fases = [1.0, 0.8, 0.5, 0.2, 0.0, 0.2, 0.5, 0.8, 1.0]
    
    for apertura in fases:
        frame = crear_ojos(expresion, direccion_mirada, apertura)
        frames.append(frame)
    
    return frames


# Bucle principal 
if __name__ == "__main__":
    expresiones = ["normal", "contento", "triste", "sorpresa", "dormido"]
    direcciones = ["izquierda", "centro", "derecha"]

    print("Mostrando expresiones sencillas...")
    print("Presiona Ctrl+C para detener")

    try:
        while True:
            for expresion in expresiones:
                for direccion in direcciones:

                    print(f"Expresión: {expresion} dirección: {direccion}")

                    img = crear_ojos(expresion, direccion)
                    disp.image(img)
                    disp.show()
                    time.sleep(5)
                                        # 30% de probabilidad de guiño entre animaciones
                    if random.random() < 0.2:
                        #lado_guiño = random.choice(["izquierdo", "derecho"])
                        print(f"¡papadeo {expresion}!")
                        frames_parpadeo = animar_parpadeo(expresion, direccion)

                        for frame in frames_parpadeo:
                            disp.image(frame)
                            disp.show()

    except KeyboardInterrupt:
        print("\nBucle detenido")

Es un programa que podemos ampliar con otras expresiones o incluso hacer llamadas desde otros programas por ejemplo conectandolo a una Raspi cam para simular que la mirada nos sigue.

Otro idea que se me ocurre es que las llamadas sean según diferentes eventos, como que despierte con el acceso por ssh o utilizar el sensor de temperatura para que nos "mire mal" si nuestra Raspberry Pi se pone lujuriosa.

Añadir programa al iniciar Raspberry PI

Lo ideal es que este programa se inicie al encender la Raspberry pi. Es un buen indicador para saber que la Raspberry Pi arranco sin problemas.

esto lo podemos hacer editando el crontab con el comando "crontab -e" (no hace falta sudo).


@reboot sleep 30 && python3 ~/Eyes_simulation/eyes_simulation.py > ~/Eyes_simulation/eyes.log 2>&1

Recuerda poner "@reboot sleep 30" para darle un tiempo prudente a la Raspberry para inicializar el puerto I2c.

Y siempre puedes ver si todo esta bien o hay algún fallo consultando el archivo que se genera en el inicio llamado "eyes.log".


Conclusión:

Es un programa sencillo que le dará a nuestra maquina un poco más de carisma (por si le faltaba un poco) y que deje de ser un puñado de lucecitas encima del escritorio.

Antes de montar el cluster con las 3 Raspberry Pi, la que máquina que usaba para estas cosas empezó a dar problemas con la tarjeta y lo pude detectar ya que no me miraba igual que siempre...

¿Le puedo poner nombre?


Aquí tienes otros enlaces de este blog relacionados con Raspberry Pi:

miércoles, 15 de octubre de 2025

Macro Keyboard con ESP32 y TFT ILI9488: Conexión paralelo 8 bits y Bluetooth

Muy buenas a todas y todos!!!

Seguramente tengas alguno de estos viejos modelos de pantalla que tienen la configuración en formato "sombrero" para los microcontroladores Arduino UNO .

Un display TFT 3,4" de 8 bits conectado en paralelo con el driver ILI9488 y táctil resistivo.

Esta manera de conectar los pines con 8bits más similitudes con un LCD 16x2 que con las modernas pantallas SPI que se manejan con menos hilos de control.

Es decir esta pantalla necesita 8 lineas para la transferencia de datos y otras 5 para la conexiones que manejan la pantalla TFT.




Librería TFT_eSPI

TFT_eSPI es una librería de alto rendimiento específicamente optimizada para controlar pantallas TFT a color con microcontroladores ESP32, ESP8266 y placas STM32.

Está diseñada desde cero para exprimir al máximo el hardware, utilizando SPI con DMA, transacciones asíncronas y manipulación directa de registros.

Su configuración manual permite un rendimiento máximo del display. 

A diferencia de librerías "plug-and-play", requiere que editemos un archivo de configuración "User_Setup.h" para especificar exactamente tu hardware: tipo de controlador, pines GPIO, frecuencia SPI, etc.

También dispone de configuraciones especificas según el tipo de microcontrolador ubicadas en la carpeta "/Users_Setups".

Es, sin duda, la librería más rápida para pantallas TFT con microcontroladores de tipo ESP32/ESP8266.

El uso directo de SPI DMA y transacciones optimizadas hace que la diferencia sea abismal frente a alternativas como Adafruit_GFX.

Aunque parece complejo, el sistema de archivos de configuración permite afinar hasta el último detalle de tu pantalla específica.

Dispone de un soporte masivo de controladores ya que es compatible con más de 50 controladores TFT diferentes. Desde los comunes ILI9341, ST7789, hasta modelos más exóticos.

Características Avanzadas de TFT_eSPI

  • Sprites en memoria para animaciones fluidas
  • Fuentes anti-aliadas con calidad profesional
  • Rotaciones eficientes de pantalla
  • Operaciones de dibujo aceleradas por hardware

ESP32 y TFT ILI9488 paralelo

La conexión de la pantalla con el ESP32 hay que seleccionar las lineas que se describen en la siguiente tabla:

Señal TFT GPIO ESP32
LCD_RST GPIO 15
LCD_CS GPIO 2
LCD_RS (DC) GPIO 32
LCD_WR GPIO 33
LCD_RD GPIO 4
LCD_D0 GPIO 12
LCD_D1 GPIO 13
LCD_D2 GPIO 26
LCD_D3 GPIO 25
LCD_D4 GPIO 17
LCD_D5 GPIO 16
LCD_D6 GPIO 27
LCD_D7 GPIO 14



Configuración de la librería TFT_eSPI

Hay que saber que esta librería tiene 2 archivos principales de configuración:

  • Setup70d_ILI9488_S3_Parallel.h

    Es el archivo de configuración ubicado en la carpeta "/Users_Setups" donde pondremos los pines personalizados tanto del ESP32 como de la pantalla con el controlador ILI9488 que hemos dispuesto en la tabla anterior:


    
        
    #define USER_SETUP_ID 146
    
    #define TFT_PARALLEL_8_BIT
    
    //#define ILI9341_DRIVER
    //#define ST7796_DRIVER
    #define ILI9488_DRIVER
    
    // ESP32 S3 pins used for the parallel interface TFT
    #define TFT_CS    2
    #define TFT_DC   32  // Data Command control pin - must use a GPIO in the range 0-31
    #define TFT_RST  15
    
    #define TFT_WR    33  // Write strobe control pin - must use a GPIO in the range 0-31
    #define TFT_RD    4
    
    #define TFT_D0   12  // Must use GPIO in the range 0-31 for the data bus
    #define TFT_D1   13  // so a single register write sets/clears all bits
    #define TFT_D2   26
    #define TFT_D3   25
    #define TFT_D4   17
    #define TFT_D5   16
    #define TFT_D6   27
    #define TFT_D7   14
    
    #define LOAD_GLCD
    #define LOAD_FONT2
    #define LOAD_FONT4
    #define LOAD_FONT6
    #define LOAD_FONT7
    #define LOAD_FONT8
    #define LOAD_GFXFF
    
    #define SMOOTH_FONT
    
    
  • User_Setup_Select.h
    Este es el archivo que maneja las configuraciones de la librería TFT_eSPI y que hará referencia al archivo "Setup70d_ILI9488_S3_Parallel.h" que acabamos de configurar.
    tenemos que comentar la linea donde esta la referencia al archivo "User_Setup.h":
    
        // Archivo User_Setup_Select.h
        
        // #include <User_Setup.h>  // Default setup is root library folder
        
        #include <User_Setups/Setup70d_ILI9488_S3_Parallel.h> // Setup file for ESP32 S3 with SPI ILI9488
    
        

Recuerda que una vez configuradas las conexiones correctamente, el rendimiento es óptimo porque la librería está compilada exactamente para tu hardware.

Ahora ya podemos probar el ejemplo que proporciona la misma librería como puede ser "Demo_3D_Cube.ino"

IMAGEN DEL CUBO 3D

Problemas con colores en pantalla TFT

Si se tienes problemas con los colores inserta esta línea en el setup() después de haber inicializado la pantalla (tft.begin())


tft.invertDisplay(true);

Con este comando se invierten los colores que se muestran en la pantalla TFT.


Otro problema que podemos encontrar es que no compile bien la librería, prueba a cambiar la versión de la placa antes de compilar. La versión de placa que estamos utilizando es la versión 2.0.17 de ESPressif Systems.

Librería LittleFS.

Sobre LittleFS ya hemos hablado antes en este blog pero enfocado al microcontroladores ESP8266.

Esta vez la usaremos para subir los iconos que se mostrarán posteriormente en la pantalla en formato .png.

Subir archivos a la memoria flash del ESP32 con Arduino IDE 2

Subiremos los archivos siguiendo unos pocos pasos:

  • Primero cerramos el IDE 2 de Arduino.

  • Después necesitamos descargar el archivo: arduino-littlefs-upload-1.x.x.vsix desde GitHub

  • Ahora nos situamos en la carpeta .ArduinoIDE y creamos un directorio llamado "plugins". Rcuerda que esta carpeta .ArduinoIDE puede estar oculta.

  • Finalmente solo tenemos que compiar el archivo "arduino-littlefs-upload-1.x.x.vsix" a la carpeta ".ArduinoIDE/plugins"

Abrimos el IDE de Arduino y pulsamos "Ctrl + Mayus + P" para buscar en la barra "upload", tal y como se muestra en la imagen:


Archivos PNG con librería LittleFS

Al pulsar sobre la opción "Upload LittleFS to Pico/ESP8266/ESP32" se copiarán todos los archivos que estén en la carpeta "data" dentro de la carpeta del sketch a la memoria flash del dispositivo ESP32 DOIT DEVKIT V1.

La memoria flash disponible es de 4MB, suficiente para unos cuantos archivos  con formato .png con un tamaño reducido.


Librería PNGdec

Esta librería fue una gran sorpresa, de uso sencillo y muy rápida pese a ser una pantalla tft con conexión en paralelo. Puedes encontrar la librería PNGdec en GitHub.

Como siempre no estaría bien que algo funcionará a la primera así que el una vez abierto el ejemplo de la librería llamado LittleFS_PNG.ino podemos compilar y nos aparecerá el siguiente error:


Compilation error: 
  invalid conversion from 'void (*)(PNGDRAW*)' {aka 'void (*)(png_draw_tag*)'} 
  to 'int (*)(PNGDRAW*)' {aka 'int (*)(png_draw_tag*)'} [-fpermissive]

Para solucionar este error solo hay que buscar la función "int pngDraw(PNGDRAW *pDraw)" y añadir "return 1; al final tal y como se muestra en el siguiente fragmento de código:


int pngDraw(PNGDRAW *pDraw) 
{
  uint16_t lineBuffer[MAX_IMAGE_WIDTH];
  png.getLineAsRGB565(pDraw, lineBuffer, PNG_RGB565_BIG_ENDIAN, 0xffffffff);
  tft.pushImage(xImage, yImage + pDraw->y, pDraw->iWidth, 1, lineBuffer);
  return 1;
}


Librería Adafruit TouchScreen

Esta ha sido la parte con la que más tiempo he perdido. Todo se ha complicado por no encontrar los pines que corresponden a XP y XM debido a que cada panel tiene una configuración de pines diferente.

También por que uno de los cabnles estaba en mal estado y, aunque permitia el encendido de la pantalla, no hací un buen contacto al hacer la lectura analógica.

Puedes descargar la librería Adafruit TouchScreen en GitHub.

La solución la encontré gracias a un Arduino UNO junto con la librería MCUFRIEND_kbv y el ejemplo: "TouchScreen_Calibr_native".

Con la pantalla en el Arduino UNO, cargamos el sketch TouchScreen_Calibr_native y esperamos a la salida serial


TouchScreen.h GFX Calibration
Making all control and bus pins INPUT_PULLUP
Typical 30k Analog pullup with corresponding pin
would read low when digital is written LOW
e.g. reads ~25 for 300R X direction
e.g. reads ~30 for 500R Y direction

Testing : (A2, D8) = 35
Testing : (A3, D9) = 25

ID = 0x9488

Nos interesan las lineas "Testing" con la que podemos realizar la siguiente conversión:

Arduino UNO TFT ESP32 Touch
A2 RS GPIO 32 YP
A3 CS GPIO 2 XM
D8 D0 GPIO 12 YM
D9 D1 GPIO 13 XP

Esto datos los pasaremos al programa principal en el apartado "Configuración TouchScreen":


// Configuración TouchScreen

#define YP 32  // Tiene que ser un pin analógico, usa la notación "ADC" 
#define XM 2   // Tiene que ser un pin analógico, usa la notación "ADC"

#define YM 12
#define XP 13


Ahora con la certeza de que el panel resistivo funcionaba, sustituimos los 4 cables que le corresponden al panel y volvemos a probar con el ESP32

Esta configuración puede cambiar

Otro de los problemas que surgió fue la lectura de la pulsación Z que corresponde a la variable "p.z" daba lecturas muy "extrañas" debido a la resolución de la lectura analógica del que dispone el ESP32 que es de 12bits frente a los 10bits que tiene, por ejemplo, el Arduino UNO.



Arduino UNO ESP32
Resolución ADC 10 bits 12 bits
Valores Posibles 0 - 1023
(2¹⁰ = 1024)
0 - 4095
(2¹² = 4096)

Una vez tenemos este dato, vamos al archivo "TouchScreen.cpp" que estará en la carpeta de las librerías /Adafruit_TouchScreen y buscamos las siguientes lineas en la función "TSPoint TouchScreen::getPoint(void)" y las sustituimos por esta:


// Función: TSPoint TouchScreen::getPoint(void)
// rtouch /= 1024;
rtouch /= 4096;

y

// z = (1023 - (z2 - z1));
z = (4095 - (z2 - z1));


Y aquí es donde daba el fallo al leer la . Para esta pantalla solo hay que cambiar estos parámetros en la función "TSPoint TouchScreen::getPoint(void)", pero para otras también cambiamos todos los valores 1024 y 1023 por 4095 y 4096 de todas las funciones en las que aparezcan dichos valores.

Ahora sí, la pantalla táctil funciona perfectamente y lo podemos comprobar en el monitor serial


Hasta aquí la primera parte para la configuración de la pantalla TFT y conexión paralela de 8bits con un microcontrolador ESP32, este es un pequeño resumen de lo que hemos realizado hasta ahora:

  • Configuración de la librería TFT_eSPI para pantalla de 8 bits.

  • Repaso a la librería LittleFS y la instalación de LittleFS upload en el IDE de Arduino.

  • Instalación de la librería PNGdec y la solución a posibles problemas.

  • Por último la librería Adafruit_TouchScreen y su configuración para el panel resistivo junto con pruebas con MCUFRIEND_kbv

Configuración para la lectura de teclas

Ahora tenemos que configurar el sistema para poder leer los comandos que nos manda el ESP32. La conexión con el PC sera realizada con el Bluetooth que nos proporciona el mismo microcontrolador ESP32.

Una vez que se realice la conexión el sistema leerá los comandos seriales y realizará las pulsaciones del el teclado o la llamada al programa que hayamos designado a dicha aplicación.

Comunicación Bluetooth entre ESP32 y PC

Aquí entran en juego las dos partes, la del microcontrolador y la conexión bluetooth que dispongamos en el PC. Como nota, podemos cambiar la conexión Bluetooth por la conexión serial por USB, pero eso lo dejamos para una actualización.

Librería BluetoothSerial en ESP32

Esta ĺibrería es interna y la proporciona el mismo IDE de Arduino así que la tenemos disponible desde el primer momento. Si necesitas más información este es la documentación de la librería BluetoothSerial.h


#include "BluetoothSerial.h"
void setup()
{
  SerialBT.begin("ESP32-ITB"); // Nombre del dispositivo Bluetooth
}

void loop()
{
  // Programa principal
  Lectura_Tecla_Pantalla()
}

void Lectura_Tecla_Pantalla()
{
  SerialBT.println("ACK: COMANDO");
}


Ahora que ya hemos visto como se conectará el microcontrolador vamos con la parte del PC de la mano, por supuesto, del lenguaje de programación Python.


Programa en Python para lectura del bluetooth

Python es la navaja suiza de los lenguajes de programación y gracias a esta versatilidad nos proporciona todas las librerías necesarias para este proyecto.

Estas son las librerías Python que vamos a utilizar.

Librerías externas

  • bluetooth
    Sera la encargada de comunicarnos con el ESP32. La puedes encontrar en GitHub como Pybluez

  • keyboard
    Esta es la librería encargada de transformar los datos seriales que proporciona el bluetooth a pulsaciones del teclado. Puedes consultar la librería keyboard de Boppreh en GitHub.

Librerías incluidas en Python

  • sys
    (System) Necesaria para interactuar con el intérprete de Python y el sistema como listar directorios o ejecutar comandos. Viene incluida con Python.

  • os
    (Operating System) Interfaz con el sistema operativo: archivos, directorios, procesos.

  • subprocess
    Ejecutar comandos externos y gestionar sus flujos de entrada/salida. Necesaria para la ejecución de los programas que designemos a las teclas.

  • time
    Realiza las pausas necesarias para no saturar la recepción de datos.

Comunicación ESP32 Bluetooth PC

Para diferenciar entre los comandos que vamos a recibir y el comando que vamos a ejecutar los vamos a almacenar en un diccionario de la siguiente manera:


# Mapeo de teclas (lo que escribes → lo que se simula)
COMANDOS = {
    'item1': 'inkscape',
    'item2': 'audacity',
    'item3': 'subl',
    'item4': 'spectacle',

    'item5': 'ctrl+z',         
    'item6': 'ctrl+x', 
    'item7': 'ctrl+c', 
    'item8': 'ctrl+v',
    
    'save': 'ctrl+s',
    'select all': 'ctrl+a',
    'find': 'ctrl+f'
    }

De esta manera podemos crear un diccionario con los atajos de teclado, comandos del sistema o aplicaciones del sistema. Así tenemos centralizados los comandos facilitando su modificación.

En la primera parte tenemos el Item que hemos seleccionado en el display. En la segunda parte del diccionario pondremos la aplicación o el atajo de teclado que queramos.

NOTA:

Depende del sistema que estes usando puede que el funcionamiento no sea el correcto. Para este ejemplo se ha utilizado una distribución basada en Debian.

Cargar ESP32 Macro keyborad en el inicio

Para cargar el programa para que se inicie al encender el ordenador tenemos que incluirlo en el crontab de usuario con el siguiente comando:


@reboot sleep 30 && sudo -u YOUR_USER /usr/bin/python3 /home/peyutron/PythonProjects/Comunicacion/Bluetooth/Bluetooth-HID-ESP32/main.py >> /home/YOUR_USER/esp32_cron.log 2>&1


Conclusión del proyecto

Hardware Especializado:

  1. Microcontrolador ESP32 con Bluetooth integrado.

  2. Pantalla TFT con interfaz táctil resistiva/capacitiva.

  3. Comunicación paralela 8 bits para control de pantalla.

Software de Control:

  1. TFT_eSPI - Librería optimizada para gráficos en ESP32.

  2. Protocolo Bluetooth RFCOMM - Comunicación serial inalámbrica.

  3. Sistema de comandos - Diccionario personalizable de acciones.

  4. Flexibilidad Total - Configuración ilimitada de comandos

  • Descargar los archivos para ESP32 y el progrma en Python3 de los que consta el proyectoTFT-eSPI 8bits ILI9488 Macro Keypad en GitHub
  • También es interesante echar un vistazo al datasheet del controlador ILI9488 para entender mejor su funcionamiento.

    Saludos!!!

    Puedes visitar los siguiente enlaces relacionados con pantallas y Arduino:

  • viernes, 13 de junio de 2025

    Raspberry Pi con sensor DHT11 y DHT22: Temperatura y humedad en Python

    Sensor DHT11
    DHT11

    Uno de los sensores más populares utilizados en conjunto con la Raspberry Pi es el DHT11, un sensor de humedad y temperatura de bajo costo y fácil de usar.


    ¿Qué son los sensores DHT11 y DHT22?

    Los sensores DHT11 y DHT22 son dispositivos electrónicos que mide la temperatura y la humedad relativa del aire. Es un sensor digital, lo que significa que los datos que proporciona son en formato digital y pueden ser fácilmente leídos por un microcontrolador como la Raspberry Pi.


    DHTT22 pinout
    DHT22

    A pesar de su bajo costo, el DHT11 más que el DHT22, son bastante precisos y fiables, lo que los convierte en una excelente opción para una amplia gama de aplicaciones.

    El DHT11 esta más indicado para Proyectos educativos o prototipos simples y ambientes interiores controlados como pueden ser habitaciones, oficinas.

    Sin embargo el DHT22 será más útil en proyectos de tipo invernadero, neveras y exteriores.

    Funciones de los sensores DHT11 y DHT22

    • Medición de temperatura
    • Medición de humedad
    • Salida digital
    • Bajo consumo
    • Fácil de usar

    Comparativa: DHT11 vs DHT22

    Característica DHT11 DHT22
    Precisión (Temp) ±2°C ±0.5°C
    Rango (Temp) 0°C a 50°C -40°C a 80°C
    Precisión (Hum) ±5% RH ±2% RH
    Rango (Hum) 20% a 80% RH 0% a 100% RH
    Frecuencia de Muestreo 1 Hz 0.5 Hz
    Resolución 1°C / 1% RH 0.1°C / 0.1% RH
    Consumo Eléctrico 0.5 - 2.5 mA

    Instalación y Configuración en Python:

    Para utilizar los sensores DHT11 y DHT22 con una Raspberry Pi, necesitaremos un entorno de programación adecuado y una biblioteca para interactuar con el sensor. Python es uno de los lenguajes de programación más populares para la Raspberry Pi, y existen varias bibliotecas que facilitan la lectura de datos del DHT11.

    Necesitamos instalar la biblioteca Adafruit. Ahora en un terminal de tu Raspberry Pi y ejecuta el siguiente comando para instalar la biblioteca adafruit-circuitpython-dht:

    
    pip3 install adafruit-circuitpython-dht
    
    
    

    Esta biblioteca proporciona una manera sencilla para leer datos de los sensores DHT11 y DHT22.

    Ahora tenemos que conectar el sensor DHT a a la Raspberry Pi. Para esto identificaremos los pines de cada sensor y los conectamos siguiendo las serigrafias que vienen en el sensor:

    • Data: al GPIO21 (pin40)
    • VCC: al cualquier pin 5V
    • GND: al cualquier pin GND

    Ahora en la terminal creamos un nuevo archivo Python llamado "dht11_test.py y escribimos el siguiente código que nos mostrará en pantalla los datos de temperatura y humedad del DHT11:


    
    # Código para lectura del sensor DHT11
    import time
    
    import board
    
    import adafruit_dht
    
    #Inicia el dispositivo DHT con el pin de datos conectado a:
    dhtDevice = adafruit_dht.DHT11(board.D21)
    
    # Si no deseas usar pulseio puedes usar DHT11 use_pulseio=False
    # Esto puede ser necesario en ordenadores de tipo Raspberry Pi con Linux
    # Pero no funcionará en CircuitPython
    # dhtDevice = adafruit_dht.DHT11(board.D18, use_pulseio=False)
    
    while True:
        try:
            # Imprime los valores en el puerto serie
            temperature_c = dhtDevice.temperature
            temperature_f = temperature_c * (9 / 5) + 32
            humidity = dhtDevice.humidity
            print(f"Temp: {temperature_f:.1f} F / {temperature_c:.1f} C    Humidity: {humidity}% ")
    
        except RuntimeError as error:
    	# Los errores ocurren con bastante frecuencia, los DHT son difíciles de leer, 
        simplemente sigue adelante.
    
            print(error.args[0])
            time.sleep(2.0)
            continue
        except Exception as error:
            dhtDevice.exit()
            raise error
    
        time.sleep(2.0)
    
    

    Guarda el archivo como dht11_test.py y ejecútalo desde la terminal:

    
    python3 dht11_test.py
    o
    python3 -m dht11_test
    
    


    Para el caso del DHT22 es exactamente igual menos la linea "sensor = adafruit_dht.DHT11" que tenemos que cambiar por "sensor = adafruit_dht.DHT22" y se ve así:

    
    # Código para lectura del sensor DHT22
    import time
    
    import board
    
    import adafruit_dht
    
    #Inicia el dispositivo DHT con el pin de datos conectado a:
    dhtDevice = adafruit_dht.DHT22(board.D21)
    
    # Si no deseas usar pulseio puedes usar DHT22 use_pulseio=False
    # Esto puede ser necesario en ordenadores de tipo Raspberry Pi con Linux
    # Pero no funcionará en CircuitPython
    # dhtDevice = adafruit_dht.DHT22(board.D21, use_pulseio=False)
    
    while True:
        try:
            # Imprime los valores en el puerto serie
            temperature_c = dhtDevice.temperature
            temperature_f = temperature_c * (9 / 5) + 32
            humidity = dhtDevice.humidity
            print(f"Temp: {temperature_f:.1f} F / {temperature_c:.1f} C    Humidity: {humidity}% ")
    
        except RuntimeError as error:
    	# Los errores ocurren con bastante frecuencia, los DHT son difíciles de leer,
        simplemente sigue adelante.
    
            print(error.args[0])
            time.sleep(2.0)
            continue
        except Exception as error:
            dhtDevice.exit()
            raise error
    
        time.sleep(2.0)
    
    

    Guardamos el archivo como dht22_test.py y ejecutamos desde la terminal:

    
    python3 dht22_test.py
    o
    python3 -m dht22_test
    
    


    El código de este ejemplo llamado dht_simpletest.py lo podemos encontrar en el repositorios de Adafruit_CircuitPython_DHT

    Solución de errores:

    Uno de los errores que más comunes es el uso de la librería "board". Muchas veces podemos recibir el siguiente mensaje:

    
    import board
    ModuleNotFoundError: No module named 'board'
    
    

    O también este:

    
    dhtDevice = adafruit_dht.DHT11(board.D21)
                                       ^^^^^^^^^
    AttributeError: module 'board' has no attribute 'D21'
    

    Nos hace referencia a la librería board que se instala normalmente con "pip install board". Pero al estar haciendo uso de Adafruit CircuitPython necesitamos la librería que esta asociadad a Adafruit llamada "Adafruit-Blinka"

    Esta librería instalara "board" y otras librerías como "pyserial" o "pyusb".

    En el caso de que haya un error nos puede aparecer este mensaje:

    
    Unable to set line 18 to input
    
    

    Si aparece este error, nos hace referencia al pin, que o bien no esta activo o no es el puerto correcto donde esta conectado el sensor DHT 11.

    Aplicaciones del DHT11 y Raspberry Pi

    La combinación de Raspberry Pi y DHT11 o DHT22 ofrece un sinfín de posibilidades para proyectos de IoT. Algunas de las aplicaciones más comunes incluyen:

    • Estaciones meteorológicas caseras: Crea una estación meteorológica personalizada para monitorear la temperatura y humedad en tu hogar o jardín.
    • Sistemas de control de invernaderos: Automatiza el riego y la ventilación de tu invernadero en función de las condiciones ambientales.
    • Monitoreo de condiciones ambientales: Utiliza el sensor para monitorear la temperatura y humedad en espacios cerrados, como oficinas o almacenes.
    • Proyectos de domótica: Puedes integrar el sensor en sistemas de domótica para crear ambientes más cómodos y eficientes.
    • Prototipos de IoT: Utilizalos como punto de partida para desarrollar prototipos de dispositivos IoT más complejos.

    Conclusión:

    El sensor DHT11 es una herramienta invaluable para cualquier persona interesada en proyectos de electrónica y programación. En combinación con la Raspberry Pi, ofrece una plataforma potente y flexible para crear una amplia variedad de aplicaciones.

    Con este artículo, has adquirido los conocimientos básicos para comenzar a trabajar con el DHT11 y la Raspberry Pi. ¡Anímate a experimentar y crear tus propios proyectos!

    Instalación librería Adafruit-Blinka

    Librería adafruit-circuitpython-dht


    Un saludo a todas y todos!!!


    Aquí tienes otros enlaces de este blog relacionados con Raspberry Pi:

    sábado, 21 de septiembre de 2024

    ADXL345 con ESP8266: Acelerómetro de 3 ejes – Lectura serial y ejemplos

    Sensor ADXL345
    ADXL345

    Buenas a todos y todas!!!

    El sensor ADXL345 es un sensor súper versátil en campos como la robótica, drones o sistemas de seguridad.

    Pues tengo varias ideas para el pero por el momento es mejor hablar sobre que es el sensor ADXL345 y cuales son sus características más destacadas.


    ¿Qué es el sensor ADXL345?

    El sensor ADXL345 es un acelerómetro digital de 3 ejes ampliamente utilizado en aplicaciones muy diversas, desde drones y robots hasta dispositivos portátiles y sistemas de control industrial.

    El sensor ADXL345 tiene una muy popular gracias a su alta precisión, bajo consumo de energía y facilidad de uso.

    Aplicaciones típicas para el sensor ADXL345:

    • Detección de movimiento: Detección de movimiento: En sistemas de seguridad, juegos, interfaces gestuales.
    • Control de orientación: Control de orientación: En drones, robots, estabilizadores de imagen.
    • Monitoreo de vibraciones: En maquinaria industrial, equipos de diagnóstico.
    • Interfaz hombre-máquina: En dispositivos portátiles, controles remotos.
    • Reconocimiento de patrones de movimiento: En aplicaciones de fitness, rehabilitación.

    Principales Características técnicas funcionalidades:

    • Consumo de 23uA en modo de medición y 0.1uA en modo standby
    • Voltaje en un rango de 2.0V a 3.6V (ADXL345 funciona a 3,3V)
    • Conexión SPI de 3 o 4 lineas e interfaces I2C
    • Resolución fija de 10-bits y una máxima de 13-bits
    • Temperatura de uso -40º hasta +85º

    Pines Esenciales para Conexión

    • VCC (Pin 1): Alimentación 3.3V - ¡NO usar 5V!
    • GND (Pin 2): Conexión a tierra
    • SDA (Pin 8): Datos I2C - Conectar a SDA
    • SCL (Pin 9): Clock I2C - Conectar a SCL
    • CS (Pin 4): Debe estar en HIGH (3.3V) para modo I2C
    • INT1/INT2 (Pines 5-6): Salidas de interrupción (opcionales)
    • SDO (Pin 7): Define dirección I2C (GND=0x53, VCC=0x1D)

    Las principales funcionalidades del sensor ADXL345 pueden ser:

    • Medición de Aceleración: Mide la aceleración en los tres ejes del espacio (X, Y, Z), así permite determinar la orientación, inclinación o movimiento de un objeto.
    • Rango de medición configurable: Permite mediciones de ±2g, ±4g, ±8g y ±16g, adaptándose a diferentes aplicaciones.
    • Alta Resolución: Con una resolución de hasta 13 bits, lo que se traduce en una alta precisión en la medición de la aceleración.
    • Bajo Consumo de Energía: Consume muy poca energía, tanto en modo de medición como en modo de espera, con consumos tan reducidos es ideal para dispositivos alimentados por batería.
    • Comunicación: Puede comunicarse a través de interfaces I2C o SPI.
    • Regulador de Voltaje Integrado: Incorpora un regulador de voltaje interno, simplificando el diseño del circuito.
    • Tasa de muestreo configurable: Permite ajustar la frecuencia a la que se toman las medidas
    • Interrupciones configurables: Se puede configurar para generar interrupciones cuando se detectan ciertos eventos, como una aceleración determinada o un cambio de orientación.

    ¿Cómo Funciona el módulo ADXL345?

    El ADXL345 utiliza micro-condensadores que se desplazan físicamente cuando se aplica una fuerza (aceleración). Este desplazamiento cambia la capacitancia de los condensadores, lo que se traduce en una señal eléctrica que es convertida a un valor digital por un convertidor analógico-digital (ADC) interno.

    En esta señal digital es donde se encuentran los datos que utilizaremos para representar las coordenadas X Y Z.

    ¿Como usar el sensor ADXL345?

    Para utilizar el sensor ADXL345 de una manera más amigable tenemos disponibles varias librerías que simplifican su uso y que como comentamos mas arriba, nos permitirá tanto lectura de datos como programar eventos.

    Estas son las librerías más populares para el sensor ADXL345:

    • Adafruit ADXL345 library:
      Es una de las librerías más conocidas y documentadas, desarrollada por Adafruit Industries.
      Esta librería ofrece una amplia gama de funciones para configurar y leer datos del sensor, como establecer el rango de medición, ajustar la tasa de muestreo y leer los valores de aceleración en los tres ejes. Es muy fácil de usar gracias a su interfaz es intuitiva y bien estructurada, lo que facilita su integración en tus proyectos.
    • SparkFun ADXnodemcu adxl 345L345 library:
      Muy similar a la librería de Adafruit pero desarrollada por SparkFun Electronics.

      Esta librería comparte muchas similitudes con la de Adafruit y puede incluir algunas funciones adicionales o variaciones en la implementación, pero en general, su funcionalidad es prácticamente la misma.

    • Jarzebski Arduino ADXL345:
      Esta librería ofrece algunas funciones más avanzadas, como la detección de caídas libres y la detección de golpes y una mayor personalización de la configuración del sensor. Incluye gravedades como Marte, la Luna o el sol. No es la más conocida pero merece la pena darle un vistazo.
    nodeMCU 8266 pinout
    NodeMCU 8266 Pinout

    Opciones avanzadas:


    Para el ejemplo del sensor ADXL345 vamos a utilizar la librería Adafruit ADXL345 library conectado a un microcontrolador ESP8266 NodeMCU por protocolo I2C. El sensor es alimentado por los 3.3V que ofrece el microcontrolador y el NodeMCU se alimenta por el mismo cable USB por donde recibiremos los datos.

    El ejemplo es el mismo que aparece en su web así que no pondré parte del ejemplo que esta en la misma librería. Una vez cargado el programa pasará por los siguientes procesos:

    • Creamos el sensor:
      
          /* Crea el sensor. 12345 asigna una ID unica para el sensor*/
          Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12345);
          
          
    • En el apartado void setup() inicializamos el sensor y seleccionamos el rango:
      
            /* Inicializa el sensor */
            if(!accel.begin()) {
            	/* Hay un problema al conectar ADXL345 ... compruebe las conexiones... */
            	Serial.println("Ooops, no ADXL345 detected ... Check your wiring!");
            	while(1);
            }
            
            /* Selecciona el rango apropiado para tu proyecto */
            accel.setRange(ADXL345_RANGE_16_G);
          
          
    • Una vez en el loop tomamos los eventos para luego mostrarlos en el monitor serial. Los datos se acualizan cada 500ms.
      
          void loop(void) {
          /* Get a new sensor event */ 
          sensors_event_t event;
          accel.getEvent(&event);
          
          /* Display the results (acceleration is measured in m/s^2) */
          Serial.print("X: "); Serial.print(event.acceleration.x); Serial.print("  ");
          Serial.print("Y: "); Serial.print(event.acceleration.y); Serial.print("  ");
          Serial.print("Z: "); Serial.print(event.acceleration.z); Serial.print("  ");Serial.println("m/s^2 ");
          delay(500);
          }
          
          

    Con todo funcionando tenemos que tener una salida en el monitor similar a esta:

    
    Accelerometer Test
    
    ------------------------------------
    Sensor:       ADXL345
    Driver Ver:   1
    Unique ID:    12345
    Max Value:    -156.91 m/s^2
    Min Value:    156.91 m/s^2
    Resolution:   0.04 m/s^2
    ------------------------------------
    
    Data Rate:    100  Hz
    Range:         +/- 16  g
    
    X: 0.47  Y: -1.18  Z: 12.20  m/s^2 
    X: 0.59  Y: -1.06  Z: 12.32  m/s^2 
    X: 0.51  Y: -1.10  Z: 12.28  m/s^2 
    X: 0.59  Y: -1.14  Z: 12.24  m/s^2 
    X: 0.08  Y: -0.71  Z: 12.24  m/s^2 
    X: 10.16  Y: -1.22  Z: 2.51  m/s^2 
    X: 10.40  Y: -0.90  Z: 2.51  m/s^2
    
    

    Salida Serial ADXL345
    Salida serial ESP8266 NodeMCU y ADXL3345

    Es realmente sencillo conectar el sensor a cualquier microcontrolador que admita protocolo I2C además de tener la opción SPI. Espero que este ejemplo os sirva de ayuda para futuros proyectos.

    Hoja de datos: ADXL345 DataSheet

    Otros enlaces con temas relacionados con la plataforma Arduino:

    Saludos a todos y todas!!!

    domingo, 1 de septiembre de 2024

    Pantalla OLED 0.96″ I2C con Raspberry Pi 3: Tutorial y monitorización

    Monitorear Raspberry con Oled 128x64 I2CMuy buenas a todos y todas amantes de Raspberry Pi!!!!

    Hace un tiempo conecte la Raspbeery pi y la pantalla Oled I2C con la librería "lemur", tenéis toda la información en la entrada 'Raspberry Pi 2 con Oled 128x64 I2C'.

    Ahora lo quiero aplicar a la Raspberry Pi 3 y añadirlo al ventilador pwm que ya instalamos anteriormente así que aprovecho para darle una actualización a la entrada anteriormente enlazada y añadir algunos pasos más.

    La librería que usamos anteriormente está descontinuada ahora la información ahora la información esta en la propia web de Adafruit.

    Los cambios son mínimos ya que solo instalando la librería y cambiando las lineas de configuración todo vuelve a funcionar como antes.

    Veamos como instalar las librerías relacionadas adafruit-circuitpython-ssd1306.


    
    sudo apt-get install python3-pip	
    
    pip3 install adafruit-circuitpython-ssd1306
    
    sudo apt-get install python3-pil
    
    sudo apt-get install python3-numpy
    
    

    Para usar pantalla Oled 128x64 I2C hay que importar la librería de la siguiente manera:


    
    import board
    import adafruit_ssd1306
    from PIL import Image, ImageDraw, ImageFont
    
    # Use for I2C.
    i2c = board.I2C()  # uses board.SCL and board.SDA
    
    try:
    	disp = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c, addr=0x3C, reset=RST)
        print("\nAdafruit SSD1306 ok")
    except Exception as errors:
    	print("\nError Adafruit SSD1306:",str(errors), "\nPlease check Raspi-config\n")
    	exit()
    
    

    Una vez que tenemos la pantalla funcionando estos son los datos que vamos a mostrar:, 


    • IP de la red
    • Uso del CPU
    • Temperatura del procesador
    • Uso de Ram
    • Velocidad del ventilador (PWM)
    • Tiempo que lleva encendido el sistema
    • Sistema operativo
    Para conseguir todos estos datos que queremos mostrar en la pantalla usaremos las librerías externas:
    • psutil
    • subprocess
    • sys

    Empecemos por la IP


    La ip la podemos obtener con subprocess:


    
        ## Obtiene la ip del equipo
        # @ return String "0.0.0.0"
        def GetIP():
            return str(subprocess.check_output(['hostname', '-I'])).split(' ')[0].replace("b'", "")
    
    

    Obtenemos la carga de CPU con psutil:

    
        ## Obtiene la carga de la CPU
        # @ return float
        def GetCPU():
            return psutil.cpu_percent(1)
    
    

    La temperatura de la CPU con subprocess:

    
        ## Obtiene la temperatura de la CPU
        # @ return float
        def GetTemp():
            output = subprocess.run(['vcgencmd', 'measure_temp'], capture_output= True)
            temp_str = output.stdout.decode()
            try:
                return float(temp_str.split('=')[1].split('\'')[0])
            except (IndexError, ValueError):
                raise RuntimeError('no se puede obtener temperatura')
    
    

    La RAM en uso mediante psutil:

    
        ## Obtiene la cantidad de RAM en uso
        # @ return float
        def GetRAM():
            return psutil.virtual_memory()[2]
    
    

    El PWM que aplicamos al pin viene nos lo devuelve la clase healt_pwm_class.py:

    
        ## Obtiene la velocidad del ventilador
        # @ hpc: healt_pwm_class.System_PWM_Fan
        # @ type_ int: 0=Proporcional, 1=Por pasos de temperatura
        # @ return int: valor de pwm aplicado en el pin
        def GetFanPWM(sef, hpc, type_):
        	if type_ == 0:
            	return hpc.Proportional()
    	else:
            	return hpc.Stepped()
    
    

    El sistema operativo lo obtenemos gracias a sys:

    
        ## Función que devuelve el SO en uso
        # @ return String
        def getOS():
            platforms = {
            'linux1' : 'linux',
            'linux2' : 'linux',
            'darwin' : 'mac',
            'win32' : 'win32'
            }
            if sys.platform not in platforms:
                return sys.platform
            
            return platforms[sys.platform]    
    
    

    Y por último el tiempo que lleva encendida la maquina con psutil:

    
        ## Obtiene el tiempo que lleva la maquina encendida
        # @ return String "0d 00h 00m 00s"
        def GetUpTime():
            boot_time_timestamp = psutil.boot_time()
            current_time_timestamp = time.time()
            uptime_seconds = current_time_timestamp - boot_time_timestamp
            uptime_minutes = uptime_seconds // 60
            uptime_hours = uptime_minutes // 60
            uptime_days = uptime_hours // 24
            uptime_str = f"{int(uptime_days)}d {int(uptime_hours % 24)}h {int(uptime_minutes % 60)}m {int(uptime_seconds % 60)}s"
            # print("Uptime: " + uptime_str)
            return uptime_str
    
    

    Como todas las funciones están en una clase llamada "system_functions_class.py" podemos llamar a estas funciones de manera individual directamente desde la clase de la siguiente manera:

    
        # Importamos la clase
        from system_functions_class import *
        
        sfc = System_Functions()
    	# Retorna la IP del equipo
        ip = sfc.GetIP()
        
        # Retorna la temperatura de la CPU
        cpu_temp =.sfc.GetTemp()
        
        # Imprime los resultados de ip y cpu_temp
        print("IP: {}, CPU temp: {}".format(ip, cpu_temp))
        
    

    ¿Como usamos el programa?

    Primero clonamos el repositorio rpi3_healt_system desde GitHub. Vamos a nuestra carpeta de proyectos y escribimos:


    
    https://github.com/Peyutron/python-Warehouse/tree/main/Raspberry_Python/rpi3_healt_system
    
    

    Con el programa en nuestro equipo, el ejecutable principal se llama "main_rpi_healt.py y lo ejecutamos con:

    
    python3 -m main_rpi_healt
    
    

    Si todo ha ido bien con la instalación en el terminal recibiremos el siguiente mensaje:

    
    Adafruit ssd1306 ok
    
    

    y en la pantalla se dibujara lo siguiente:


    Los datos que se muestran se guardan en un archivo JSON. Esto nos servirá en un futuro para enviar los datos a otros equipos o como consulta de la última vez que se ejecuto el programa. La información se guarda en un archivo llamado healt_datas.json y este es su contenido:

    
    {
        "filename": "healt_datas.json",
        "ip": "192.168.1.39",
        "m0_cpuf": 39,
        "m0_cpul": 0.0,
        "m0_cput": 45.6,
        "m0_os": "linux",
        "m0_raml": 18.5,
        "m0_ut": "0d 4h 48m 55s"
    }
    
    

    Un saludo a todas y todos!!

    Aquí tienes otros enlaces de este blog relacionados con Raspberry Pi:

    lunes, 26 de septiembre de 2016

    Arduino: Pantalla TFT de 3.5" primeros pasos

    ILI9481 con arduino UNO

    Muy buenas a todos los Arduineros y Arduineras!!!

    Ya estamos de vuelta del verano!! Ya hace un tiempo que he adquirido una pantalla TFT. Es una pantalla que puedes encontrar fácilmente en Ebay y a la cual se le puede sacar mucho jugo.

    Lo único malo es que no dispone de panel táctil pero si adaptador de tarjeta microSD. Tiene una resolución de 480x320.

    Especificaciones técnicas:

    • Dimensiones: 3.5"
    • Controlador: ILI9481 o ILI9488 driver
    • Resolución: 480x320
    • Data bits: 8 - 16
    • Número de pines de la interfaz: 8 pin
    • Lector de tarjetas: Si, microSD