Controle USB com Arduino

Intro

Uma característica dos novos Arduinos é a possibilidade destes serem facilmente programados como dispositivos USB. Isso deve-se aos Modelos serem baseados em um chip com USB nativo. A possibilidade de se programar o Arduino como um dispositivo USB permite que o mesmo funcione como teclado, mouse, controle, controladores midi ou outros dispositivos dentro da especificação USB.

Para esse tutorial, será utilizado o Arduino Leonardo por ser possível conectar-se um shield neste. O Arduino micro também pode ser utilizado como um dispositivo USB HID, inclusive eu utilizei ele antes pra converter controles de SNES pra USB, porém shields não podem ser facilmente conectados nesse.

O Arduino Leonrado possui USB nativa.
O Arduino Leonrado possui USB nativa.

USB HID

HID é uma sigla para human interface device. A vantagem de se programar um dispositivo HID é que todos os sistemas geralmente já possuem os drivers pra esses dispositivos, não sendo necessário programar ou instalar drivers novos.

Esse tutorial não funciona nos outros Arduinos(Uno, nano, mega), pois esses são reconhecidos como dispositivos de conversão USB-serial. Os Arduinos com atmega8u2 ou Atmega16u2 até poderiam ser programados pra usar HID, mas seria outro processo e está  fora do escopo desse artigo.

Joystick Shield

O shield joystick será utilizado, mas qualquer forma de input pode ser utilizada. Seja push-buttons, potenciômetros, teclados matriciais ou mesmo controles de video games antigos. Basta ajustar o código para isso.

Uma imagem do shield Joystick
Joystick Shield

Montagem

Como um shield foi utilizado neste projeto, não houve a necessidade de conectar mais nada.

Arduino Leonardo + Joystick Shield
Arduino Leonardo + Joystick Shield

Código

Nesta seção estudaremos o código parte a parte. Primeiro é necessário instalar a biblioteca Joystick no seu ambiente Arduino.  Esta biblioteca foi escrita por Matthew Heironimus e está disponível em seu github.

Primeiro incluimos a biblioteca Joystick e fazemos a declaração de um objeto Joystick com algumas alterações.

#include "Joystick.h"

//From the examples, creates a customised controller
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_GAMEPAD,
  6, 0,                  // Button Count, Hat Switch Count
  true, true, false,     // X and Y, but no Z Axis
  false, false, false,   // No Rx, Ry, or Rz
  false, false,          // No rudder or throttle
  false, false, false);  // No accelerator, brake, or steering

Os vários Argumentos acima são necessários para definir um controle com apenas 6 botões e eixos X e Y. Caso o objeto fosse definido de forma padrão, os outros eixos iriam aparecer no computador com valores mínimos, impedindo a configuração do controle em jogos e emuladores.

A seguir, as constantes para os pinos onde estão conectados cada botão e eixo foram definidas.

//Digital
const int Button_A=2;
const int Button_B=3;
const int Button_C=4;
const int Button_D=5;
const int Button_E=6;
const int Button_F=7;
//Analog
const int Axis_X=0;
const int Axis_Y=1;

O analógio é conectado a pinos… analógicos, claro. Cada eixo está conectado a um pino. O analógico divide 5V, que são a tensão de funcionamento do Arduino. Por exemplo, quando o analógico está no centro, a tensão no pino está em torno de 2,5V ou um valor lido de 512 com analogRead().

Na função Setup(), todos os pinos estão definidos como entrada Pull-up e a biblioteca Joystick é inicializada.

void setup() {
  pinMode(Button_A,INPUT_PULLUP);
  pinMode(Button_B,INPUT_PULLUP);
  pinMode(Button_C,INPUT_PULLUP);
  pinMode(Button_D,INPUT_PULLUP);
  pinMode(Button_E,INPUT_PULLUP);
  pinMode(Button_F,INPUT_PULLUP);
  
  Joystick.begin(false); //autosend off
  Joystick.setXAxisRange(-127, 127);
  Joystick.setYAxisRange(-127, 127);
}

A entrada é configurada como pull-up porque do contrário os pinos ficariam flutuando (valores indeterminados). O pull-up garante que o pino tenha um valor fixo (5V) quando o botão não está sendo pressionado.

A biblioteca é inicializada com um argumento false para impedir que o Arduino envie os dados do controle automaticamente. Assim é preciso dizer quando os dados devem ser enviados manualmente, uma vantagem desse método seria enviar os dados apenas quando o estado dos botões ou joystick mudam, mas para manter a simplicidade do código, os dados são enviados toda iteração do loop().

Por falar no loop(), abaixo um trecho de código do mesmo.

int Value_X=0,Value_Y=0;
void loop() {
  Value_X=analogRead(Axis_X)/4-127;
  Value_Y=-analogRead(Axis_Y)/4+127; //Value_Y inverted :/
  Joystick.setXAxis(Value_X);
  Joystick.setYAxis(Value_Y);

Aqui os valores do direcional analógico são lidos e convertidos pra um range de 255 valores (8 bit). Lembre-se que no analógico o centro equivale a 0, mas o centro do range é 127, por isso a subtração. Por alguma falha no design do shield, o eixo Y foi invertido, isso foi remediado com as mudanças no sinal(matemático) do valor lido. Repare Joystick.setXAxis(Value_X) sendo usado para confirmar o valor do eixo ao objeto Joystick.

Feito isso basta apenas ler os estados dos botões e enviar o status do controle ao computador:

  if(digitalRead(Button_A)==LOW){
    Joystick.setButton(0, 1);  
  }else{
    Joystick.setButton(0, 0);  
  }

  ...

  if(digitalRead(Button_F)==LOW){
    Joystick.setButton(5, 1);  
  }else{
    Joystick.setButton(5, 0);  
  }
  
  Joystick.sendState();//Send packet to computer
  delay(10); 
}

Repare que por causa do Pull-up, um botao pressionado possui estado 0 (ou LOW), mas para a biblioteca um botão pressionado possui estado 1. Conforme descrito anteriormente, o estado do controle é enviado manualmente através da funcão Joystick.sendState(); Após isso é feito um delay de 10ms até a próxima leitura.

Conclusões

O controle funcionou perfeitamente. Não há necessidade de se utilizar nenhuma ferramente externa. Os jogos e emuladores o reconhecem como um controle HID normal. O vídeo abaixo mostra o controle funcionando.

 

O código completo está disponível aqui.

Por enquanto é isso mesmo. Obrigado pela leitura e até o próximo artigo!

Ah, não esquece de curtir a página se quiser ficar por dentro dos nossos posts :)

Robson Couto

Recentemente graduado Engenheiro Eletricista. Gosto de dedicar meu tempo a aprender sobre computadores, eletrônica, programação e engenharia reversa. Documento meus projetos no Dragão quando possível.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *