SkyWayとRaspPiで遠隔操作できるカメラを作ろう

## はじめに

ペットカメラを作ったけど、フレームにペットが映ってない！
カメラを動かしたい！

「カメラ、あとちょっと左向いて！」

ということがありました。

**そう思ったら、カメラを動くようにするのがエンジニアです。**

![camera_pan.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141013/f1cecd6b-e646-87b5-5f94-34e6c0c17057.gif)

このような構成になります。

![Screenshot 2023-07-07 at 16.04.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141013/a27014b9-76ad-6386-00ad-36fea3c8487d.png)


なお、この記事は以下の記事で開発したソフトをベースにしていますので、見ていない方はこちらを先に見ていただくとわかりやすいかと思います。

https://qiita.com/komasayuki/items/1caef300977efa5d5981

この記事では以下のような流れで説明していきます。

1. 全体像
2. Raspberry Piの配線
3. SkyWayとの通信
4. pet-cam-piの説明
5. 実行方法

## 1. 全体像

デバイスやスマホ、クラウドは、下の図のように通信します。

![Screenshot 2023-07-07 at 16.39.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141013/2b882db3-ccf1-a1eb-3248-8e2c27cd3c8b.png)

### 自宅側

自宅側には、Raspberry Piとサーボがあります。
Raspberry Piでは、node.jsプログラムとしてpet-cam-piサーバーが動いています。

このpet-cam-piサーバーはhttpサーバーになっており、Raspberry Pi内部のブラウザにペットカメラのWebページを公開します。また、RESTサーバーとして、サーボ操作APIを提供します。

SkyWayからサーボ角度がブラウザに届くので、ブラウザのjavascriptはサーボ角度を受け取ったら、pet-cam-piにRESTでPOSTしてサーボ角度を設定します。

```shell
# サーボ操作APIをcurlから行う例、angleはdegreeではなく、サーボ上の角度設定範囲の0%~100%を意味する
curl -X POST -H 'Content-Type: application/json' -d '{"angle":10}' http://localhost:8080/servo
```




### 外出先
外出先では、スマホからGitHub Pagesのペットカメラ用のWebサイトを開きます。
スマホでは、SkyWayから送信される動画/音声を表示します。

SkyWay経由でサーボ角度をRaspberry Piのブラウザに配信します。


## 2. Raspberry Piの配線

Raspberry Piとサーボモーターは以下のように配線します。
Piの電源で動く小さめのサーボを使いましょう。

![Screenshot 2023-07-07 at 16.18.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141013/b9eac769-19a8-1279-c49c-e2b07d71802e.png)

Pi 2pin (5V Power) -> Servo VCC
Pi 6pin (Ground) -> Servo GND
Pi 12pin (GPIO 18) -> Servo SIGNAL

Raspberry PiからはGPIOを使って、サーボのDuty比を操作することで、サーボモーターの上に乗っているUSBカメラの角度を制御します。

### 3. SkyWayとの通信

[前記事](https://qiita.com/komasayuki/items/1caef300977efa5d5981)のインターネットペットカメラでは、Streamとして

- VideoStream (ビデオ映像)
- AudioStream (オーディオ音声、マイク音声)

のみを使っていましたが、今回はサーボ角度の送信のために

- DataStream （任意のメッセージ、P2P Roomでのみ使える）

を追加で使います。

### Dataの送信

Dataを送信するためには、まず以下のようにDataStreamを準備します。

```typescript
const me = await channel.join();

const data = await SkyWayStreamFactory.createDataStream();
await me.publish(data);
```

実際に送信するには

```typescript
data.write(JSON.stringify({angle: servoAngle}));
```

のようにすることでデータを送ることができます。

### Dataの受信

Dataを受信するためには、以下のようにSubscribeします。

```typescript
const subscribeAndAttach = async (publication) => {
  if (publication.publisher.id === me.id) return;

  const { stream } = await me.subscribe(publication.id);

  switch (stream.contentType) {
    case 'data':
      {
        stream.onData.add(async (data) => {
          const obj = JSON.parse(data as string);
          const angle:number = obj.angle;
          console.log('angle:' + angle);
        });
      }
      break;
  }
};

channel.publications.forEach(subscribeAndAttach);
channel.onStreamPublished.add((e) => subscribeAndAttach(e.publication));
```

[SkyWayのJavaScript SDK](https://skyway.ntt.com/ja/docs/user-guide/javascript-sdk/overview/)を使って、簡単にメッセージ送受信機能を実装することができます。


## 4. pet-cam-piの説明

pet-cam-piではhttpサーバーとしてexpressを利用しています。

expressでは以下の処理で、publicフォルダのペットカメラWebコンテンツを公開できます。

```typescript
app.use('/', express.static('public'));
```


そして、サーボ角度が`/servo`にPOSTされたら、以下のようにサーボ角度を設定します。

```typescript
app.post('/servo', async (req, res) => {

    let anglePercentage:number = req.body.angle;

    if (anglePercentage === undefined) {
        res.status(400).send('angle is undefined');
        return;
    }
    currentAngle = Math.max(0, Math.min(100, anglePercentage));
    setServoAngleByPercentage(currentAngle);
    
    res.status(200).send('ok');
});
```

実際のサーボ操作は、[pigpio](https://www.npmjs.com/package/pigpio)を使っており、以下のような感じになっています。

```typescript
import {Gpio} from 'pigpio';

const motor = new Gpio(18, {mode: Gpio.OUTPUT});

const PulseWidthMax = 2500;
const PulseWidthMin = 500;

const setServoAngleByPercentage = (anglePercentage:number) => {
    const pulseWidth = ~~(PulseWidthMin + (PulseWidthMax - PulseWidthMin) * anglePercentage / 100);
    console.log('set servo angle: ' + anglePercentage + ' pulse width: ' + pulseWidth);
    motor.servoWrite(pulseWidth);
};
```

ここで`PulseWidthMax`や`PulseWidthMin`は利用しているサーボのパルス幅で、サーボごとに変更してください。私が使ったサーボの場合はパルス幅0.5ms-2.5msの範囲で、サーボ角度0°-270°の制御ができます。





## 5. 実行方法

pet-cam-piのリポジトリを公開しています。

https://github.com/komasayuki/pet-cam-pi

Raspberry piにログインして、


```bash
# clone pet-cam repository from GitHub
% git clone https://github.com/komasayuki/pet-cam-pi

% cd pet-cam-pi/server

# first time only
% npm install

# pet-cam-piサーバーを起動、8080ポートで待ち受け
% sudo npm start
```

これでpet-cam-piサーバーが起動できました。


### 自宅カメラ側の起動

Raspberry Piのpet-cam-piが起動している状態で、ブラウザで
[カメラ用URL](http://localhost:8080/index.html?camera=true&audio=true)
を開いて、SkyWayで取得した、あなたの`アプリケーションID`と`シークレットキー`を入力します。

（まだ取得していなければ、[SkyWayのサイト](https://skyway.ntt.com/ja/)から無料で作れます）

これで自宅カメラ側の準備が完了です。


### 外出先でペットカメラを見る

外出先のスマホからは
[ビューワー用URL](https://komasayuki.github.io/pet-cam-pi/index.html?camera=false)
を開いて、あなたの`アプリケーションID`と`シークレットキー`を入力してください。

あなたの大好きなペットは映りましたか？
サーボの角度も変えてみましょう！



## 最後に

[SkyWayのJavaScript SDK](https://skyway.ntt.com/ja/docs/user-guide/javascript-sdk/overview/)は、とても良くできていて、[前記事](https://qiita.com/komasayuki/items/1caef300977efa5d5981)のインターネットペットカメラから、簡単にサーボ操作機能を追加することができました。

双方向通信が可能になるDateStreamは様々な用途に使えそうですね。

![Screenshot 2023-07-07 at 19.49.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141013/88d7328e-dc74-22e7-52dd-be0a3b68c74a.png)