以前、以下の投稿をしています。
「原神」が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程度にします。
#define WEBSOCKETS_MAX_DATA_SIZE (25 * 1024)
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ファイルも)
配置したフォルダやファイル名を以下に記載しておきます。
var vmd_list = [
・・・
var stage_list = [
・・・
var pmx_list = [
・・・
あとは、HTML Canvasへの描画タイミングに合わせて、CanvasからJPEGを作成し、WebSocketでM5Core2に送信しています。
以下の部分です。
あまりに早く送信するとESP32側のWebSocketの通信バッファがあふれてしまうようで、間隔をあけて送信しています。
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ファイルのキャラクタ名を選択します。
そうすると、M5Core2に踊る画像が表示されます。更新間隔を200msec以上じゃないとうまく表示されませんでした。LovyanGFXの問題ではなく、WebSocketの問題です。
ちなみに、ブラウザのCanvas上で、マウス操作することで拡大や視点移動ができ、その結果もM5Core2の画面に反映されるのがわかります。
#補足
ESP32内に立ち上げられるWebSocketサーバはHTTP接続のため、HTTPSで立ち上げたWebページのJavascriptからは「Mixed Content」となって接続できません。
その場合は、Web Proxyサーバを立ち上げる必要があります。以下を参考にしてください。
HTTPで立ち上げたWebSocketサーバに、HTTPSで接続するためのWeb Proxyの立ち上げ
以上