quinta-feira, 29 de dezembro de 2016

Conectando o controle sem fio do XBox360 no PC ou Raspberry Pi

Obviamente se você veio até aqui, é porque não quer comprar o receptor original do controle sem fio XBox360, está procurando uma solução mais "alternativa", primeiramente pelo preço absurdo de um hardware simples, segundo pela dificuldade de achar, e terceiro que na maioria das vezes você não quer mais um controle que acompanha o receptor, quer apenas usar o controle que já tem.

Infelizmente a MS ao contrário da Sony, não desenvolveu os controles sem fio baseado no protocolo bluetooth, que inviabiliza tentar qualquer "gambiarra" igual é feito com os controles do PS3 e PS4, a única coisa que é possível fazer é usar a própria placa de RF do XBox360, mais especificamente a placa que fica na parte frontal, que tem o botão de liga desliga e os leds. Como as primeiras versões do XBox360 deram problema "a rodo" com o famoso 3RL, tem dessas plaquinhas aos montes por ai, com certeza se você for a alguma assistência técnica de video game, você encontrará facilmente ela, pois é justamente o módulo de RF do XBox360 FAT que iremos usar nesse projeto, eu particularmente encontrei essa placa no mercado livre por uma média de R$ 10,00 bem mais em conta do que gastar R$ 70,00 no receptor original da Microsoft.

Essa placa na verdade usa uma conexão USB para transmitir os dados do controle, a única diferença é que ela trabalha com 3.3V ao invés dos 5V de uma USB comum. Isso é facilmente corrigido usando um CI regulador de tensão 3.3V, ou um divisor de tensão usando resistores ou apenas 2 diodos comuns (1N4007) ligados em série. Porém o maior problema é que usando somente a USB, não é possível dar o comando de sincronismo entre a base e o controle para poder parear ambos. Esse comando de sincronismo é um código binário enviado em um outro pino na placa, e para enviar esse código sem o XBox360 é necessário um microcontrolador, nesse caso, o mais fácil seria usar um Arduíno.

Para resumir, temos 3 cenários:

1- Usar um XBox360 Fat funcional e puxar os 4 fios da USB da placa RF dele. É uma opção válida, você usa o próprio XBox360 para parear o controle, e como ele é funcional, você usa ele normalmente, quando quiser usar o modulo dele no seu PC, você teria que desligar ele da tomada, e conectar o cabo USB no computador. Como o controle já está pareado na placa, não é necessário mais nada para funcionar no PC

2- Usar uma placa de Arduíno temporariamente. Como é inviável usar uma placa de Arduíno exclusivamente somente para sincronizar o controle (por questão de tamanho e preço), é possível conectar uma placa de Arduíno somente para sincronizar o controle. Após parear o controle com a base, o Arduíno não é mais necessário, a não ser que você pareie esse controle em outro aparelho.

3- Utilizar um pequeno microcontrolador nesse caso o Attiny85 que é programado usando uma placa de Arduíno. Esta opção é deixar fixo um Attiny, que é um microcontrolador pequeno e barato para enviar o comando de sincronismo, a vantagem desta opção é ter sempre disponível a possibilidade de sincronizar qualquer controle, sem precisar de um XBox360.

Clique na imagem para ampliar

Basicamente vamos usar os 7 primeiros pinos do conector da placa RF. Os primeiros 4 primeiros pinos, olhando a placa de frente, e contando a partir do primeiro pino superior esquerdo, temos a conexão USB, com o diferencial de que a placa trabalha com 3.3V, como a USB trabalha com 5V vai ser necessário usar algo para baixar para 3.3V, nesse caso, foi usado 2 diodos de uso geral, ligados em série, a resistência interna somada dos diodos foi suficiente para baixar a tensão de 5 para 3.2V. Se você quer algo mais preciso, pode sem problema usar um CI regulador de tensão de 3.3V. Se você vai usar o modulo em conjunto com um XBox funcional, basta usar esses ligados na USB e pronto.

Os próximos 3 pinos, que seria a segunda fileira da esquerda pra direita, seria o pino do botão Power central, que será usado como entrada para dar o comando de sincronismo, o DATA e o CLOCK. Esses vão ser ligados ao Arduíno ou ao Attiny, para ser possível parear o controle sem ter um XBox360. Como deu pra ver, é bem simples, basta ligar 3 fios do modulo RF nas portas do Arduíno, como mostra a figura acima e programar o Arduíno com o seguinte código:

/* Arduino code to communicate with xbox 360 RF module.
 * Original work by (yaywoop) / additional ideas from Alexander Martinez - modified by dilandou (www.dilandou.com, www.diru.org/wordpress)
 * First sends LED initialisation code followed by LED startup animation code, then sleeps until a button press for sync command.
 * RF module must be powered with 3.3V, two diodes in series with USB 5v will do. Connect the USB wires to a host computer, and the data and serial wires to Arduino.
 * of course, make sure to have a common ground 
 */

#include

const int sync_pin = 2; //power button repurposed for sync button (pin 5 on the module)
const int data_pin = 3; //data line (pin 6 on the module)
const int clock_pin = 4; //clock line (pin 7 on module) 

int led_cmd[10] =  {0,0,1,0,0,0,0,1,0,0}; //Activates/initialises the LEDs, leaving the center LED lit.
int anim_cmd[10] = {0,0,1,0,0,0,0,1,0,1}; //Makes the startup animation on the ring of light.
int sync_cmd[10] = {0,0,0,0,0,0,0,1,0,0}; //Initiates the sync process.
volatile boolean sync_enable = 0;

void sendData(int cmd_do[]) {
  pinMode(data_pin, OUTPUT);
  digitalWrite(data_pin, LOW);    //start sending data.
  int prev = 1;
  for(int i = 0; i < 10; i++){

    while (prev == digitalRead(clock_pin)){} //detects change in clock
    prev = digitalRead(clock_pin);
      // should be after downward edge of clock, so send bit of data now
    digitalWrite(data_pin, cmd_do[i]);

    while (prev == digitalRead(clock_pin)){} //detects upward edge of clock
    prev = digitalRead(clock_pin);
  }
  digitalWrite(data_pin, HIGH);
  pinMode(data_pin, INPUT);
}

void initLEDs(){
  sendData(led_cmd);
  delay(50);
  sendData(anim_cmd);
  delay(50);
}

void wakeUp(){
  sync_enable = 1;
}

void sleepNow() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode
  sleep_enable(); //enable sleep bit
  attachInterrupt(0, wakeUp, LOW);
  sleep_mode();
  sleep_disable(); //disable sleep bit
  detachInterrupt(0); // disables interrupt 0 on pin 2
}

void setup() {
  Serial.begin(9600);
  pinMode(sync_pin, INPUT);
  digitalWrite(sync_pin,HIGH);
  pinMode(data_pin, INPUT);
  pinMode(clock_pin, INPUT);
  delay(2000);
  initLEDs();
}

void loop(){
  Serial.println("Sleeping.");
  sleepNow();
  delay(200);
  if(sync_enable==1) {
    Serial.println("Syncing.");
    sendData(sync_cmd);
    sync_enable = 0;
  }
}

Feito o upload do programa no Arduíno, agora é hora de ligar o módulo RF no computador, ligue primeiro o modulo RF e somente depois ligue o Arduíno, o LED central deve ficar aceso, se não ficar, algo deu errado com a comunicação do Arduíno, se você checou tudo e ainda assim o led não acende, no final do post eu tenho um macete que pode ajudar a fazer funcionar, lembrando que o Arduíno só é necessário para parear o controle, mesmo com todos os leds apagados, uma vez pareado o controle, ele funciona normal. 

Ao conectar na USB, ele irá reconhecer (se não reconhecer ou ficar conectando e desconectando intermitentemente, tem algo errado), mas ao abrir o gerenciador de dispositivos, o windows vai mostrar que falta driver. O Driver que vamos usar é do próprio receptor wireless para controle XBox360, sendo assim entre na página da MS e baixe o driver conforme a versão do seu Windows:


Depois de baixar e instalar o driver, ao entrar no gerenciador de dispositivos, você vai ver que mesmo assim o receptor não está instalado, ele provavelmente vai estar como "dispositivo desconhecido" isso é devido que esse driver não foi feito específico para essa placa. Sendo assim vamos ter que forçar a instalação do driver neste dispositivo, o driver que você deverá instalar forçado está em:

C:\Program Files\Microsoft Xbox 360 Accessories

Se você não sabe como instalar um driver forçado, acompanhe esse video, ele usa o "cenário 1", sem a necessidade de ter um Arduíno, mas funciona em todos os casos:


Após o driver instalado e funcional, agora sim é hora de sincronizar o controle (se ele ainda não foi pareado), se o led central estiver aceso, basta apertar o pequeno botão da placa que seria o botão "Power" do XBox360 que os leds vão começa a rodar, dai você já sabe, basta apertar o botão sync do controle também, e pronto ! Para testar o controle, mande executar (Windows + R): joy.cpl que se tudo estiver Ok, ele irá aparecer lá.

Agora se mesmo depois de ter checado tudo e mesmo assim o LED não quer ligar, existe um macete que pode te ajudar. Alguns módulos de RF necessitam de 2 resistores Pull UP nos pinos DATA e CLOCK, se você não sabe o que é isso, não tem problema, basta ligar 2 resistores de 10K um no pino DATA e outro no pino CLOCK (pinos 6 e 7 do modulo RF), ambos os resistores ligados no +5V da USB, se esse for o seu caso (foi o meu caso ...), vai funcionar na hora.


Essa foi a plaquinha que eu montei, no meu caso eu usei um Attiny 85 permanente colado na placa. Repare que eu usei um pedaço de cabo flat que eu cortei de um cabo de HDD sata, e como eu não tinha a mão um CI regulador de 3.3V, acabei usando 2 diodos em série mesmo. Caso você também queira usar um Attiny para fazer o sincronismo eu aconselho você usar outro código:

/* Arduino code to communicate with xbox 360 RF module.
 * Original work by (yaywoop) / additional ideas from Alexander Martinez - modified by dilandou (www.dilandou.com, www.diru.org/wordpress)
 * First sends LED initialisation code followed by LED startup animation code, then sleeps until a button press for sync command.
 * RF module must be powered with 3.3V, two diodes in series with USB 5v will do. Connect the USB wires to a host computer, and the data and serial wires to Arduino.
 * of course, make sure to have a common ground
 * 
 * Modified to the Attiny by dantavares
 */

#include
#include

const int sync_pin = 3;  //power button repurposed for sync button (pin 5 on the module) do not change this !
const int data_pin = 2;  //data line (pin 6 on the module)
const int clock_pin = 1; //clock line (pin 7 on module) 

int led_cmd[10] =  {0, 0, 1, 0, 0, 0, 0, 1, 0, 0}; //Activates/initialises the LEDs, leaving the center LED lit.
int anim_cmd[10] = {0, 0, 1, 0, 0, 0, 0, 1, 0, 1}; //Makes the startup animation on the ring of light.
int sync_cmd[10] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0}; //Initiates the sync process.
volatile boolean sync_enable = 0;

void sendData(int cmd_do[]) {
  pinMode(data_pin, OUTPUT);
  digitalWrite(data_pin, LOW);    //start sending data.
  int prev = 1;
  for (int i = 0; i < 10; i++) {

    while (prev == digitalRead(clock_pin)) {} //detects change in clock
    prev = digitalRead(clock_pin);
    // should be after downward edge of clock, so send bit of data now
    digitalWrite(data_pin, cmd_do[i]);

    while (prev == digitalRead(clock_pin)) {} //detects upward edge of clock
    prev = digitalRead(clock_pin);
  }
  digitalWrite(data_pin, HIGH);
  pinMode(data_pin, INPUT);
}

void initLEDs() {
  sendData(led_cmd);
  delay(50);
  sendData(anim_cmd);
  delay(50);
}

void sleep() {
    GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
    PCMSK |= _BV(PCINT3);                   // Use PB3 as interrupt pin
    ADCSRA &= ~_BV(ADEN);                   // ADC off
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement

    sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
    sei();                                  // Enable interrupts
    sleep_cpu();                            // sleep

    cli();                                  // Disable interrupts
    PCMSK &= ~_BV(PCINT3);                  // Turn off PB3 as interrupt pin
    sleep_disable();                        // Clear SE bit
    ADCSRA |= _BV(ADEN);                    // ADC on

    sei();                                  // Enable interrupts
}

ISR(PCINT0_vect) {
  // This is called when the interrupt occurs, but I don't need to do anything in it
  sync_enable = 1;
}

void setup() {
  pinMode(sync_pin, INPUT);
  digitalWrite(sync_pin, HIGH);
  pinMode(data_pin, INPUT);
  pinMode(clock_pin, INPUT);
  delay(2000);
  initLEDs();
}

void loop() {
  sleep();
  delay(200);
  if (sync_enable == 1) {
    sendData(sync_cmd);
    sync_enable = 0;
  }
}

Lembrando que esse código deve ser compilado usando o Arduíno e pode usar a própria placa do Arduíno UNO para programa-lo. Se você não sabe fazer isso, o google esta ai cheio de tutorial pra você aprender. No meu caso, eu setei ele como 16Mhz Internal PPL.

Caso você queira usa-lo no Raspberry Pi, para mim funcionou perfeitamente no RecalBox, a única coisa que tive que fazer é editar o arquivo de configuração e desativar o suporte ao controle PS3 e habilitar o suporte ao controle XBox360.

quarta-feira, 14 de dezembro de 2016

Instalando driver desconhecido no Windows mais facilmente

Já vou avisando que ao contrário de muito blog charlatão por ai, eu não vou dar aqui solução mágica para você encontrar aquele driver que não tem em lugar nenhum, porém, eu vou ensinar de uma forma mais correta de encontra-lo. É claro que se você já chegou nesse ponto, é porque já entrou no site do fabricante etc e nada deu certo.

A ideia aqui é descobrir a identificação de hardware do dispositivo para encontra-lo de forma mais precisa. Algumas pessoas abrem para olhar o CI e mal sabem que é muito mais fácil consultar isso. O que nos interessa na verdade são duas coisas o VID e o PID. O VID é a identificação do fabricante e o PID é a identificação do dispositivo. Para descobrir isso é bem simples, basta entrar no gerenciador de dispositivos (Execute devmgmt.msc) e localizar o dispositivo faltando o driver, vá em propriedades deste dispositivo, e na aba "Detalhes" mude em "Propriedades" para IDs de Hardware, você vai ver algo parecido com isso: USB\VID_0C45&PID_608F&REV_0101&MI_00

Para facilitar, veja o exemplo na figura abaixo:

Clique na imagem para ampliar

Neste exemplo, é uma webcam ligada na USB, repare que o VID é 0C45 e o PID é 608F ou simplesmente 0C45 608F. Agora basta procurar por isso, e existe um site russo com um banco muito bom de drivers, em que a consulta pode ser feita pelo VID e PID, ele é o:


Repare como fica fácil agora achar ele neste site:


Mesmo assim, o devid não é 100%, como você mesmo pode ver, eu não achei drivers para sistema de 64bits (e provavelmente não tenha mesmo) que no meu caso não funcionou. Se o bater o desespero, tente a sorte no google mesmo, mas dai a pesquisa se torna bem mais árdua, para facilitar, no google, separe o vid e pid por ":" ou seja, nesse caso 0C45:608F


Mesmo assim, não encontrei nada que funcione para mim (para 64Bits), como eu disse anteriormente, é a maneira mais fácil de encontrar, porém o drivers precisa existir primeiro.