Mostrando entradas con la etiqueta i2C. Mostrar todas las entradas
Mostrando entradas con la etiqueta i2C. 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:

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:

domingo, 5 de noviembre de 2023

Raspberry pi 2 con Oled 128x64 I2C

 Raspberry Pi ese pequeño ordenador de "bajo coste" que poca presentación necesita. Todos sabemos de su versatilidad y potencia. Raspberry Pi se usa en muchisimos proyectos de robotica, mini PCs, servidores y un largo etcétera. Por su puesto gracias a su puerto GPIO donde podremos conectar otra gran cantidad de sensores y accesorios.

En mi caso tengo un Raspberry Pi 2 Model B. A día de escribir esta entrada ya tiene unos añitos pero sigue dando guerra. La tengo para hacer mis cosillas de programación, también alguna vez como emulador de juegos retro o como es este caso, para cacharrear.

En este caso he añadido una pequeña pantalla Oled de 128x64 y comunicación I2C. Es la que uso generalmente con Arduino y en tiendas de internet lo podemos encontrar por unos 2 o 3 euros.
Si bien es una pantalla pequeña es lo suficiente para poder mostrar cierta información en ella así como imágenes o animaciones.

sábado, 23 de diciembre de 2017

Arduino Ejemplos: Reloj para coche con RTC DS1307, LCD y DHT11

Hola a todos y todas!!!

Hace un tiempo se me rompió el reloj del coche, un viejito Clio de 2004. Estuve mirando el repuesto y estaba entre 20 y 40€ y su función en mi coche era solamente dar la hora, el sensor de temperatura era un extra que no puse en su momento.

Bueno después de muchos años el reloj del coche paso a mejor vida...

Arduino ha sido una solución original y económica para este problema. Con menos de 10€ he montado un reloj con 2 sensores de temperatura/humedad DHT11 un LCD a juego con la iluminación interior de coche (rojo) y un modulo RTC D1307 i2C.

Este programa es una variación de otro que publique en este articulo Arduino: Reloj calendario con RTC DS1307 i2C.


Componentes necesarios:

  • 1 x Arduino NANO.
  • 1 x LCD 16x2.
  • 2 x DHT11.
  • 3 x Pulsadores
  • 1 x Divisor de tensión.

Arduino_RTC, DHT11 and voltmeter
Arduino Reloj RTC para coche.

viernes, 10 de noviembre de 2017

Arduino Ejemplos: Mando RC XboxRC24 con NRF24L01 – Software y pestañas (Parte 2)

Buenas a todos y todas Ardublogeros!!!

La primera versión del software ya esta lista así que en esta entrada voy a explicar el código ya que esta vez he optado por estructurar el software por pestañas como vimos en la entrada anterior: Ejemplos Arduino: Trabajando con pestañas en el Sketch.

Es algo que ya había visto en algún proyecto y no le había hecho mucho caso, pero en programas donde hay muchas lineas y funciones se ha convertido en algo fundamental para poder organizar el programa.

Otra de las ventajas es que podemos reutilizar el código para otros proyectos solo importando el *.ino y añadiéndolo al proyecto.


Estructura de archivos

XboxRC24 Transmitter esta estructurado en 8 pestañas y son las siguientes:

  1. Transmisor_XboxRC.ino
  2. Calibracion.ino
  3. Caracteres.ino
  4. Comunicacion.ino
  5. LCD.ino
  6. Memoria.ino
  7. Menu.ino
  8. Modo_tanque.ino

viernes, 3 de noviembre de 2017

Arduino Ejemplos: Mando RC XboxRC24 con NRF24L01 – Transmisor (Parte 1)

Buenas a todos y todas los fanáticos y fanáticas de Arduino!!!

Desde siempre me ha gustado el radio control, coches, aviones, barcos... todo lo que se pueda controlar remotamente, simplemente me encanta.

Arduino ha hecho un gran trabajo con esto, ha puesto a disposición del consumidor muchos módulos con un coste realmente bajo y un efectividad realmente alta.

Ya no solo por que puedas construir tu propio emisor/receptor (como va a ser este caso) si no por la cantidad de información que puedes enviar y recibir (Temperatura, GPS, imagen, etc..)

Después de estar hablando con un amigo bastante "gamer" y fan de Xbox de siempre, me recomendó que si hacia algo así lo hiciera con un mando de Xbox, sobre todo por los gatillos y la comodidad de agarre.

Aunque como todos sabemos esto es muy subjetivo, no quiero crear un debate sobre que mando de video consola es mejor o peor.

En resumen, elegí este mando por los consejos de mi amigo y por el tamaño del mismo, en el se pueden meter todas las piezas, aunque lamentablemente la batería que tendrá que quedarse fuera.


¿Que vamos a utilizar?





Esquema RCXbox LCD Infotronikblog
Esquema RCXbox LCD

jueves, 26 de octubre de 2017

Arduino Ejemplos: Pantalla LCD 16x2 con módulo I2C

Buenas a todos Arduineros y Arduineras!!!

Si ya vimos antes como utilizar un LCD 16x02, Arduino: El LCD 16x2 y
Arduino: Diferentes efectos en un LCD 16x2
directamente conectando a Arduino usando en total un mínimo de 6 líneas, esto puede que no sea un problema si tú proyecto no utiliza muchos sensores, pero si es necesario usar el máximo de pines disponibles existe una solución que puedes usar, el módulo I2c para LCD.
Con este dispositivo conseguiremos un ahorro en los pines de nuestro Arduino pudiendo, además, conectar más dispositivos I2c a las mismas líneas de control.

El bus TWI/i2c:


El bus serie i2c fue creado por Philips en 1982 para la comunicación entre microcontroladores y periféricos.

El TWI (Two Wired Interface) es idéntico a nivel técnico pero Atmel tuvo que cambiar las siglas por motivos de licencias, pero a efectos prácticos es lo mismo.

I2c está diseñado como un bus de comunicación Maestro-Esclavo, con esto uno de los dispositivos (maestro) regula la comunicación y los demás dispositivos (esclavos) que atienden sus peticiones.

Este bus está formado por dos líneas, SLK y SDA.

  • SLK (Serial Clock) marca la velocidad de comunicación y marca los tiempos para la lectura/escritura en el bus.
  • SDA (Serial Data) es la línea por donde se transmite la información.

Cada dispositivo conectado al bus tiene una dirección, así que para conectarnos con el debemos saber dicha dirección, con este programa podréis saber cual es la dirección de vuestro dispositivo:

leer dirección de un dispositivo i2C

Conexión Arduino modulo i2C:
Conexión Arduino UNO con LCD i2C
Conexión Arduino UNO con LCD i2C