HayBox es un firmware modular multiplataforma para controladores digitales o mixtos analógicos/digitales, dirigido principalmente a controladores estilo B0XX.
Las características incluyen:
Si simplemente desea utilizar un firmware prediseñado con configuraciones y asignaciones de pines predeterminadas, consulte la sección de binarios prediseñados. Si desea realizar algún cambio en el código, consulte la sección de creación desde el código fuente.
.uf2
), simplemente póngalo en modo de arranque mientras lo conecta a su PC, y arrastre y suelte el archivo .uf2
en la unidad RPI-RP2 que aparece..hex
), puede usar un programa como QMK Toolbox para actualizar el archivo .hex
Actualmente existen tres formas principales de construir HayBox:
Tanto GitHub Actions como GitHub Codespaces requieren que crees una cuenta de GitHub, pero no requieren que instales ninguna dependencia en tu máquina local.
Se requieren las siguientes dependencias al construir localmente:
Después de instalar todos los requisitos, descargue y extraiga la última versión de HayBox o clone el repositorio si tiene git instalado (lo que le facilitará la obtención de actualizaciones).
Después:
git config --global core.longpaths true
en cualquier terminal (dentro de VS Code o cmd/PowerShell normal están bien)config/<environment>/config.cpp
). Cualquier botón que su controlador no tenga puede simplemente eliminarse de la lista.HayBox/.pio/build/<environment>/firmware.uf2
en el RPI -Unidad RP2 que aparece.Esta es probablemente la forma más conveniente de modificar y reconstruir HayBox, pero tenga en cuenta que el nivel gratuito de GitHub impone algunas limitaciones sobre cuánto puede usar Codespaces cada mes. Debido a esto, querrás asegurarte de cerrar tus Codespaces cuando no los estés usando, para maximizar lo que puedes obtener de tu cuota.
Primero, cree una cuenta de GitHub o simplemente inicie sesión si ya tiene una, luego bifurque este repositorio y abra su bifurcación en un nuevo Codespace haciendo clic en el botón verde Código -> Codespaces -> Crear codespace en maestro. Esto debería abrir VS Code en su navegador con todas las extensiones y dependencias necesarias preinstaladas. A partir de aquí, el proceso es muy similar al de la compilación local, excepto que no puede usar el botón Cargar para actualizar el firmware. En su lugar, tendrá que descargar el binario compilado desde HayBox/.pio/build/<environment>/
y actualizarlo manualmente (consulte aquí para obtener más información al respecto).
Este repositorio contiene una definición de flujo de trabajo de GitHub Actions que crea cada entorno PlatformIO especificado en la matriz en cada envío y carga archivos binarios de firmware como artefactos que puede descargar haciendo clic en una ejecución de flujo de trabajo específica del historial. Puede crear una bifurcación de este repositorio y habilitar Acciones haciendo clic en Configuración -> Acciones -> General -> Seleccione "Permitir todas las acciones y flujos de trabajo reutilizables" -> Guardar.
La forma más rápida de realizar cambios si solo desea compilar a través de GitHub Actions es usar github.dev. Puedes hacerlo simplemente presionando .
en su teclado mientras tiene abierta la bifurcación de este repositorio, y se abrirá un editor de VS Code en su navegador. Esto no le brinda las mismas capacidades de desarrollo que obtendría en un Codespace, pero le permite realizar cambios y confirmarlos directamente desde su navegador. Cambie lo que desee, luego use la pestaña Control de código fuente a la izquierda para agregar, confirmar e impulsar sus cambios. Finalmente, regrese al repositorio y haga clic en la pestaña Acciones, haga clic en la ejecución de su flujo de trabajo y espere a que cree el artefacto.
Si está agregando una nueva configuración de dispositivo/entorno PlatformIO, deberá agregar el entorno a la matriz para que el flujo de trabajo de GitHub Actions lo cree. También puede eliminar cualquier entorno de la matriz que no le interese para reducir el uso de recursos y potencialmente acelerar sus compilaciones.
Para reiniciar los controladores basados en Pico en modo de arranque, mantenga presionado Iniciar en el complemento.
Para cambiar al modo de placa Brook en GCCPCB2, GCCMX, B0XX R2 o LBX, mantenga presionado B en el complemento.
Los backends de comunicación se seleccionan de forma ligeramente diferente según el tipo de microcontrolador utilizado en el controlador.
En Pico/RP2040, USB vs GameCube vs Nintendo 64 se detecta automáticamente. Si no está conectado a una consola, el valor predeterminado es XInput , que debería funcionar plug-and-play con la mayoría de los juegos de PC. Otros backends se seleccionan manteniendo presionado uno de los siguientes botones en el complemento:
En Arduino/AVR, el backend DInput se selecciona si se detecta una conexión USB. De lo contrario, el valor predeterminado es el backend de GameCube, a menos que se seleccione manualmente otro backend manteniendo presionado uno de los siguientes botones en el complemento:
A diferencia de otros firmwares similares, HayBox de forma predeterminada le permite cambiar de modo sobre la marcha sin desconectar el controlador. Esto es principalmente útil en PC, a diferencia de la consola, donde normalmente tienes que reiniciar la consola para cambiar de juego de todos modos. También sirve para reducir la cantidad de botones que debe sostener con una mano mientras lo conecta.
Las combinaciones de botones del modo de controlador predeterminadas son:
Combinaciones de botones del modo de teclado predeterminadas (solo disponibles cuando se usa el backend DInput, no con XInput):
HayBox necesita un perfil de controlador Dolphin diferente al del firmware oficial B0XX, ya que utiliza diferentes asignaciones DInput que tienen más sentido para su uso en múltiples juegos. Estos se pueden encontrar en la carpeta dolphin
en el repositorio de HayBox. Los archivos de perfil tienen nombres para indicar para qué backend de comunicación y sistema operativo son:
Para instalar el perfil:
dolphin
dentro de HayBox a la carpeta <YourDolphinInstallation>UserConfigProfilesGCPad
(créelo si no existe)%appdata%Slippi LaunchernetplayUserConfigProfilesGCPad
~/.config/SlippiOnline/Config/Profiles/GCPad/
Cmd + Shift + G
e ingresa la ruta /Users/<USER>/Library/Application Support/Slippi Launcher/netplay/Slippi Dolphin.app/Contents/Resources/Sys/Config/Profiles/GCPad
%userprofile%DocumentsDolphin EmulatorConfigProfilesGCPad
~/.config/dolphin-emu/Profiles/GCPad/
* macOS solo admite DInput (y no muy bien), por lo que si usa un controlador basado en Pico/RP2040 tendrá que forzar el modo DInput manteniendo presionado Z en el complemento, e incluso así puede que no funcione. Realmente no puedo hacer nada con respecto a la mala compatibilidad con el controlador de Apple (que parece que se rompe con cada otra actualización) y no tengo ningún dispositivo Apple, por lo que esto también se considerará uso no compatible de HayBox.
El backend de comunicación (por ejemplo, DInput, GameCube o N64) se selecciona en parte mediante detección automática y en parte en función de los botones que se mantienen en el complemento. Esto se maneja en config/<environment>/config.cpp
, en la función setup()
. La lógica es bastante simple, e incluso si no tienes experiencia en programación, no debería ser demasiado difícil ver qué está pasando y cambiar las cosas si lo deseas.
Las carpetas de configuración correspondientes a los entornos Arduino son:
config/arduino_nativeusb/
para Arduino con soporte USB nativo (por ejemplo, Leonardo, Micro)config/arduino/
para Arduino sin soporte USB nativo (por ejemplo, Uno, Nano, Mega 2560) Para las configuraciones de dispositivos Arduino, puedes notar que el número 125 se pasa a GamecubeBackend()
. Esto te permite cambiar la tasa de sondeo, por ejemplo, si tu DIY no admite USB nativo y quieres usarlo con un adaptador de controlador GameCube overclockeado. En ese ejemplo, podría pasar 1000 para sincronizar hasta la tasa de sondeo de 1000 Hz, o 0 para desactivar completamente esta corrección de retraso. La tasa de sondeo se puede pasar al constructor N64Backend de la misma manera.
Quizás notes que la tasa de sondeo de 1000 Hz también funciona en la consola. Tenga en cuenta que, si bien esto funciona, provocará un mayor retraso de entrada. El objetivo de establecer la tasa de sondeo aquí es para que el backend de GameCube pueda retrasarse hasta justo antes de la siguiente encuesta antes de leer las entradas, para que las entradas estén actualizadas y no desactualizadas.
Para Pico/RP2040, no es necesario pasar una tasa de sondeo de la consola, porque Pico tiene suficiente potencia de procesamiento para leer/procesar entradas después de recibir el sondeo de la consola, por lo que no hay necesidad de predecir cuándo llegará el sondeo y preparar las cosas. por adelantado.
Para configurar los botones retenidos para los modos de entrada (modos de controlador/teclado), edite la función select_mode()
en config/mode_selection.hpp
. Cada declaración if
es una combinación de botones para seleccionar un modo de entrada.
La mayoría de los modos de entrada admiten pasar un modo de limpieza SOCD, por ejemplo, socd::2IP_NO_REAC
. Consulte aquí para conocer los otros modos disponibles.
Para crear nuevos modos de entrada, es útil saber algo de C++ o al menos tener algo de experiencia en programación. Dicho esto, deberías poder arreglártelas incluso sin experiencia previa si simplemente basas tu nuevo modo en los existentes y los mencionas como ejemplos.
Hay dos tipos de modos de entrada: ControllerMode y KeyboardMode
Los modos de teclado son un poco más simples, así que comencemos por ahí.
Un KeyboardMode se comporta como un teclado estándar y debería funcionar con cualquier dispositivo que admita teclados.
Eres libre de utilizar cualquier truco de lógica y programación que desees en la función UpdateKeys()
para decidir las salidas según el estado de entrada. Podrías crear capas de entrada (como la capa del D-Pad en el modo cuerpo a cuerpo que se activa al mantener presionados Mod X y Mod Y), u otros tipos de entradas condicionales.
La lista de códigos clave disponibles se puede encontrar aquí.
Recuerde que los modos de teclado solo se pueden activar cuando se utiliza el backend de comunicación DInput ( no XInput).
Un ControllerMode toma el estado de entrada de un botón digital y lo transforma en un estado de salida correspondiente a un gamepad estándar. Cualquier ControllerMode funcionará con cualquier CommunicationBackend. Un CommunicationBackend simplemente lee las entradas de una o más fuentes de entrada, utiliza el ControllerMode actual para actualizar las salidas en función de esas entradas y maneja el envío de las salidas a la consola o PC.
Para crear un ControllerMode, solo necesita implementar las funciones UpdateDigitalOutputs()
y UpdateAnalogOutputs()
.
UpdateDigitalOutputs()
es muy similar a la función UpdateKeys()
en los modos de teclado, con la diferencia de que en lugar de llamar a una función Press()
para enviar entradas inmediatamente, simplemente configuramos el estado de salida para esta iteración. Como su nombre indica, en esta función sólo nos ocuparemos de las salidas digitales.
UpdateAnalogOutputs()
es un poco más complicado. En primer lugar, tiene que llamar UpdateDirections()
antes de hacer cualquier otra cosa. Esta función toma valores que indican si los joysticks izquierdo y derecho apuntan hacia la izquierda/derecha/arriba/abajo. También pasa los valores analógicos mínimo, neutral (centro) y máximo de la palanca, para que pueda configurarlos por modo. Toda esta información se utiliza para configurar automáticamente los valores analógicos de la palanca en función de las entradas que ingresaste. Esto es todo lo que necesitas hacer a menos que quieras implementar modificadores.
UpdateDirections()
también completa las directions
variables con valores que indican la dirección actual del joystick, que puede ser 1, 0 o -1 para los ejes X e Y de ambos joysticks. Estos valores hacen que sea mucho más fácil escribir lógica de modificador.
Después de llamar UpdateDirections()
, agregue cualquier lógica de manejo de modificadores que desee. Recuerde que UpdateDirections()
ya establece los valores predeterminados del joystick analógico, por lo que cuando maneje modificadores solo necesita configurar manualmente los valores para los ejes que realmente se están modificando. Aparte de esto, no puedo enseñarte cómo escribir tu lógica de modificador, así que simplemente mira los ejemplos y juega.
Finalmente, configure los valores de activación analógicos que necesite.
Nota: Las salidas de activación analógicas también podrían manejarse en UpdateDigitalOutputs()
, pero creo que generalmente parece más limpio mantenerlas junto con las otras salidas analógicas.
También tenga en cuenta: no necesita preocuparse por restablecer el estado de salida o borrar nada del mismo. Esto se hace automáticamente al comienzo de cada iteración.
En el constructor de cada modo (para modos de controlador y modos de teclado), puede configurar pares de entradas de direcciones opuestas para aplicar la limpieza SOCD.
Por ejemplo, en src/modes/Melee20Button.cpp
:
_socd_pair_count = 4;
_socd_pairs = new socd::SocdPair[_socd_pair_count]{
socd::SocdPair{&InputState::left, &InputState::right, socd_type},
socd::SocdPair{ &InputState::down, &InputState::up, socd_type},
socd::SocdPair{ &InputState::c_left, &InputState::c_right, socd_type},
socd::SocdPair{ &InputState::c_down, &InputState::c_up, socd_type},
};
Esto configura izquierda/derecha, abajo/arriba, C-Izquierda/C-Derecha y C-Abajo/C-Arriba como pares de direcciones cardinales opuestas para las cuales se aplicará la limpieza SOCD. La limpieza SOCD se realiza automáticamente antes de UpdateDigitalOutputs()
y UpdateAnalogOutputs()
, y no necesita preocuparse más que eso.
Para cada SocdPair
puede pasar un SocdType
de su elección. De forma predeterminada, en la mayoría de los modos, esto se pasa como un único parámetro de constructor, pero es posible pasar múltiples parámetros o simplemente usar un valor codificado. Ambos enfoques se ejemplifican en src/modes/FgcMode.cpp
.
SocdType | Descripción |
---|---|
SOCD_NEUTRAL | Izquierda + derecha = neutral: el valor predeterminado si no se especifica ningún SocdType en SocdPair |
SOCD_2IP | Segunda prioridad de entrada: izquierda -> izquierda + derecha = derecha y viceversa. Al soltar la segunda dirección se obtiene la dirección original. |
SOCD_2IP_NO_REAC | Segunda prioridad de entrada sin reactivación: igual que arriba, excepto que al liberar la segunda dirección se obtiene neutral. La dirección original debe reactivarse físicamente. |
SOCD_DIR1_PRIORITY | El primer botón del SocdPair siempre tiene prioridad sobre el segundo. |
SOCD_DIR2_PRIORITY | El segundo botón del SocdPair siempre tiene prioridad sobre el primero. |
SOCD_NONE | Sin resolución SOCD: el juego decide |
Tenga en cuenta que no es necesario implementar una función HandleSocd()
como en los modos Melee20Button y Melee18Button. Solo se anula en estos modos para que podamos verificar si la izquierda y la derecha se mantienen antes de la limpieza SOCD, porque cuando ambos se mantienen (sin que se mantenga una dirección vertical) necesitamos anular todos los modificadores.
Si su controlador no tiene botones de escudo de luz, es posible que desee usar Mod X para el escudo de luz y poner la inclinación del escudo en R en su lugar. Puedes hacer esto usando el modo Melee18Button en lugar de Melee20Button.
Los modos Melee20Button y Melee18Button permiten elegir qué coordenadas usar al presionar abajo + derecha. De forma predeterminada, mantener presionado + atrás te permitirá realizar cancelaciones automáticas de jab, lo cual es una técnica útil para algunos personajes.
Otra técnica popular que utiliza la diagonal abajo + derecha es la llamada opción de selección de agacharse/caminar. Esta técnica implica mantener presionado + hacia adelante en un cierto ángulo mientras estás agachado, de modo que después de agacharte y cancelar un ataque, automáticamente comenzarás a caminar hacia tu oponente en lugar de volver a agacharte. Esto puede ser muy útil para la persecución tecnológica, pero las coordenadas utilizadas para esta técnica no le permiten cancelar automáticamente el jab.
Esto se puede configurar como se ve en config/mode_selection.hpp
configurando la opción crouch_walk_os
en verdadero:
new Melee20Button(socd::SOCD_2IP_NO_REAC, { .crouch_walk_os = false })
También tendrás que cambiar esto en tu config/<environment>/config.cpp
para que se aplique en el complemento, ya que mode_selection.hpp
solo controla lo que sucede cuando cambias de modo.
El modo ProjectM tiene algunas opciones adicionales para configurar ciertos comportamientos. Como se ve en config/mode_selection.hpp
:
new ProjectM(
socd::SOCD_2IP_NO_REAC,
{ .true_z_press = false, .ledgedash_max_jump_traj = true }
)
En primer lugar, la opción ledgedash_max_jump_traj
te permite habilitar o deshabilitar el comportamiento tomado del modo cuerpo a cuerpo donde mantener presionado hacia la izquierda y hacia la derecha (y sin direcciones verticales) dará un cardinal 1.0 independientemente de los modificadores que se mantengan.
Si cambia el modo SOCD a 2IP (con reactivación), también debe cambiar esta opción a falso si desea una experiencia de juego fluida.
En segundo lugar, la opción true_z_press
existe porque Project M/Project+ no maneja las pulsaciones Z de la misma manera que lo hace Melee. Melee interpreta una pulsación Z como escudo de luz + A y, por lo tanto, puede usarse para cancelar L sin bloquearte de los técnicos. En PM/P+, presionar Z activará una tecnología y, por lo tanto, provocará bloqueos de tecnología no deseados si se usa para cancelar L. De forma predeterminada en HayBox, el modo ProjectM está configurado para usar una macro de lightshield + A para preservar el comportamiento esperado de Melee. Sin embargo, esta macro no le permite utilizar ataques de atadura/agarre ni agarrar objetos. Para solucionar este problema, puede presionar Mod X + Z para enviar una entrada Z verdadera.
Si esto le molesta y solo desea enviar una entrada Z verdadera de forma predeterminada al presionar Z, puede configurar la opción true_z_press
en verdadera.
HayBox admite varias fuentes de entrada desde las que se puede leer para actualizar el estado de entrada:
GpioButtonInput
: el más utilizado para leer interruptores/botones conectados directamente a pines GPIO. Las asignaciones de entrada están definidas por una serie de GpioButtonMapping
como se puede ver en casi todas las configuraciones existentes.SwitchMatrixInput
: similar al anterior, pero escanea una matriz de interruptores estilo teclado en lugar de interruptores individuales. Se incluye una configuración para el modelo C<=53 de Crane en config/c53/config.cpp
que sirve como ejemplo de cómo definir y utilizar una fuente de entrada de matriz de conmutación.NunchukInput
: lee entradas de un Wii Nunchuk usando i2c. Esto se puede usar para controladores de entrada mixta (por ejemplo, la mano izquierda usa un Nunchuk para el movimiento y la mano derecha usa botones para otros controles)GamecubeControllerInput
: similar al anterior, pero lee desde un controlador GameCube. Se puede crear una instancia de manera similar a GamecubeBackend. Actualmente solo está implementado para Pico, y debes ejecutarlo en una instancia de pio diferente (pio0 o pio1) que cualquier instancia de GamecubeBackend, o asegurarte de que ambas usen el mismo desplazamiento de memoria de instrucciones PIO. Cada fuente de entrada tiene un valor de "velocidad de escaneo" que indica aproximadamente cuánto tiempo le toma leer las entradas. Las fuentes de entrada rápida siempre se leen en el último momento posible (al menos en Pico), lo que da como resultado una latencia muy baja. Por el contrario, las fuentes de entrada lentas suelen leerse mucho antes de ser necesarias, ya que son demasiado lentas para leerse en respuesta a una encuesta. Debido a esto, es más ideal leer constantemente esas entradas en un núcleo separado. Esto no es posible en las MCU AVR ya que todas son de un solo núcleo, pero es posible (y fácil) en el Pico/RP2040. La parte inferior de la configuración predeterminada de Pico config config/pico/config.cpp
ilustra esto usando core1 para leer las entradas de Nunchuk mientras core0 maneja todo lo demás. Consulte la siguiente sección para obtener más información sobre el uso de core1.
En la función setup()
de cada configuración, creamos una matriz de fuentes de entrada y luego la pasamos a un backend de comunicación. El backend de comunicación decide cuándo leer qué fuentes de entrada, porque las entradas deben leerse en diferentes momentos para diferentes backends. También creamos una variedad de backends de comunicación, lo que permite utilizar más de un backend a la vez. Por ejemplo, en la mayoría de las configuraciones, el backend del visor de entrada B0XX se usa como backend secundario siempre que se usa el backend DInput. En cada iteración, el bucle principal le dice a cada uno de los backends que envíe sus respectivos informes. En el futuro, podría haber más backends para cosas como escribir información en una pantalla OLED.
En cada configuración, existen las funciones setup()
y loop()
, donde setup()
se ejecuta primero y luego loop()
se ejecuta repetidamente hasta que se apaga el dispositivo.
En Pico/RP2040, las funciones setup()
y loop()
se ejecutan en core0, y puede agregar las funciones setup1()
y loop1()
para ejecutar tareas en core1.
Por ejemplo, para leer las entradas del controlador GameCube en core1:
GamecubeControllerInput *gcc = nullptr;
void setup1() {
while (backends == nullptr) {
tight_loop_contents();
}
gcc = new GamecubeControllerInput(gcc_pin, 2500, pio1);
}
void loop1() {
if (backends != nullptr) {
gcc->UpdateInputs(backends[0]->GetInputs());
}
}
El bucle while
asegura que esperemos hasta que setup()
en core0 haya terminado de configurar los servidores de comunicación. Luego creamos una fuente de entrada del controlador GameCube con una tasa de sondeo de 2500 Hz. También lo ejecutamos en pio1
como una manera fácil de evitar interferir con cualquier backend de GameCube/N64, que usa pio0
a menos que se especifique lo contrario. En loop1()
asumimos que el backend principal es el primer elemento de la matriz backends
(que de todos modos está configurado en el mismo archivo, por lo que realmente no asumimos nada que no sepamos) y escaneamos directamente el controlador de GameCube. entradas en el estado de entrada del backend.
Como ejemplo hipotético un poco más loco, uno podría incluso alimentar todos los controles de una máquina recreativa para dos personas usando un solo Pico creando dos fuentes de entrada de matriz de conmutación usando, digamos, 10 pines cada una, y dos backends de GameCube, ambos en núcleos separados. Las posibilidades son infinitas.
Si está utilizando un adaptador oficial con un controlador basado en Arduino, probablemente tendrá que mantener presionado A en el complemento que deshabilita la optimización de la latencia de sondeo al pasar una tasa de sondeo de 0 al constructor de GamecubeBackend.
Si está utilizando un controlador basado en Arduino sin un circuito de refuerzo, necesitará alimentación de 5 V, por lo que para el adaptador Mayflash necesitará ambos cables USB conectados y, en la consola, la línea sonora debe estar intacta. Pico funciona de forma nativa con una alimentación de 3,3 V, por lo que esto no es un problema.
Acepto las contribuciones y si crea un modo de entrada que desea compartir, no dude en realizar una solicitud de extracción. Instale el complemento clang-format para VS Code y utilícelo para formatear cualquier código que desee agregar.
Usamos SemVer para el control de versiones. Para conocer las versiones disponibles, consulte las etiquetas en este repositorio.
Vea también la lista de contribuyentes que participaron en este proyecto.
Este proyecto tiene la licencia GNU GPL Versión 3; consulte el archivo de LICENCIA para obtener más detalles.