2
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

MMDダンスをM5Core2のディスプレイに表示する

以前、以下の投稿をしています。
 「原神」がMMDモデルを配信してくれたので、JavascriptでMMDビューアを作った

今回は、ブラウザ上でMMDダンスしている様子を、ESP32のM5Core2のディスプレイに表示してみます。
ESP32でのディスプレイ表示は、Lovyanさんの以下のライブラリを利用させていただいていますので、いろんなESP32デバイスで表示できるかと思います。

lovyan03/LovyanGFX

M5Core2とブラウザとの間は、WebSocketで通信しています。

Links2004/arduinoWebSockets

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

poruruba/MMDViewer_WebSocket

M5Core2側

WebSocketを開いておいて、受信したJpegをLovyanGFXでLCDに描画しているだけです。

1点だけ、注意がありまして、WebSocketのデフォルトのバッファサイズが小さく、あふれるとコネクションが切断されてしまうので、画像ファイルのサイズに合わせて、大きくしています。
今回は、解像度が320x240なので、25KB程度にします。

MMDViewer_WebSocket\.pio\libdeps\m5stack-fire\WebSockets\src\WebSockets.h
#define WEBSOCKETS_MAX_DATA_SIZE (25 * 1024)

main.cppのソースコードです。

MMDViewer_WebSocket\src\main.cpp
#define LGFX_AUTODETECT // 自動認識

#include <M5Core2.h>
#include <LovyanGFX.hpp>
#include <WiFi.h>
#include <HTTPClient.h>
#include <WebSocketsServer.h>

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

#define WEBSOCKET_PORT  81 // WebSocketのポート番号

WebSocketsServer webSocket = WebSocketsServer(WEBSOCKET_PORT);
static LGFX lcd; 

void wifi_connect(const char *ssid, const char *password){
  Serial.println("");
  Serial.print("WiFi Connenting");
  lcd.println("WiFi Connecting");

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    lcd.print(".");
    delay(1000);
  }
  Serial.println("");
  Serial.print("Connected : ");
  Serial.print(WiFi.localIP());
  Serial.print(" (");
  Serial.print(WEBSOCKET_PORT);
  Serial.println(")");
  lcd.println("");
  lcd.print("Connected : ");
  lcd.print(WiFi.localIP());
  lcd.print(" (");
  lcd.print(WEBSOCKET_PORT);
  lcd.println(")");
}

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
  switch(type) {
    case WStype_DISCONNECTED:
      Serial.printf("[%u] Disconnected!\n", num);
      break;
    case WStype_CONNECTED:{
        IPAddress ip = webSocket.remoteIP(num);
        Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
      }
      break;
    case WStype_BIN:
//      Serial.printf("WStype_BIN : [%u] get binary length: %u\n", num, length);
      lcd.drawJpg(payload, length);
      break;
    case WStype_ERROR:
      Serial.printf("WStype_ERROR : [%u]\n", num);
      break;
    case WStype_FRAGMENT_BIN_START:
    case WStype_FRAGMENT:
    case WStype_FRAGMENT_FIN:
    case WStype_FRAGMENT_TEXT_START:
    default:
      break;
  }
}

void setup() {
  Serial.begin(9600);
  Serial.println("setup");

  lcd.init();

  wifi_connect(wifi_ssid, wifi_password);

  webSocket.begin();
  webSocket.onEvent(webSocketEvent);
}

void loop() {
  webSocket.loop();
}

環境に合わせて、以下の部分を変えてください。
・【WiFiアクセスポイントのSSID】
・【WiFiアクセスポイントのパスワード】

ブラウザ側

MMDファイルのJavascriptによる表示は以下で投稿しています。
 「原神」がMMDモデルを配信してくれたので、JavascriptでMMDビューアを作った

まずは、上記を参考にして、three.jsとMMDローダをセットアップしておきます。
また、お好きなMMDのPMD/PMXファイルをダウンロードし配備しておきます。(著作権の関係で置いていません)。
(お好みでダンスのためのVMDファイルやステージのためのPMDや、ポーズのためのPMXファイルも)
配置したフォルダやファイル名を以下に記載しておきます。

public_html\js\start.js
var vmd_list = [
・・・
var stage_list = [
・・・
var pmx_list = [
・・・

あとは、HTML Canvasへの描画タイミングに合わせて、CanvasからJPEGを作成し、WebSocketでM5Core2に送信しています。

以下の部分です。
あまりに早く送信するとESP32側のWebSocketの通信バッファがあふれてしまうようで、間隔をあけて送信しています。

public_html\js\start.js
        this.selecting.mmd.setCallback((canvas) =>{
            if( socket.bufferedAmount <= 0 ){
                var end_tick = new Date().getTime();
                if( this.start_tick == 0 || ((end_tick - this.start_tick) >= this.duration)){
                    this.start_tick = end_tick;
                    canvas.toBlob((blob) =>{
                        socket.send(blob);
                    }, "image/jpeg", this.quality / 100);
                }
            }
        });

以下の部分は環境に合わせて書き換えてください。
・【M5Core2のIPアドレス】

実行方法

ブラウザからページを開き、websocket_urlにM5Core2のIPアドレスを指定して、PMXファイルのキャラクタ名を選択します。

image.png

そうすると、M5Core2に踊る画像が表示されます。更新間隔を200msec以上じゃないとうまく表示されませんでした。LovyanGFXの問題ではなく、WebSocketの問題です。

(保護シート付けたままなので、汚くてすみません!)
image.png

ちなみに、ブラウザのCanvas上で、マウス操作することで拡大や視点移動ができ、その結果もM5Core2の画面に反映されるのがわかります。

補足

ESP32内に立ち上げられるWebSocketサーバはHTTP接続のため、HTTPSで立ち上げたWebページのJavascriptからは「Mixed Content」となって接続できません。
その場合は、Web Proxyサーバを立ち上げる必要があります。以下を参考にしてください。

 HTTPで立ち上げたWebSocketサーバに、HTTPSで接続するためのWeb Proxyの立ち上げ

以上

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
2
Help us understand the problem. What are the problem?