Teclado y Ratón HID en CircuitPython HID

Estos ejemplos han sido actualizados para la versión de la librería HID para CircuitPython 4+. En algunas tarjetas como las CircuitPlayground Express, esta librería está integrada en CircuitPython. Así que use la última versión de CircuitPython para estos ejemplos. (Al menos 5.0.0-beta3).

Una de las cosas que hemos integrado a CircuitPython es 'HID' o Dispositivo control para Interfaces Humanas - lo que quiere decir, capacidades de teclado y de mouse. Esto significa que tu tarjeta de CircuitPython puede trabajar como un dispositivo de teclado, presionando teclas y comandos, o como un mouse que mueve su puntero y presiona botones. Esto puede ser muy útil porque incluso si no puedes adaptar tu software para trabajar con hardware, siempre hay una interface de teclado - así que si deseas una interfaz de toque capacitivo para un juego, por ejemplo, ¡el emular un teclado es algo que tienes funcionando en momento!

Esta sección te guía sobre el código necesario para crear un emulador de teclado o mouse. Primero, vamos a recorrer un ejemplo que utiliza pines en tu tarjeta para emular una entrada de teclado. Luego, vamos a mostrarte como cablear un joystick para que funcione como mouse, y analizar el código que hace que esto sea posible.

Vas a necesitar la librería de adafruit_hid en tu carpeta /lib si no las has copiado todavía. Puedes conseguirla en el compilado de Librerías de CircuitPython. Si necesitas ayuda instalando esta librería, revisa la página de Librerías para CircuitPython.

Emulador de Teclado con CircuitPython

Copia y pega el código hacia code.py utilizando tu editor favorito y salva el archivo.

# CircuitPython demo - Keyboard emulator

import time

import board
import digitalio
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.keycode import Keycode

# A simple neat keyboard demo in CircuitPython

# The pins we'll use, each will have an internal pullup
keypress_pins = [board.A1, board.A2]
# Our array of key objects
key_pin_array = []
# The Keycode sent for each button, will be paired with a control key
keys_pressed = [Keycode.A, "Hello World!\n"]
control_key = Keycode.SHIFT

# The keyboard object!
time.sleep(1)  # Sleep for a bit to avoid a race condition on some systems
keyboard = Keyboard(usb_hid.devices)
keyboard_layout = KeyboardLayoutUS(keyboard)  # We're in the US :)

# Make all pin objects inputs with pullups
for pin in keypress_pins:
    key_pin = digitalio.DigitalInOut(pin)
    key_pin.direction = digitalio.Direction.INPUT
    key_pin.pull = digitalio.Pull.UP
    key_pin_array.append(key_pin)

led = digitalio.DigitalInOut(board.D13)
led.direction = digitalio.Direction.OUTPUT

print("Waiting for key pin...")

while True:
    # Check each pin
    for key_pin in key_pin_array:
        if not key_pin.value:  # Is it grounded?
            i = key_pin_array.index(key_pin)
            print("Pin #%d is grounded." % i)

            # Turn on the red LED
            led.value = True

            while not key_pin.value:
                pass  # Wait for it to be ungrounded!
            # "Type" the Keycode or string
            key = keys_pressed[i]  # Get the corresponding Keycode or string
            if isinstance(key, str):  # If it's a string...
                keyboard_layout.write(key)  # ...Print the string
            else:  # If it's not a string...
                keyboard.press(control_key, key)  # "Press"...
                keyboard.release_all()  # ..."Release"!

            # Turn off the red LED
            led.value = False

    time.sleep(0.01)

Conecta el pin A1 o A2 hacia tierra, usando un cable de lagarto, y luego lo desconectas para presionar la tecla "A" o para escribir el texto "Hello world!" (o ¡Hola mundo!)

Este ejemplo de cableado muestra A1 y A2 conectados a tierra.

Recuerde, ¡en las Trinket, A1 y A2 están etiquetados como 2 y 0! En otras tarjetas, si vas a tener a A1 y A2 etiquetadas de forma esperada.

Creando los Objetos y las Variables

Primero, vamos a asignar unas variables para utilizar más tarde. Vamos a crear tres arreglos asignados a variables: keypress_pins, key_pin_array, y keys_pressed. El primero es los pines que vamos a utilizar. El segundo está vacío porque lo vamos a rellenar luego. El tercero lo que queremos que nuestro teclado escriba - en este caso la letra "A" y la frase "Hello World!". Creamos nuestra última variable, asignada a control_key el cual permite usar la tecla de "shift" cuando presionamos teclas. Nosotros vamos a utilizar hasta dos teclas de forma simultánea, pero es posible tener hasta seis teclas presionadas a la vez.

Luego creamos los objetos de keyboard y keyboard_layout. Por ahora solo tenemos US (si usted crea otro diseño de teclado, ¡favor envíe un Pull Request en Github!). Con time.sleep(1) nos evitamosun error que puede suceder si el programa comienza a trabajar apenas se conecta la tarjeta, antes de que la computadora pueda inicializar a la tarjeta como un teclado.

Luego tomamos los pines que seleccionamos arriba, y creamos los objetos de pin, definimos la dirección y les damos a cada uno un pull-up. Luego aplicamos los objetos de pin hacia key_pin_array para poderlos utilizar más tarde.

Luego, configuramos el LED rojo para poder utilizarlo como una luz de estado.

¡La última cosa que realizamos antes de comenzar el ciclo es imprimir (print) el texto "Waiting for key pin..." (o "Esperando a pin de tecla...") para que sepas que tu código está listo y esperando!

El Ciclo Principal

Dentro del ciclo, vamos a revisar cada pin para verificar que el estado haya cambiado, por ejemplo, si has conectado el pin hacia tierra. Una vez que cambie, imprime "Pin # grounded." para indicar que se ha detectado el estado en tierra. Luego prendemos el LED rojo. El código espera que el estado cambie de nuevo, por ejemplo, que desconectes el pin conectado a tierra.

Luego, el código recibe las teclas que debe presionar correspondientes al pin manipulado. Si conectaste a tierra y luego desconectaste A1, el código recibe para presionar la tecla de a, y si conectaste a tierra y luego desconectaste A2 el código recibe para presionar el texto de "Hello world!".

Si el código encuentra que ha recibido una cadena de texto, imprime la cadena usando el keyboard_layout para determinar las teclas que debe presionar. De otra forma, el código presiona las teclas de control_key y se presioa "a", lo que resulta en "A". Luego hace una llamada a keyboard.release_all() (para soltar todas las teclas presionadas). ¡Siempre quieres hacer este llamado tan pronto como presionas teclas o te va a ocasionar una tecla pegada que es realmente molesto!

En lugar de utilizar un cable para conectar los pines hacia tierra, puede tratar de cablear los botones como hicimos en Entradas y Salidas Digitales con CircuitPython. ¡Trata de modificar el código para agregar más pines para mayor opción de teclas para presionar!

Emulador de Ratón con CircuitPython

Copie y pegue el código dentro de code.py utilizando su editor preferido y salve el archivo.

import time

import analogio
import board
import digitalio
import usb_hid
from adafruit_hid.mouse import Mouse

mouse = Mouse(usb_hid.devices)

x_axis = analogio.AnalogIn(board.A0)
y_axis = analogio.AnalogIn(board.A1)
select = digitalio.DigitalInOut(board.A2)
select.direction = digitalio.Direction.INPUT
select.pull = digitalio.Pull.UP

pot_min = 0.00
pot_max = 3.29
step = (pot_max - pot_min) / 20.0


def get_voltage(pin):
    return (pin.value * 3.3) / 65536


def steps(axis):
    """ Maps the potentiometer voltage range to 0-20 """
    return round((axis - pot_min) / step)


while True:
    x = get_voltage(x_axis)
    y = get_voltage(y_axis)

    if select.value is False:
        mouse.click(Mouse.LEFT_BUTTON)
        time.sleep(0.2)  # Debounce delay

    if steps(x) > 11.0:
        # print(steps(x))
        mouse.move(x=1)
    if steps(x) < 9.0:
        # print(steps(x))
        mouse.move(x=-1)

    if steps(x) > 19.0:
        # print(steps(x))
        mouse.move(x=8)
    if steps(x) < 1.0:
        # print(steps(x))
        mouse.move(x=-8)

    if steps(y) > 11.0:
        # print(steps(y))
        mouse.move(y=-1)
    if steps(y) < 9.0:
        # print(steps(y))
        mouse.move(y=1)

    if steps(y) > 19.0:
        # print(steps(y))
        mouse.move(y=-8)
    if steps(y) < 1.0:
        # print(steps(y))
        mouse.move(y=8)

Para este ejemplo, hemos conectado un joystick de pulgar de 2 ejes con botón de selección. Usamos esto para emular el movimiento de un ratón y para realizar un click izquierdo. Para cablear este joystick:

  • Conecte VCC en el joystick a 3V de tu tarjeta. Conecte tierra a tierra (GND).
  • Conecte Xout en el joystick al pin A0 de tu tarjeta.
  • Conecte Yout en el joystick al pin A1 de tu tarjeta.
  • Conecte Sel en el joystick al pin A2 de tu tarjeta.

Recuerda, en las Trinket los pines están etiquetados de forma diferente. Revisa la  página de pines para las Trinket para verificar tu cableado.

Para utilizar esta demostración, simplemente mueva el joystick un poco. El puntero del ratón se va a mover lentamente si mueves el joystick un poco fuera del centro y más rápido si lo alejas más del centro. Presiona hacia abajo el joystick para realizar un click del ratón. ¡Asombroso! Ahora vamos a revisar el código.

Creando los Objetos y las Variables

Primero creamos el objeto mouse o ratón.

Luego, asociamos los ejes con x_axis y y_axis a los pines A0 y A1. Luego asociamos select con A2, lo definimos como una entrada y le asignamos un pull-up.

Los ejes "x" y "y" del joystick funcionan como 2 potenciómetros. Los vamos a usar de forma similar a como lo hicimos en Entradas Analógicas con CircuitPython. Definimos pot_min y pot_max para ser los valores mínimos y máximos que leen los potenciómetros. Asignamos step = (pot_max - pot_min) / 20.0 para usar en la función utilitario.

Utilitarios para Ratón HID en CircuitPython

Primero debemos obtener el voltaje con el utilitario get_voltage() para recibir lecturas correctas de los potenciómetros. ¿Le parece familiar? Aprendimos a utilizarlo en en Entradas Analógicas.

Segundo, tenemos steps(axis). Para usarlo, le damos el eje que vas a leer. Aquí es donde vamos a usar la variable step que asignamos anteriormente. El rango del potenciómetro es de 0-3.29. Esto es un rango pequeño. Es todavía más pequeño con el joystick porque el joystick está en el centro de su rango, 1.66, y la parte positiva o negativa de cada eje está por abajo o encima de este número. Ya que requerimos de umbrales en nuestro código, vamos a mapear el rango de 0-3.29 a números entre 0-20.0 utilizando esta función utilitario. De esta forma podemos simplificar nuestro código y utilizar rangos más grandes para nuestros umbrales en lugar de tratar de entender cambios decimales muy pequeños.

Ciclo Principal

Primero asignamos x y y para leer los voltajes de x_axis y y_axis.

Luego, revisamos si el estado del botón de select es False. Por omisión es True cuando no está presionado, así que si el estado es False, el botón ha sido presionado. Cuando es presionado, envía el comando para realizar un click izquierdo del puntero. time.sleep(0.2) previene que leamos múltiples clicks cuando solo se presionó una vez.

Ahora utilizamos la función de steps() para poner el puntero de nuestro ratón en movimiento. Tenemos unas parejas de if para cada eje. Recuerde que 10 es la posición central dado que la hemos mapeado a un rango de 0-20. Las primeras parejas le dicen a cada eje que si el joystick se mueve un paso fuera del centro (a la izquierda o a la derecha para el eje x y arriba o abajo para el eje y), mueva el puntero del ratón en la dirección apropiada por 1 unidad. Las segundas parejas para cada eje le dicen que si el joystick se mueve lo más lejos del centro como es posible, mueva el puntero del mouse en la dirección apropiada 8 unidades. ¡De esta manera tienes la opción de mover el ratón rápido o despacio!

Para ver cual step (o paso) está el joystick cuando lo mueves, descomenta las líneas con  print removiendo el de las líneas que se vean como # print(steps(x)), y conectando la consola serial para que puedas ver la salida. ¡Considera descomentarlas una a la vez, o vas a terminar con mucha información que tienes que navegar en la pantalla y puede que sea difícil de leer!

Para más detalles revisa la última versión de la documentación en https://circuitpython.readthedocs.io/projects/hid/en/latest/
This guide was first published on Jun 24, 2020. It was last updated on Jun 24, 2020.
This page (Teclado y Ratón HID en CircuitPython HID) was last updated on Jul 15, 2020.