Saltar al contenido principal

Botones, pines y mando

En Windows, los controles de tu juego son teclas. En una placa son botones conectados a sus pines GPIO, el mando I2C de Adafruit o, en el ESP32-S31, un teclado USB. Tu programa los lee igual en todos los casos: asigna cada acción con Input.map y pregunta por ella con Input.pressed. Las funciones están en el módulo Input; aquí está lo que cambia en la placa.

Si tu juego es de salas del editor de escenas, no escribes Input.map: los controles, sus pines y el mando se ponen en la pestaña Game, con Edit controls..., y el código que genera el editor los asigna (ver La ventana Game controls).

Input.map en la placa​

Lo que acepta Input.map depende de dónde corre el programa:

Input.map(acción, …)WindowsESP32 / ESP32-S3ESP32-P4ESP32-S31
Con el nombre de una tecla, como "LEFT"SíNo: lanza ERR_TYPE_MISMATCHNo: lanza ERR_TYPE_MISMATCHSí: una tecla del teclado USB
Con un GPIO, como 39No: lanza ERR_TYPE_MISMATCHSí, de 0 a 63Sí, de 0 a 63Sí, de 0 a 63

Un GPIO fuera de 0 a 63 lanza ERR_INVALID_VALUE. Son excepciones: si no las recoges con try, paran el programa (ver Errores y excepciones).

Para que el mismo programa funcione en Windows y en la placa, elige el mapeo con platform(), como hacen los ejemplos que trae JARU IDE:

use Input

const IZQUIERDA = 0
const DERECHA = 1
const SALTO = 2

if (platform() == HW_WIN32) then
Input.map(IZQUIERDA, "LEFT")
Input.map(DERECHA, "RIGHT")
Input.map(SALTO, "SPACE")
elsif (platform() == HW_ESP32) then
// Los tres botones de la LILYGO T4, los que usan los ejemplos
Input.map(IZQUIERDA, 39)
Input.map(DERECHA, 38)
Input.map(SALTO, 37)
end

Compara con HW_ESP32, no con «cualquier placa»: los pines de una placa no valen en otra (ver Pines que no valen para un botón). Las constantes de cada chip, como HW_ESP32S3 o HW_ESP32P4, son las de platform(), en la librería estándar.

Cómo se conecta un botón​

Input.map pone el pin como entrada, con su resistencia de pull-up interna, y lo lee al revés: la acción está pulsada mientras el pin está a 0 V. Conecta el botón entre el pin y GND. En reposo, la resistencia deja el pin a 3,3 V y la acción, suelta.

Los perfiles de placa no dicen qué pin es cada botón: míralo en el esquema de tu placa. La VM no lo comprueba: acepta cualquier número de 0 a 63, aunque tu chip no tenga ese GPIO o la placa lo use para otra cosa.

Los GPIO de cada chip​

ChipGPIO que tiene
ESP32Del 0 al 39, menos el 24 y del 28 al 31. Del 34 al 39 son solo de entrada.
ESP32-S3Del 0 al 48, menos del 22 al 25.
ESP32-P4Del 0 al 54.
ESP32-S31Del 0 al 61, menos el 29 y el 41.

Pines que no valen para un botón​

ChipPinesPor qué
TodosLos de la pantalla, la tarjeta SD, el sonido y el bus I2CYa son de otra cosa. Están en el perfil de tu placa: páginas Display, Storage y Audio de ESP32 Tools.
ESP321 y 3Son la consola serie: por ahí salen lo que imprime tu programa y los mensajes de la VM.
ESP32Del 34 al 39No tienen resistencia de pull-up interna. Sin una resistencia externa, el pin queda al aire, y la acción puede leerse pulsada sin que nadie la toque. Los botones de la LILYGO T4 llevan la suya.
ESP32-S343 y 44La consola serie.
ESP32-S319 y 20Son el USB del propio chip, por el que se conectan algunas placas, como la Waveshare ESP32-S3-Touch-LCD-2.8 y la JaruCade.
ESP32-S3Del 33 al 37Son de la PSRAM octal, la memoria con la que trabaja la VM del S3. Con Input.map en el 37, la placa se cuelga y se reinicia una y otra vez.
ESP32-P437 y 38La consola serie.
ESP32-S3158 y 59La consola serie.

Un botón que da 3,3 V al pulsarlo​

Si tu botón lleva el pin a 3,3 V al pulsarlo, Input.map lo lee al revés. Léelo tú con el módulo GPIO y pásale su estado a la acción con Input.set, una vez por fotograma y antes de preguntar por las acciones:

use GPIO
use Input
use Display

const BOTON = 21 // el GPIO de tu botón
const SALTO = 2

GPIO.pinmode(BOTON, GPIO.PULLDOWN) // suelto, el pin queda a 0 V

Display.open(320, 240)
Display.fps = 30
while (true)
Input.set(SALTO, GPIO.read(BOTON) == 1) // lo primero del fotograma
if (Input.justPressed(SALTO)) then
println("¡Salto!")
end
Display.update()
end

Con Input.set, la acción funciona como las demás: Input.pressed, Input.justPressed e Input.released dicen lo de siempre. Los GPIO del 34 al 39 del ESP32 tampoco tienen pull-down interna.

Lee los pines puestos como entrada

Para leer un pin, ponlo como entrada: GPIO.INPUT, GPIO.PULLUP o GPIO.PULLDOWN. En el ESP32-P4 y el ESP32-S31, un pin en GPIO.OUTPUT es solo de salida: GPIO.read() devuelve siempre 0 (ver Placas soportadas).

El mando I2C​

El mando I2C es el Adafruit Mini I2C Gamepad (seesaw, producto 5743): un joystick y seis botones, A, B, X, Y, Start y Select. Se conecta al bus I2C de la placa y contesta en la dirección 0x50 (de 0x51 a 0x53, si se cortan sus puentes A0 y A1). Es el mando de la JaruCade.

Los pines del bus están en el perfil de la placa, en la página Storage, grupo I2C Bus: SDA 11 y SCL 10 en la JaruCade y la Waveshare ESP32-S3-Touch-LCD-2.8, y SDA 7 y SCL 8 en las dos del ESP32-P4. La VM no los lee: los usan el editor de escenas y el gestor de drivers I2C para escribir tu código.

Con el editor de escenas​

En un juego de salas, el mando se enciende en la pestaña Game, con Edit controls...: eliges qué botón del mando maneja cada control, y el código que genera el editor arranca el mando y lo lee en cada sala. Todo está en El mando I2C, y un caso entero, con StarterPlatformer, en Tu primer juego con salas.

El mando del editor de escenas en el ESP32-S31

En el ESP32-S31, con el mando encendido y ningún control con GPIO, el juego no usa las teclas del teclado USB: si el mando no contesta, se queda sin entrada. Si juegas con el teclado, apaga el mando con Gamepad on, que pasa a Gamepad off.

Todos los problemas conocidos de JARU, juntos, están en Problemas conocidos.

Desde tu código​

Fuera del editor de escenas, el mando se maneja con el driver SeesawGamepad. Mételo en el proyecto desde el gestor de drivers I2C, en la categoría Input, con Inser Driver: lo copia a Code/Drivers y escribe su import (ver Drivers I2C).

El driver no tiene acciones propias: le dices qué acción de Input mueve cada botón y cada dirección del joystick, y en cada fotograma, con update(), lee el mando y se lo pasa a las acciones con Input.set. Tu juego sigue preguntando con Input.pressed.

use I2C
use Input
use Display
import "Drivers/DRV_SEESAW_GAMEPAD.aru"

const IZQUIERDA = 0
const DERECHA = 1
const SALTO = 2

var mando = nil
if (platform() == HW_WIN32) then
Input.map(IZQUIERDA, "LEFT")
Input.map(DERECHA, "RIGHT")
Input.map(SALTO, "SPACE")
else
I2C.init(0, 11, 10, 400000) // el bus de la JaruCade: SDA 11 y SCL 10
mando = SeesawGamepad(0)
if (mando.beginChecked()) then
mando.bind(SALTO, mando.BTN_A)
mando.bindAxes(IZQUIERDA, DERECHA, -1, -1, 200)
else
println("No hay mando: error ", mando.getLastError())
mando = nil
end
end

Display.open(320, 240)
Display.fps = 30
while (true)
if (mando != nil) then mando.update() end // lo primero del fotograma
if (Input.pressed(SALTO)) then
println("Salto")
end
Display.update()
end
MétodoQué hace
SeesawGamepad(puerto)Crea el driver en un puerto I2C ya abierto con I2C.init.
configureAddress(dir)Cambia la dirección, si has cortado los puentes. De serie, 0x50.
beginChecked()Busca el mando, comprueba que es el 5743 y lo prepara. Devuelve false si no está; getLastError() dice por qué.
bind(acción, botón)El botón mueve la acción: BTN_A, BTN_B, BTN_X, BTN_Y, BTN_START o BTN_SELECT.
bindAxes(izq, der, arriba, abajo, zonaMuerta)Las cuatro direcciones del joystick mueven esas acciones; -1, ninguna. zonaMuerta es cuánto se aparta del centro antes de contar: el joystick va de 0 a 1023, con el centro en 512.
update()Lee el mando y pone las acciones. Llámalo una vez por fotograma, antes de leer las acciones. Devuelve false si una lectura ha fallado.
pressed(botón), axisX() y axisY()El botón y el joystick tal como los leyó el último update().

Llama a tus Input.map antes de crear el mando: Input.map borra lo que el mando haya puesto en esa acción. Un botón y una dirección pueden mover la misma acción.

El teclado USB del ESP32-S31​

La placa del ESP32-S31, la Korvo-1, tiene un puerto USB-A donde se enchufa un teclado. Con él, Input.map acepta los mismos nombres de tecla que en Windows (la lista, en Input), y también GPIO:

use Input

const IZQUIERDA = 0
const DERECHA = 1
const SALTO = 2

if (platform() == HW_WIN32 or platform() == HW_ESP32S31) then
// Las mismas teclas en Windows y en el teclado USB del ESP32-S31
Input.map(IZQUIERDA, "LEFT")
Input.map(DERECHA, "RIGHT")
Input.map(SALTO, "SPACE")
end
  • Se enciende en ESP32 Tools, con la casilla USB keyboard de la página Hardware, que solo sale con el ESP32-S31. El perfil de la Korvo-1 la trae marcada.
  • El teclado, en su modo más sencillo. La VM abre el teclado en el modo de arranque de USB (el protocolo boot). Un teclado que no lo tenga no se lee, ni tampoco los ratones, los mandos USB o las teclas multimedia.
  • Dos teclados a la vez, como mucho, y hasta seis teclas a la vez en cada uno, sin contar SHIFT, CTRL y ALT. Con más, el teclado no dice cuáles son, y la VM se queda con las que tenía.
  • SHIFT, CTRL y ALT son las de la izquierda, como en Windows.
  • Se puede enchufar y desenchufar con el programa en marcha. El monitor serie lo cuenta: «USB host ready: plug a keyboard into the USB-A port.» al arrancar, y después «USB keyboard connected» y «USB keyboard disconnected».
  • Si deja de responder, el monitor dice «USB keyboard transfer error: unplug it and plug it again.»: desenchúfalo y vuelve a enchufarlo. Si la placa deja de ver el teclado del todo, quítale la alimentación y vuelve a dársela.

Los botones de la Korvo-1​

Los cuatro botones de la Korvo-1 (SET, MODE, VOL- y VOL+) no van cada uno a su GPIO: van todos al GPIO 42, cada uno con una tensión distinta. Input.map no los puede leer; léelos con GPIO.aread(42). En la placa con la que se probó la VM, GPIO.aread(42) da 0 sin pulsar nada, 315 con SET, 853 con MODE, 1369 con VOL- y 1816 con VOL+. Esta función pone la frontera a mitad de camino entre cada dos lecturas:

use GPIO

// Los cuatro botones de la Korvo-1 van al GPIO 42, cada uno con su tensión.
// Devuelve el pulsado, o "" si no hay ninguno.
func BotonKorvo()
var v = GPIO.aread(42)
if (v < 157) then return "" end
if (v < 584) then return "SET" end
if (v < 1111) then return "MODE" end
if (v < 1592) then return "VOL-" end
return "VOL+"
end

Comprueba antes los números de tu placa, con println(GPIO.aread(42)). Con dos botones pulsados a la vez, la lectura no dice cuáles son. Para usarlos como acciones, pásalos con Input.set, como en Un botón que da 3,3 V al pulsarlo.