Comunicazione con il controller Stadia tramite WebHID

Il controller Stadia con flash funziona come un gamepad standard, il che significa che non tutti i suoi pulsanti sono accessibili tramite l'API Gamepad. Con WebHID, ora puoi accedere ai pulsanti mancanti.

Dopo la chiusura di Stadia, molti temevano che il controller sarebbe diventato un hardware inutile gettato in discarica. Fortunatamente, il team di Stadia ha deciso di rendere disponibile invece il controller Stadia fornendo un firmware personalizzato che puoi lampeggiare sul controller visitando la pagina della modalità Bluetooth di Stadia. In questo modo il controller Stadia apparirà come un gamepad standard a cui puoi collegarlo tramite cavo USB o in modalità wireless tramite Bluetooth. orgogliosamente presente nella vetrina dell'API Project Fugu, la pagina Bluetooth di Stadia stessa utilizza WebHID e WebUSB, ma questo non è l'argomento di questo articolo. In questo post voglio spiegare come comunicare con il controller Stadia tramite WebHID.

Il controller Stadia come gamepad standard

Dopo il flashing, il controller viene visualizzato come gamepad standard per il sistema operativo. Fai riferimento allo screenshot che segue per avere una disposizione comune di pulsanti e assi in un gamepad standard. Come definito nelle specifiche dell'API Gamepad, i gamepad standard hanno pulsanti da 0 a 16, quindi 17 in totale (il D-pad conta come quattro pulsanti). Se provi il controller Stadia nella demo dei tester per i gamepad, noterai che funziona come un ciondolo.

Uno schema di un gamepad standard con i vari assi e pulsanti etichettati.

Tuttavia, se si conteggiano i pulsanti sul controller Stadia, ce ne sono 19. Se li provi sistematicamente uno alla volta nel tester del gamepad, ti accorgerai che i pulsanti Assistente e Acquisisci non funzionano. Anche se l'attributo buttons del gamepad come definito nelle specifiche del Gamepad, è aperto, poiché il controller Stadia appare come un gamepad standard, vengono mappati solo i pulsanti da 0 a 16. Puoi comunque usare gli altri pulsanti, ma la maggior parte dei giochi non se ne aspetta.

WebHID in soccorso

Grazie all'API WebHID, puoi parlare con i pulsanti mancanti 17 e 18. Se vuoi, puoi persino recuperare i dati relativi a tutti gli altri pulsanti e assi già disponibili tramite l'API Gamepad. Il primo passo consiste nel capire come il controller Stadia si riferisce al sistema operativo. Un modo per farlo è aprire la console Chrome DevTools in una qualsiasi pagina a caso e richiedere un elenco non filtrato di dispositivi all'API WebHID. Quindi scegli manualmente il controller Stadia per ulteriori ispezioni. Ottieni un elenco di dispositivi senza filtri semplicemente passando un array di opzioni filters vuoto.

const [device] = await navigator.hid.requestDevice({filters: []});

Nel selettore, la penultima voce sembra il controller Stadia.

Il selettore dispositivo dell'API WebHID che mostra alcuni dispositivi non correlati e il controller Stadia nella penultima posizione.

Dopo aver selezionato il dispositivo "Controller Stadia rev. A", registra l'oggetto HIDDevice risultante nella Console. Vengono visualizzati productId (37888, che è 0x9400 in esadecimale) e vendorId (6353, che è 0x18d1 in esadecimale). Se cerchi vendorID nella tabella degli ID fornitore USB ufficiale, vedrai che 6353 corrisponde a ciò che ti aspetteresti: Google Inc..

Console Chrome DevTools che mostra l'output del logging dell'oggetto HIDDevice.

Un'alternativa alla procedura descritta sopra è andare all'indirizzo chrome://device-log/ nella barra degli URL, premere il pulsante Cancella, collegare il controller Stadia, quindi premere Aggiorna. In questo modo ottieni le stesse informazioni.

L'interfaccia di debug chrome://device-log che mostra informazioni sul controller Stadia collegato.

Un'altra alternativa è utilizzare lo strumento HID Explorer, che consente di esplorare ancora più dettagli sui dispositivi HID collegati al computer.

Utilizza questi due ID, vendorId e productId, per perfezionare ciò che viene mostrato nel selettore, filtrando correttamente in base al dispositivo WebHID corretto.

const [stadiaController] = await navigator.hid.requestDevice({filters: [{
  vendorId: 6353,
  productId: 37888,
}]});

Ora il rumore di tutti i dispositivi non correlati è scomparso e viene visualizzato solo il controller Stadia.

Selettore dispositivo dell'API WebHID che mostra solo il controller Stadia.

Nel prossimo passaggio, apri HIDDevice chiamando il metodo open().

await stadiaController.open();

Registra di nuovo HIDDevice e il flag opened è impostato su true.

La console Chrome DevTools che mostra l'output del logging dell'oggetto HIDDevice dopo l'apertura.

Con il dispositivo aperto, puoi ascoltare gli eventi inputreport in arrivo collegando un listener di eventi.

stadiaController.addEventListener('inputreport', (e) => {
  console.log(e);
});

Quando premi e rilasci il pulsante Assistente sul controller, nella console vengono registrati due eventi. Puoi considerarli come eventi con pulsante "Bottone Assistente e "Assistente Bottone Alto". Ad eccezione di timeStamp, i due eventi sembrano indistinguibili a prima vista.

La console Chrome DevTools che mostra gli oggetti HIDInputReportEvent che vengono registrati.

La proprietà reportId dell'interfaccia HIDInputReportEvent restituisce il prefisso di identificazione a un byte per questo report o 0 se l'interfaccia HID non utilizza gli ID report. In questo caso è 3. Il secret si trova nella proprietà data, rappresentata come un DataView di dimensione 10. Un elemento DataView offre un'interfaccia di basso livello per la lettura e la scrittura di più tipi di numeri in un file binario ArrayBuffer. Il modo per ottenere qualcosa di più digeribile da questa rappresentazione è creare un Uint8Array da ArrayBuffer, in modo da poter vedere i singoli numeri interi senza segno a 8 bit.

const data = new Uint8Array(event.data.buffer);

Quando poi registri di nuovo i dati sugli eventi del report sull'input, le cose iniziano a essere più sensate e gli eventi "Tasto dell'assistente" e "Tasto Assistente" iniziano a essere decifrabili. Il primo numero intero (8 in entrambi gli eventi) sembra essere correlato alla pressione dei pulsanti, mentre il secondo numero intero (2 e 0) sembra essere correlato al fatto che il pulsante dell'assistente sia stato premuto o meno.

La console Chrome DevTools che mostra gli oggetti Uint8Array registrati per ogni HIDInputReportEvent.

Premi il pulsante Acquisisci anziché il pulsante Assistente e vedrai che il secondo numero intero passa da 1 quando viene premuto a 0 quando viene rilasciato. Ciò ti permette di scrivere un "driver" molto semplice che ti consente di utilizzare i due pulsanti mancanti.

stadia.addEventListener('inputreport', (event) => {
  if (!e.reportId === 3) {
    return;
  }
  const data = new Uint8Array(event.data.buffer);
  if (data[0] === 8) {
    if (data[1] === 1) {
      hidButtons[1].classList.add('highlight');
    } else if (data[1] === 2) {
      hidButtons[0].classList.add('highlight');
    } else if (data[1] === 3) {
      hidButtons[0].classList.add('highlight');
      hidButtons[1].classList.add('highlight');
    } else {
      hidButtons[0].classList.remove('highlight');
      hidButtons[1].classList.remove('highlight');
    }
  }
});

Utilizzando un approccio di reverse engineering come questo, puoi, pulsante per pulsante e asse per asse, capire come comunicare con il controller Stadia con WebHID. Una volta che hai preso dimestichezza, il resto è un lavoro di mappatura di numeri interi meccanici.

Quello che manca ora è l'esperienza di connessione ottimale offerta dall'API Gamepad. Anche se per motivi di sicurezza devi sempre eseguire una volta l'esperienza del selettore iniziale per poter funzionare con un dispositivo WebHID come il controller Stadia, per le connessioni future puoi riconnetterti ai dispositivi noti. Per farlo, chiama il metodo getDevices().

let stadiaController;
const [device] = await navigator.hid.getDevices();
if (device && device.vendorId === 6353 && device.productId === 37888) {
  stadiaController = device;
}

Demo

Puoi vedere il controller Stadia controllato congiuntamente dall'API Gamepad e dall'API WebHID in una demo che ho creato. Assicurati di controllare il codice sorgente, che si basa sugli snippet di questo articolo. Per semplicità, mostro soltanto i pulsanti A, B, X e Y (controllati dall'API Gamepad), nonché i pulsanti assistente e Acquisisci (controllati dall'API WebHID). Sotto l'immagine del controller puoi vedere i dati WebHID non elaborati, in modo da avere un'idea di tutti i pulsanti e gli assi del controller.

L'app demo all'indirizzo https://stadia-controller-webhid-gamepad.glitch.me/ mostra i pulsanti A, B, X e Y controllati dall'API Gamepad, nonché i pulsanti Assistente e Capture controllati dall'API WebHID.

Conclusioni

Grazie al nuovo firmware, il controller Stadia è ora utilizzabile come gamepad standard con 17 pulsanti, che, nella maggior parte dei casi, sono più che sufficienti per controllare i giochi web più comuni. Se, per qualsiasi motivo, hai bisogno dei dati di tutti e 19 i pulsanti sul controller, WebHID ti consente di accedere a report di input di basso livello che puoi decifrare eseguendo il reverse engineering uno alla volta. Se dovessi scrivere un driver WebHID completo dopo aver letto questo articolo, assicurati di contattarmi e sarò felice di collegare il tuo progetto qui. Buon WebHIDing!

Ringraziamenti

Questo articolo è stato recensito da François Beaufort.