WiiリモコンをGamepadにして、HTML5で使う


以前の投稿で、Wiiの操作をブラウザから取得しました。

Wiiとブラウザの間をUbuntuが仲介し、ブラウザとUbuntuはMQTTを使っていました。
今回は、WindowsからみてBLE接続のM5StickCをGamepadとして見えるようにします。こうすることで、HTML5 Gamepad APIとしてWiiを使えるようになります。

 ブラウザ → HTML5 Gamepad API → Gamepad(ESP32) → Ubuntu → Wiiリモコン

繰り返しになりますが、HTML5 Gamepad APIとGamepad(ESP32)の間は、BLEです。
Gamepad(ES32)とUbuntuの間はMQTTではなくUDPにしました。シンプルにしたかったためというのと、ESP32は非力であるためです。
UbuntuとWiiリモコンの間は以前と同じです。
※ubuntuでなくてもラズパイなんかでもいいです

もろもろのソースコードはGitHubに上げておきました。

poruruba/WiiRemocon
 https://github.com/poruruba/WiiRemocon

また、以下のライブラリを利用していますので、あらかじめZIPダウンロードしてArduino IDEにインストールしておきます。

lemmingDev/ESP32-BLE-Gamepad
 https://github.com/lemmingDev/ESP32-BLE-Gamepad

Gamepadの準備

ESP32としてM5StickCを使いました。コンパイル環境はArduinoIDEを使いました。
ソースは、WiiGamepadフォルダにあります。

以下の部分を修正してください。

WiiGamepad/WiiGamepad.ino
const char* wifi_ssid = "【WiFiアクセスポイントのSID】";
const char* wifi_password = "【WiFiアクセスポイントのパスワード】";

以下の部分でUDP受信して、Jsonパースして解析し、Gamepadとして通知しています。

WiiGamepad/WiiGamepad.ino
uint32_t prev_buttons = 0;

void loop() 
{
  if( udp.parsePacket() ){
    int len = udp.read(message_buffer, sizeof(message_buffer));
    if( len > 0 && bleGamepad.isConnected() ){
      Serial.printf("received(%d) ", len);
      message_buffer[len] = '\0';
      Serial.printf("%s\n", message_buffer);
      DeserializationError err = deserializeJson(json_message, message_buffer, len);
      if( err ){
        Serial.println("Deserialize error");
        Serial.println(err.c_str());
        return;
      }

      int rsp = json_message["rsp"];
      if( rsp == WIIREMOTE_CMD_EVT ){
        uint8_t data[22];
        for( int i = 0 ; i < sizeof(data) ; i++ )
          data[i] = json_message["evt"][i]; 
        WII_REPORT report = parseReporting(data);
        if( report.report_id == WIIREMOTE_REPORTID_BTNS ){
          uint32_t diff = prev_buttons ^ report.btns.btns;
          bleGamepad.release(diff ^ (diff & report.btns.btns));
          bleGamepad.press(diff & report.btns.btns);
          prev_buttons = report.btns.btns;
        }else{
          Serial.println("Not support report id");
        }
      }
    }
  }
}

書き込まれ、起動が完了すると、Wifi接続し、接続が完了するとIPアドレスがSerialに表示されます。このIPアドレスは後で使うので覚えておきます。

ちなみに、Node.js v12.19.0で確認しました。
※前回まではv8で動かしていたのですが、v12に合わせていくつか修正してあります。

inquiryフォルダに移動します。

cd inquiry
npm install
sudo node inquiry_test.js

これで、Wiiを探している状態になるので、Wiiリモコンの①②を両方押して、Discoveryモードにすると、発見されてMACアドレスが表示されます。このMACアドレスを覚えておきます。

$sudo node inquiry_test.js
local: XX:XX:XX:XX:XX:XX
remote: XX:XX:XX:XX:XX:XX  ★これです!
status: 0
{ local: 'XX:XX:XX:XX:XX:XX', remote: 'XX:XX:XX:XX:XX:XX' }

次に、Wiiと通信してESP32にUDP通知するサーバを用意します。

cd wiiremocon
npm install
node-gyp rebuild

これで準備ができました。以下を起動します。

sudo index_dgram.js 【WiiのBLEのMACアドレス】 【ESP32のIPアドレス】

Wiiリモコンの①②を両方押して、Discoveryモードにすると、接続が完了します。(Wiiリモコンとの接続を切断した直後であれば、Wiiリモコンの①②を押さなくても接続が完了する場合があります)

Gamepadとして認識させる

まずは、WindowsにGamepadとして認識させます。

BLEデバイスを追加します。Bluetoothまたはその他のデバイスを追加する をクリックし、次の画面で、Bluetoothを選択します。

そうすると、ESP32 BLE Gamepadというのがあるのがわかります。クリックすると接続を試みます。

接続が完了しました。

こんな感じで「接続済み」となります。

Gamepadとして認識するには少し時間がかかります。(30秒ぐらい?)
デバイスとプリンタを選択し、ESP32 BLE Gamepadを右クリックすると、ゲームコントローラの設定というのがでてきていたら、認識完了です。

簡単な動作確認をしてみます。
ゲームコントローラの設定を選択します。

6軸32ボタンデバイスハットスイッチ付きが選択されている状態でプロパティを押下します。(現状はボタンしか対応させていませんが。。。)

ボタンを押せば、赤くなるのがわかります。

HTML5 Gamepad APIとして使う

今度は、ブラウザからアクセスしてみます。
WindowsがM5StickCをGamepadとして認識しているので、あとは、ブラウザのHTML5 Gamepad APIからアクセスします。

htmlフォルダにあるコンテンツをどこかのHTTPSのWebサーバに配備します。
ブラウザからそれを開きます。

 https://【Webサーバのホスト名】/wiigamepad/index.html

接続できると、gamepad_foundがtrueとなり、押したボタンの色が濃くなるかと思います。

html/js/start.js
        setInterval(() =>{
            var gamepadList = navigator.getGamepads();
            console.log(gamepadList);
            var found = false;
            for( var i = 0 ; i < gamepadList.length ; i++ ){
                var gamepad = gamepadList[i];
                if( gamepad ){
                    found = true;
                    for( var j = 0 ; j < gamepad.buttons.length ; j++ ){
                        this.$set(this.chk_btns, j, gamepad.buttons[j].pressed);
                    }
                }
            }
            this.gamepad_found = found;
        }, 100);

以上