Komunikacja z kontrolerem Stadia za pomocą protokołu WebHID

Kontroler Stadia po wgraniu oprogramowania działa jak standardowy pad do gier, co oznacza, że nie wszystkie jego przyciski są dostępne za pomocą interfejsu Gamepad API. Dzięki WebHID możesz teraz uzyskać dostęp do brakujących przycisków.

Po zamknięciu Stadia wiele osób obawiało się, że kontroler stanie się bezużytecznym sprzętem na wysypisku śmieci. Na szczęście zespół Stadia postanowił udostępnić kontroler Stadia, dostarczając niestandardowe oprogramowanie, które możesz wgrać na kontroler, przechodząc na stronę Tryb Bluetooth Stadia. Dzięki temu kontroler Stadia będzie widoczny jako standardowy gamepad, który można podłączyć za pomocą kabla USB lub bezprzewodowo przez Bluetooth. Strona Stadia Bluetooth jest prezentowana w sekcji Project Fugu API Showcase i korzysta z interfejsów WebHIDWebUSB, ale nie o tym jest ten artykuł. W tym poście chcę wyjaśnić, jak można komunikować się z kontrolerem Stadia za pomocą WebHID.

Kontroler Stadia jako standardowy pad do gier

Po zakończeniu migania kontroler będzie widoczny w systemie operacyjnym jako standardowy gamepad. Na zrzucie ekranu poniżej widać typowy układ przycisków i osi na standardowym gamepadzie. Zgodnie ze specyfikacją Gamepad API standardowe gamepady mają przyciski od 0 do 16, czyli łącznie 17 przycisków (pad kierunkowy jest liczony jako 4 przyciski). Jeśli wypróbujesz kontroler Stadia w wersji demonstracyjnej testera pada do gier, zauważysz, że działa bez zarzutu.

Schemat standardowego gamepada z oznaczonymi osiami i przyciskami.

Jeśli jednak policzysz przyciski na kontrolerze Stadia, jest ich 19. Jeśli będziesz je kolejno testować w testerze pada, zauważysz, że przyciski AsystentZapis nie działają. Nawet jeśli buttons atrybut zdefiniowany w specyfikacji pada do gier jest otwarty, ponieważ kontroler Stadia jest traktowany jako standardowy pad do gier, mapowane są tylko przyciski 0–16. Możesz nadal używać innych przycisków, ale większość gier nie będzie ich obsługiwać.

WebHID na ratunek

Dzięki interfejsowi WebHID API możesz korzystać z brakujących przycisków 17 i 18. Jeśli chcesz, możesz nawet uzyskać dane o wszystkich innych przyciskach i osiach, które są już dostępne w interfejsie Gamepad API. Pierwszym krokiem jest sprawdzenie, jak kontroler Stadia zgłasza się do systemu operacyjnego. Możesz to zrobić, otwierając konsolę Narzędzi deweloperskich w Chrome na dowolnej stronie i wysyłając do interfejsu WebHID API żądanie niefiltrowanej listy urządzeń. Następnie ręcznie wybierz kontroler Stadia, aby go dokładniej sprawdzić. Aby uzyskać niefiltrowaną listę urządzeń, wystarczy przekazać pustą tablicę opcji filters.

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

Przedostatni element na liście wygląda jak kontroler Stadia.

Selektor urządzeń interfejsu WebHID API wyświetla kilka niezwiązanych urządzeń, a kontroler Stadia znajduje się na przedostatniej pozycji.

Po wybraniu urządzenia „Stadia Controller rev. A” zaloguj wynikowy obiekt HIDDevice w konsoli. Wyświetlą się wartości productId (37888, czyli 0x9400 w systemie szesnastkowym) i vendorId (6353, czyli 0x18d1 w systemie szesnastkowym) kontrolera Stadia. Jeśli wyszukasz vendorID w oficjalnej tabeli identyfikatorów dostawców USB, zobaczysz, że 6353 odpowiada oczekiwanej wartości: Google Inc..

Konsola Narzędzi deweloperskich w Chrome pokazująca dane wyjściowe rejestrowania obiektu HIDDevice.

Alternatywą dla opisanej powyżej procedury jest przejście do ikony chrome://device-log/ na pasku adresu URL, naciśnięcie przycisku Wyczyść, podłączenie kontrolera Stadia, a następnie naciśnięcie przycisku Odśwież. Zawiera ona te same informacje.

Interfejs debugowania chrome://device-log z informacjami o podłączonym kontrolerze Stadia.

Inną alternatywą jest użycie narzędzia HID Explorer, które pozwala poznać jeszcze więcej szczegółów dotyczących urządzeń HID podłączonych do komputera.

Użyj tych 2 identyfikatorów, vendorIdproductId, aby doprecyzować, co jest wyświetlane w selektorze, poprzez prawidłowe filtrowanie odpowiedniego urządzenia WebHID.

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

Szum z niepowiązanych urządzeń zniknął i wyświetla się tylko kontroler Stadia.

Selektor urządzeń interfejsu WebHID API pokazujący tylko kontroler Stadia.

Następnie otwórz HIDDevice, wywołując metodę open().

await stadiaController.open();

Zaloguj ponownie HIDDevice, a flaga opened zostanie ustawiona na true.

Konsola narzędzi Chrome dla programistów pokazująca dane wyjściowe logowania obiektu HIDDevice po jego otwarciu.

Po otwarciu urządzenia nasłuchuj przychodzących zdarzeń inputreport, dołączając detektor zdarzeń.

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

Gdy naciśniesz i puścisz przycisk Asystenta na kontrolerze, w konsoli zostaną zarejestrowane 2 zdarzenia. Możesz je traktować jako zdarzenia „przycisk Asystenta wciśnięty” i „przycisk Asystenta zwolniony”. Poza wartością timeStamp te 2 zdarzenia na pierwszy rzut oka wyglądają identycznie.

Konsola Narzędzi deweloperskich w Chrome wyświetlająca obiekty HIDInputReportEvent, które są rejestrowane.

Właściwość reportId interfejsu HIDInputReportEvent zwraca 1-bajtowy prefiks identyfikacyjny tego raportu lub wartość 0, jeśli interfejs HID nie używa identyfikatorów raportów. W tym przypadku jest to 3. Tajemnica tkwi we data właściwości, która jest reprezentowana jako DataView o rozmiarze 10. DataView zapewnia interfejs niskiego poziomu do odczytywania i zapisywania wielu typów liczb w binarnym ArrayBuffer. Aby uzyskać bardziej przystępną formę tej reprezentacji, utwórz z ArrayBuffer Uint8Array, aby zobaczyć poszczególne 8-bitowe liczby całkowite bez znaku.

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

Gdy ponownie zarejestrujesz dane zdarzenia raportu wejściowego, wszystko zacznie mieć więcej sensu, a zdarzenia „Przycisk Asystenta wciśnięty” i „Przycisk Asystenta zwolniony” staną się zrozumiałe. Pierwsza liczba całkowita (8 w obu zdarzeniach) wydaje się związana z naciśnięciami przycisków, a druga liczba całkowita (20) – z tym, czy przycisk Asystenta jest naciśnięty.

Konsola Narzędzi deweloperskich Chrome wyświetlająca obiekty Uint8Array rejestrowane dla każdego zdarzenia HIDInputReportEvent.

Naciśnij przycisk Zapisz zamiast przycisku Asystent. Zobaczysz, że druga liczba całkowita zmienia się z 1 (gdy przycisk jest naciśnięty) na 0 (gdy jest zwolniony). Dzięki temu możesz napisać bardzo prosty „sterownik”, który umożliwi korzystanie z brakujących 2 przycisków.

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');
    }
  }
});

Stosując taką metodę inżynierii wstecznej, możesz przycisk po przycisku i oś po osi dowiedzieć się, jak komunikować się z kontrolerem Stadia za pomocą WebHID. Gdy już to opanujesz, reszta będzie niemal mechanicznym odwzorowywaniem liczb całkowitych.

Brakuje tylko płynnego połączenia, które zapewnia interfejs Gamepad API. Ze względów bezpieczeństwa musisz przejść przez początkowy proces wyboru, aby korzystać z urządzenia WebHID, takiego jak kontroler Stadia. W przypadku przyszłych połączeń możesz ponownie połączyć się ze znanymi urządzeniami. Aby to zrobić, wywołaj metodę getDevices().

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

Prezentacja

Możesz zobaczyć kontroler Stadia sterowany wspólnie przez interfejs Gamepad API i interfejs WebHID API w demonstracji, którą przygotowałem. Zapoznaj się z kodem źródłowym, który jest rozszerzeniem fragmentów kodu z tego artykułu. Dla uproszczenia wyświetlam tylko przyciski A, B, XY (sterowane przez interfejs Gamepad API) oraz przyciski AsystentZrzut (sterowane przez interfejs WebHID API). Pod obrazem kontrolera zobaczysz surowe dane WebHID, dzięki czemu możesz sprawdzić wszystkie przyciski i osie na kontrolerze.

Aplikacja demonstracyjna kontrolera Stadia pokazująca przyciski A, B, X i Y sterowane przez interfejs Gamepad API oraz przyciski Asystenta i Zrzut sterowane przez interfejs WebHID API.

Podsumowanie

Dzięki nowemu oprogramowaniu kontroler Stadia może być używany jako standardowy gamepad z 17 przyciskami, co w większości przypadków wystarcza do sterowania popularnymi grami internetowymi. Jeśli z jakiegoś powodu potrzebujesz danych ze wszystkich 19 przycisków na kontrolerze, WebHID umożliwia uzyskanie dostępu do raportów wejściowych niskiego poziomu, które możesz odczytać, analizując je pojedynczo. Jeśli po przeczytaniu tego artykułu napiszesz kompletny sterownik WebHID, skontaktuj się ze mną, a z przyjemnością umieszczę tu link do Twojego projektu. Happy WebHIDing!

Podziękowania

Ten artykuł został sprawdzony przez François Beauforta.