えっWebRTCって Web Real-Time Christmastreeのことだよね？？【後編】


こちらは[SkyWay Advent Calendar 2018](https://qiita.com/advent-calendar/2018/skyway)の最終日、25日目の記事です。
【前編】は、[IoTLT Advent Calendar 2018](https://qiita.com/advent-calendar/2018/iotlt)の[24日目の記事](https://qiita.com/ukk0/items/7b352ac251c5b3217b52)になります。

***

昨晩プレゼントをもらえたみなさまも、サンタクロースをやっていたみなさまも、おはようございます。
この記事は、Webリアルタイムクリスマスツリーをつくり、ローカルPCのブラウザからリモートのPCを経由してクリスマスツリーのLEDを制御し、そのリアルタイム映像をWebRTCを経由して虚ろに眺めるというプロダクトの後編となります。（長い）
いきなりこちらに来た方は、まずはじめに[【前編】](https://qiita.com/ukk0/items/7b352ac251c5b3217b52)をご覧ください。

## なにをつくるんだっけ

組み込みWebRTCに心が折れ、WebBluetoothに浮気して、最終的にこんな感じにしたくなったのでした↓

![fig2.png](https://qiita-image-store.s3.amazonaws.com/0/116011/36113753-93b1-0741-5511-562ba0475d17.png "fig2.png")

前編の記事では、WebからBluetoothでクリスマスツリーを操作するところまでできました。
では次にWebRTCとつなげるところからはじめましょう。

## SkyWayで簡単にWebRTCを試す

SkyWayとは、WebRTCにおけるサーバサイドを全部なんとかしてくれる素敵なシステムです。
知らなかった方は以下をどうぞ。

[SkyWay - Enterprise Cloud WebRTC Platform](https://webrtc.ecl.ntt.com)

ところで、僕は[SkyWay Advent Calendar 2018](https://qiita.com/advent-calendar/2018/skyway)の23日目にも1つ記事を書かせてもらっております。

[テキスト送受信機能つきビデオチャットのサンプルをつくる](https://qiita.com/ukk0/items/aa7cf0d30acacdc1102d)

書くことになった経緯は忘れたのですが、Qiitaから通知があって思い出して頑張って書きました。
そうしたら、こんなにもWebRTCって簡単にできるのか？という気持ちになり、じゃあクリスマスツリーぐらいリアルタイムできるよね🎄🎉となって今に至るわけです。

さて、上述の記事を要約すると

- サンプルプログラムには「ビデオチャット」と「テキストチャット」がある
- しかしビデオとテキストの両方を同時に送るサンプルがない
- 作った

というものになります。
つまり、この時点ですでに

- 映像・音声・文字列が同時に送受信できる    
**→制御コマンドの送信と、相手の映像の受信が同時にできる**

ということになります。これはもう勝ったも同然ですね🍷🍾

## テキスト送受信機能つきビデオチャットを改造する

まずは拙作のこちらのリポジトリのソースをもとに改良を加えてゆきます。

GitHub: [ukkz/skyway-video-and-text-chat](https://github.com/ukkz/skyway-video-and-text-chat)

### HTML

```index.html
<!-- Video area -->
<div class="pure-u-2-3" id="video-container">
  <div style="text-align:left;">
    BLE Local Connection: <span id="ble_dev_name">none</span> <button id="btn_ble_ctrl">Connect</button><br>
    Command destination: <button id="command_send_to">LOCAL</button><br>
    <br>
    Mouse - X <input type="text" id="mouse_x" size="20"> Diff: <input type="text" id="mouse_x_diff" size="20"><br>
    Mouse - Y <input type="text" id="mouse_y" size="20"> Diff: <input type="text" id="mouse_y_diff" size="20"><br>
    Moving: <input type="text" id="mouse_moving" size="20"> State: 🎄<span class="tree_state" hidden>✨</span><br>
  </div>
  <div id="their-videos"></div>
  <video id="my-video" muted="true" autoplay playsinline></video>
</div>
```

もともとのHTMLのビデオ表示部分に、以下の項目を追加しました。

- BLEデバイス接続・切断ボタン
- コマンド送信先選択（このブラウザにつながったデバイスか、WebRTCのPeerに送信するか）
- マウスポインタX座標と移動差分
- マウスポインタY座標と移動差分
- XY移動速度の積とツリーLED発火インジケータ

前編記事の最後では、マウスを動かすとツリーに直接コマンドが送られるように作りましたが
そのUI部分を移植したものになります。

### JS

JavaScriptについては、WebBluetooth部分と、WebRTC(SkyWay SDK)部分にわけました。

#### WebUI部分

```ble.js
// コマンド送信先切り替え
// 表示だけ切り替えて、あとは実際に送信の直前に参照
$('#command_send_to').on('click', () => {
  if ($('#command_send_to').text() == 'LOCAL') {
    $('#command_send_to').text('PEERS');
  } else {
    $('#command_send_to').text('LOCAL');
  }
});
```

コマンド送信先選択ボタンは、単純にDOMのテキストをtoggleしているだけです。
実際に送信する前に参照してどちらに送るか決めています。

```script.js
// マウス位置取得
$(window).mousemove(function() {
  // 差分取得
  var diffX = posX-event.clientX;
  var diffY = posY-event.clientY;
  var moving = diffX * diffY;

  // Aチャンネル
  if (diffX >= 50)
    sendCommandWithSwitch('A0');
  else if (diffX <= -50)
    sendCommandWithSwitch('A1');

  // Bチャンネル
  if (diffY >= 50)
    sendCommandWithSwitch('B0');
  else if (diffY <= -50)
    sendCommandWithSwitch('B1');

  // Cチャンネル
  if (moving >= 150)
    sendCommandWithSwitch('C0');
  else if (moving <= -150)
    sendCommandWithSwitch('C1');
  else
    sendCommandWithSwitch('X0');

  // ブラウザ側の表示を更新
  $('#mouse_x_diff').val(diffX);
  $('#mouse_y_diff').val(diffY);
  $('#mouse_x').val(event.clientX);
  $('#mouse_y').val(event.clientY);
  $('#mouse_moving').val(moving);
  posX = event.clientX;
  posY = event.clientY;
});
```

マウスが動くたびに発火するイベントはWebRTCスクリプト側に記述しました。
イベントの発火レートの上限がどこまであるか、というか一定であるかすらわからないので、まさかずっとゼロのままなのでは…… と思いながら試してみたらそれなりに差分は抽出できました。詳しい人教えてくださいな。

#### コマンドの送信

```script.js
// 送信先切り替え用
function sendCommandWithSwitch(code) {
  // ボタンの文字列を参照してLOCALかPEERSで切り替える
  if ($('#command_send_to').text() == 'LOCAL') {
    // LOCALのとき:通常コマンドを使う
    sendToLocalTree(code);
  } else {
    // PEERSのとき:コマンド用の接頭辞をつけて送信する
    room.send('COMMAND_' + code);
  }
}
```

こちらの関数では、先ほどの送信先切り替えボタンのtextを読んで、ローカルに接続されたBLEに送るか、それともWebRTCで送るか、を判断しています。
WebRTCで送るときはRoomオブジェクトで`room.send()`をするだけでOKですが、テキストチャットの通常メッセージと区別するために、`COMMAND_`を先頭に付加しています。

#### コマンドの受信

```script.js
room.on('data', message => {
  if (message.data.substring(0,8) == 'COMMAND_') {
    // COMMAND_ で始まるメッセージはコマンド扱い
    // ツリー操作に利用する
    sendToLocalTree(message.data.substring(8)); // コマンド部分のみ送る
  } else {
    // ふつうのメッセージ
    messages.prepend('<div><span class="peer">' + message.src.substr(0,8) + '</span>: ' + message.data + '</div>');
  }
});
```

`room.on('data', コールバック)`の部分で、まず`message.data`の先頭8文字が`COMMAND_`であるかどうかで制御コマンドかどうか判定します。で、到着メッセージをそのままDOMに追加していくとコマンド文字列まみれでえらいことになるので、コマンドじゃない場合にのみDOMにprependするようにします。

## できた

![5caf91dd-6d45-498b-ba33-b3393b847a88.png](https://qiita-image-store.s3.amazonaws.com/0/116011/403abd45-9691-92cf-1ffa-4ba7d57cbb29.png "5caf91dd-6d45-498b-ba33-b3393b847a88.png")

こんな画面になりました。
使い方としては、自分のPCと、クリスマスツリーの前に別のPCをもう一つ用意し、両方からこのページをブラウザで開いて同じルームに参加します。

![fig3.png](https://qiita-image-store.s3.amazonaws.com/0/116011/945832d2-6a88-3abf-a18e-777a9449855a.png "fig3.png")

クリスマスツリー前PCは、WebBluetoothでNefryに接続し、コマンド送信先をその接続したNefry宛て（LOCAL）にしておきます。ツリーを眺めるための自分のPCは、WebBluetoothは接続しませんが、コマンド送信先をPEERにしておくことで、マウスカーソルの動きがデータとしてWebRTCでツリー前PCに送信されます。

## 試してみた動画

<iframe width="560" height="315" src="https://www.youtube.com/embed/SDiOY_H2uEE" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

[WebRealTimeChristmastree at holy night - YouTube](https://youtu.be/SDiOY_H2uEE)

クリスマスの夜に、部屋を真っ暗にして撮ってみました。
みんなもNefryBTやSkyWayでレッツクリスマスナイッ🎄✨

## サンプル

まずお手持ちのNefry BTに以下のスケッチを書き込んでください。
ピン配置などはお手持ちのクリスマスツリーにあわせて適宜変更してくださいねー
[NefryXmasSkyWay.ino](https://github.com/ukkz/skyway-web-real-time-christmastree/blob/master/NefryXmasSkyWay/NefryXmasSkyWay.ino)


あとはこちらのページを2台のPCで表示して、クリスマスツリーとの対話を楽しんでください。
[GitHub Pages](https://ukkz.github.io/skyway-web-real-time-christmastree)


## まとめ

- クリスマスツリーはWebでリアルタイムになれる！！！！！（？）
- WebRTC畑の方も、SkyWayな方も、ぜひマイコンボードとかWebBluetoothにも触れてみてください。
- IoT勢のみなさんも、SkyWayがなかなか簡単で楽しいので、ハードウェア監視などにいかがでしょうか。
- ここまで読んでいただき非常にメリークリスマスです🍖🍺よいお年をお過ごし下さいませ🎍