Fonte de inspiração
Nas exposições de jogos, os desenvolvedores do
Objects in Space mostraram uma demonstração de seu jogo com um controlador no cockpit de uma enorme nave espacial. Foi complementado por botões iluminados, dispositivos analógicos, indicadores de status, interruptores, etc. ... Isso afeta muito a imersão no jogo:
Um
tutorial do Arduino foi publicado no site do jogo com uma descrição do
protocolo de comunicação para esses controladores.
Eu quero criar o mesmo para o meu jogoNeste exemplo, gastarei cerca de US $ 40 para adicionar switches bonitos, grandes e pesados ao cockpit de um simulador de corrida. Os principais custos estão associados a essas opções - se eu usasse opções / botões simples, o preço seria metade do valor! Este é um equipamento real que pode suportar 240 watts de potência, e eu os deixarei absorver apenas 0,03 watts.
Aviso: decidi economizar dinheiro, por isso deixo um link para um site chinês barato, onde compro vários componentes / ferramentas diferentes. Uma das desvantagens de comprar componentes baratos é que eles geralmente não possuem documentação, portanto, neste artigo, resolverei esse problema.
Componentes principais
Ferramentas em destaque
De software
- Arduino IDE para programar o processador Arduino
- Para criar um controlador que aparece como um controlador / joystick USB de hardware real:
- FLIP para piscar novo firmware no controlador USB do Arduino
- Biblioteca Arduino-usb no github
- Para criar um controlador com o qual o jogo se comunica diretamente ( ou que apareça como um controlador / joystick USB virtual )
- Minha biblioteca ois_protocol no github
- Driver VJoy, se você quiser usar o controlador como um controlador / joystick USB virtual.
Advertência
Estudei eletrônica no ensino médio, aprendi a usar um ferro de soldar, aprendi que os fios vermelhos precisam ser conectados ao vermelho e do preto ao preto ... Volts, amperes, resistência e as equações que os conectam - isso foi tudo que esgotou meu treinamento formal em eletrônica.
Para mim, foi um projeto de treinamento, por isso pode ter maus conselhos ou erros!
Parte 1. Montando o Controlador!
Trabalhamos com switches sem documentação ...
Como mencionado acima, compro peças baratas de um revendedor de baixa margem; portanto, a primeira coisa a fazer é descobrir como esses interruptores / botões funcionam.
Botão simples / interruptor
Com o botão, tudo é simples - não há LEDs e apenas dois contatos. Coloque o multímetro no modo de continuidade / discagem (

) e toque nas sondas de contatos diferentes - OL (circuito aberto, circuito aberto) será exibido na tela: isso significa que não há conexão entre as duas sondas. Em seguida, pressionamos o botão, ainda tocando as sondas de contato - algo como 0,1Ω agora deve ser exibido na tela e o multímetro emitirá um bipe (
informando que há uma resistência muito baixa entre as sondas - um circuito fechado ).
Agora sabemos que, quando o botão é pressionado, o circuito fecha e, quando pressionado, ele se abre. No diagrama, isso pode ser descrito como uma opção simples:

Conectamos o switch ao Arduino
Encontre dois pinos na placa do Arduino: GND e “2” (ou qualquer outro número arbitrário - esses são pinos de E / S de uso geral que podemos controlar através do software).
Se conectarmos o comutador dessa maneira e solicitarmos ao Arduino que configure o pino “2” como o pino de ENTRADA, obteremos o circuito mostrado à esquerda (na figura abaixo). Quando o botão é pressionado, o pino 2 será conectado diretamente ao terra / 0V e, quando pressionado, o pino 2 não será conectado a nada. Esse estado (
não conectado a nada ) é chamado de "flutuante" (estado de alta impedância) e, infelizmente, não é uma condição muito boa para nossos propósitos. Quando lemos dados de um contato no software (
usando digitalRead (2) ), obtemos BAIXO se o contato estiver aterrado e um resultado imprevisível (BAIXO ou ALTO) se o contato estiver flutuando!
Para corrigir isso, podemos configurar o contato para estar no modo INPUT_PULLUP, que se conecta ao resistor dentro do processador e cria o circuito mostrado à direita. Neste circuito, com o interruptor aberto, o pino 2 tem um caminho de + 5V, portanto, quando for lido, o resultado será sempre ALTO. Quando o comutador estiver fechado, o contato ainda terá um caminho com alta resistência a + 5V, assim como um caminho sem resistência ao terra / 0V, que “vence”, portanto, quando lemos o contato, obtemos um BAIXO.
Para desenvolvedores de software, a ordem pode parecer invertida - quando clicamos no botão, lemos false / LOW e, quando pressionados, lemos true / HIGH.
Você pode fazer o oposto, mas o processador possui apenas resistores de pull-up integrados e não há resistores de pull-down, portanto, manteremos esse modelo.
O programa mais simples para o Arduino, que lê o status do comutador e informa ao PC sobre seu status, se parece com o mostrado abaixo. Você pode clicar no botão de download no IDE do Arduino e abrir o Serial Monitor (no menu Ferramentas) para ver os resultados.
void setup() { Serial.begin(9600); pinMode(2, INPUT_PULLUP); } void loop() { int state = digitalRead(pin); Serial.println( state == HIGH ? "Released" : "Pressed" ); delay(100);
Outros switches quase sem documentação ...
Interruptor LED de três pinos
Felizmente, nos principais comutadores do meu painel, existem marcas de três contatos:
Não tenho certeza de como ele funciona, portanto, voltaremos ao multímetro para o modo contínuo e tocaremos em todos os pares de contatos quando o interruptor for ligado e desligado ... no entanto, desta vez, o multímetro não emitirá um bipe quando tocarmos nas sondas [GND] e [+] com " ligue! A única configuração na qual o multímetro emite
um bipe (
detecta uma conexão ) é quando o interruptor está “ligado” e as pontas de prova estão acesas [+] e [lâmpada].
O LED dentro do comutador bloqueia as medições de continuidade; portanto, a partir dos testes acima, podemos assumir que o LED está conectado diretamente ao pino [GND] e não aos contatos [+] e [lamp]. Em seguida, mudaremos o multímetro para o modo de teste de diodo (símbolo

) e verifique o par de contatos novamente, mas desta vez a polaridade é importante (
sonda vermelha e preta ). Agora, se conectarmos a sonda vermelha a [lamp] e a preta a [GND], o LED acenderá e 2,25 V serão exibidos no multímetro. Esta é a tensão direta do diodo ou a tensão mínima necessária para ligá-lo. Independentemente da posição do interruptor, 2,25 V de [lâmpada] a [GND] acende o LED. Se conectarmos a sonda vermelha a [+] e a preta a [GND], o LED acenderá apenas quando a chave estiver ligada.
A partir dessas leituras, podemos assumir que o interior dessa opção se parece com o diagrama abaixo:
- [+] e [lâmpada] ficam em curto-circuito quando o interruptor está ligado / fechado.
- Uma voltagem positiva de [lâmpada] a [GND] sempre acende o LED.
- Uma tensão positiva de [+] a [GND] acende o LED somente quando o interruptor está ligado / fechado.
Honestamente, só podemos adivinhar a presença de um resistor. O LED
deve estar conectado ao resistor apropriado para limitar a corrente fornecida, ou queimará. O meu não se esgotou e parece que está funcionando corretamente. No fórum do site do vendedor, eu encontrei um post que fala sobre um resistor instalado suportando até 12 V, e isso me poupou tempo na verificação / cálculo de um resistor adequado.
Conectamos o switch ao Arduino
A maneira mais fácil é usar o comutador com o Arduino, ignorando o pino [lamp]: conecte [GND] ao GND no Arduino e conecte [+] a um dos contatos numerados do Arduino, por exemplo 3.
Se configurarmos o pino 3 como INPUT_PULLUP (
igual ao botão anterior ), obteremos o resultado mostrado abaixo. O canto superior esquerdo mostra o valor que receberemos executando “digitalRead (3)” no código do Arduino.
Quando o interruptor está ligado / fechado, lemos LOW e o LED acende! Para usar esse comutador nessa configuração, podemos usar o mesmo código do Arduino que no exemplo do botão.
Problemas desta solução
Após conectar-se ao Arduino, o circuito completo fica assim:
No entanto, aqui podemos ver que quando o interruptor é fechado, além do pequeno resistor limitador de corrente na frente do LED (suponho que sua resistência seja 100 Ohms), também há um resistor de pull up de 20 kOhm, o que reduz ainda mais a quantidade de corrente que flui através do LED. Isso significa que, embora o circuito funcione, o LED não ficará muito claro.
Outra desvantagem desse esquema é que não temos controle de software sobre o LED - ele é ativado quando o comutador está ligado e desativado no caso oposto.
Você pode ver o que acontece se conectarmos o pino [lâmpada] a 0V ou + 5V.
Se a [lâmpada] estiver conectada a 0V, o LED estará constantemente apagado (
independentemente da posição do interruptor ) e o reconhecimento da posição do Arduino ainda será realizado. Isso nos permite desativar programaticamente o LED!
Se a [lâmpada] estiver conectada a + 5V, o LED estará constantemente aceso (
independentemente da posição da chave ),
no entanto, o reconhecimento da posição do Arduino será interrompido - HIGH sempre será lido a partir do contato.
Conectamos esse switch ao Arduino corretamente
Podemos superar as limitações descritas acima (
baixa corrente / brilho do LED e a falta de controle do programa sobre o LED ) escrevendo mais código! Para resolver o conflito entre a capacidade de controlar o LED e o reconhecimento de posição que foi quebrado devido a ele, podemos separar as duas tarefas no tempo, ou seja, desligar temporariamente o LED ao ler o contato do sensor (3).
Primeiro, conecte o pino [lâmpada] a outro pino Arduino de uso geral, por exemplo, a 4 para que você possa controlar a lâmpada.
Para criar um programa que leia corretamente a posição do comutador e controle o LED (faremos piscar), basta desligar o LED antes de ler o status do comutador. O LED se apaga apenas uma fração de milissegundo, portanto o tremulação não deve ser perceptível:
int pinSwitch = 3; int pinLed = 4; void setup() {
No Arduino Mega, os pinos 2-13 e 44-46 podem usar a função analogWrite, que na verdade não gera tensão de 0V a + 5V, mas a aproxima usando uma onda quadrada. Se desejar, você pode usá-lo para controlar o brilho do LED! Este código fará a luz pulsar, não apenas piscar:
void loop() { int lampState = (millis()>>1)&0xFF;
Dicas de montagem
A postagem já é muito grande, então não adicionarei o tutorial de solda, você pode pesquisar no Google!
No entanto, darei as dicas mais básicas:
- Ao conectar fios com contatos de metal grandes, primeiro verifique se o ferro de solda está quente e aqueça o contato de metal por um tempo. O significado da solda é formar uma junta permanente criando uma liga, mas se apenas uma parte da junta estiver quente, é possível obter facilmente uma “junta fria” que se parece com uma junta, mas na verdade não está conectada.
- Ao conectar os dois fios, primeiro coloque um deles em um tubo termo-retrátil - após a conexão, o tubo não pode ser colocado. Isso parece óbvio, mas sempre o esqueço e preciso usar fita isolante em vez do tubo ... Puxe o tubo de contração da conexão para que não aqueça antes do tempo. Depois de verificar a conexão soldada, deslize o tubo e aqueça-o.
- Os pequenos e finos fios de conexão que mencionei no início são adequados para conexões sem solda (por exemplo, quando conectados a um Arduino!), Mas bastante frágeis. Após a solda, use uma pistola de cola para corrigi-los e remover todas as tensões da própria conexão. Por exemplo, os fios vermelhos na figura abaixo podem ser puxados acidentalmente durante a operação; portanto, após a solda, fixei-os com uma gota de cola quente:
Parte 2. Transformamos o dispositivo em um controlador de jogo!
Para que o sistema operacional reconheça o dispositivo como um controlador de jogos USB, você precisa de um código bastante simples, mas, infelizmente, também precisa substituir o firmware do chip USB do Arduino por outro, que pode ser obtido aqui:
https://github.com/harlequin-tech/arduino-usb .
Porém, após o upload deste firmware para o Arduino, o dispositivo se torna um joystick USB e deixa de ser Arduino. Portanto, para reprogramá-lo, é necessário atualizar novamente o firmware original do Arduino. Essas iterações são bastante dolorosas - carregue o código do Arduino, atualize o firmware do joystick, teste, atualize o firmware do arduino, repita ...
Um exemplo de programa para o Arduino que pode ser usado com este firmware é mostrado abaixo - ele configura os três botões como entradas, lê seus valores, copia os valores para a estrutura de dados esperada por esse firmware e envia os dados. Lave, sabonete, repita.
Parte 3. Nós integramos o dispositivo ao nosso próprio jogo!
Se você tem controle sobre o jogo com o qual o dispositivo deve interagir, como alternativa, você pode se comunicar diretamente com o controlador - não há necessidade de torná-lo visível para o sistema operacional como um joystick! No começo do post, mencionei Objects In Space; essa é a abordagem usada pelos desenvolvedores. Eles criaram um protocolo de comunicação ASCII simples que permite que o controlador e o jogo se comuniquem. Simplesmente liste as portas seriais do sistema (
são portas COM no Windows; a propósito, veja o quão horrível é em C ), encontre a porta à qual o dispositivo chamado “Arduino” está conectado e comece a ler / gravar ASCII nesse link.
No lado do Arduino, usamos apenas as funções Serial.print que foram usadas nos exemplos acima.
No início deste post, também mencionei minha biblioteca para resolver esse problema:
https://github.com/hodgman/ois_protocol .
Ele contém código C ++ que pode ser integrado ao jogo e usado como um "servidor" e código Arduino que pode ser executado no controlador para usá-lo como um "cliente".
Personalizar Arduino
Em
example_hardware.h, criei classes para abstrair botões / botões de opção individuais; por exemplo, “Switch” é um botão simples do primeiro exemplo. e “LedSwitch2Pin” é um switch com um LED controlado do segundo exemplo.
O código de exemplo para minha barra de botões está em
example.ino .
Como um pequeno exemplo, digamos que temos um único botão que precisa ser enviado ao jogo e um LED controlado pelo jogo. O código necessário do Arduino se parece com o seguinte:
#include "ois_protocol.h"
Personalize o jogo
O código do jogo é escrito no estilo de "cabeçalho único". Para importar a biblioteca, inclua
oisdevice.h no jogo.
No arquivo CPP único, antes de executar o cabeçalho #include, escreva #define OIS_DEVICE_IMPL e #define OIS_SERIALPORT_IMPL - isso adicionará o código-fonte das classes ao arquivo CPP. Se você possui suas próprias instruções, log, seqüências de caracteres ou vetores, existem várias outras macros OIS_ * que você pode definir antes de importar o cabeçalho para aproveitar os recursos do mecanismo.
Para listar portas COM e criar uma conexão com um dispositivo específico, você pode usar o seguinte código:
OIS_PORT_LIST portList; OIS_STRING_BUILDER sb; SerialPort::EnumerateSerialPorts(portList, sb, -1); for( auto it = portList.begin(); it != portList.end(); ++it ) { std::string label = it->name + '(' + it->path + ')'; if( ) { int gameVersion = 1; OisDevice* device = new OisDevice(it->id, it->path, it->name, gameVersion, "Game Title"); ... } }
Após receber uma instância do OisDevice, você precisa chamar regularmente sua função de membro Poll (por exemplo, em cada quadro), pode obter o estado atual da saída do controlador usando DeviceOutputs (), usar eventos do dispositivo usando PopEvents () e enviar valores para o dispositivo usando SetInput ().
Um exemplo de aplicativo que faz tudo isso pode ser encontrado aqui:
example_ois2vjoy / main.cpp .
Parte 4. E se eu quiser as partes 2 e 3 ao mesmo tempo?
Para que o controlador funcione em outros jogos (parte 2), você precisa instalar seu próprio firmware e um programa Arduino, mas para que o controlador seja totalmente programado pelo jogo, usamos o firmware padrão do Arduino e outro programa do Arduino. Mas e se quisermos ter as duas possibilidades ao mesmo tempo?
O aplicativo de amostra ao qual
forneci o link acima (
ois2vjoy ) resolve esse problema.
Esse aplicativo se comunica com o dispositivo OIS (o programa da Parte 3) e, em seguida, no PC converte esses dados em dados regulares do controlador / joystick, que são transferidos para o dispositivo do controlador
virtual / joystick. Isso significa que você pode permitir que seu controlador use constantemente a biblioteca OIS (nenhum outro firmware é necessário) e, se quisermos usá-lo como um controlador / joystick comum, basta executar o aplicativo ois2vjoy no PC, que realiza a conversão.
Parte 5. Conclusão
Espero que alguém tenha achado este artigo útil ou interessante. Obrigado por ler até o fim!
Se você estiver curioso, convido você a participar do desenvolvimento da biblioteca
ois_protocol ! Eu acho que será ótimo desenvolver um protocolo único para suportar todos os tipos de controladores caseiros em jogos e incentivar os jogos a apoiar diretamente controladores caseiros!