家にあるリモコンでWindowsの動画再生制御してみる(実験編)


# 製作に至るきっかけ

最近ChromeでYoutubeを見ていると、右上の音符マーク?のところで、再生を制御できることに気が付きました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1015969/3094be1b-feda-1b72-c878-6eec1aa1737a.png)



これ、もしかするとWindowsの再生ボタン機能を送信できるデバイスがあれば、再生・一時停止などできるのでは。。？と思い、以前から作りたいと思っていた、USB HIDデバイスによる再生・停止・音量調整などのコマンドを送信するデバイスの製作にチャレンジしてみてました。

# コンセプト
あまり大掛かりなものにするつもりはないので、PICのUSB機能を利用して作ることにしました。
デバイスのイメージとしてはこんな感じのものです。

* 家にあるような普通のリモコンでWindowsの再生・停止・音量調整などを行うUSBデバイスです。
* USB HID Cosumer page deviceとして製作しますので、Windows側にアプリやドライバを作る必要はなく、OSの機能で動作します。
* リモコンのデータを受信して、USB HIDに再生・停止・音量調整などのコマンドをUSB HIDデバイスとして発します。
* USBの利用できるPICとして、秋月電子の「ＰＩＣ１８Ｆ１４Ｋ５０使用ＵＳＢ対応超小型マイコンボード」を利用して試作します。


# 回路図

回路図にするほどのものでもないので、省略しますが、ＰＩＣ１８Ｆ１４Ｋ５０使用ＵＳＢ対応超小型マイコンボード
に対して、以下の部品の接続をしたものです。
* RC5 .. 赤外線リモコン受信用素子 VS1838B
* RB6 .. 470Ω + 緑色LED(電源ON確認用)
* RB7 .. 470Ω + 赤色LED(IR受信時用)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1015969/b8752565-8553-c586-627b-c18e9f33dcd5.png)


# 使用したリモコン

近所のWattmanからジャンクのリモコンを入手しました。
Sonyのビデオカメラ用でしょうか、小ぶりで気に入りました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1015969/c2943230-d3dc-5690-6c2b-323848989e3b.png)



# ソフトウエア

砂町技研さんのページの手順でMicroChipのサンプルを元にして、個別のプロジェクトを作成します。
この段階でUSB HIDデバイスとしての大枠ができました、ここに望み通りの動作となるようにUSB HIDデバイスとしてのレポートディスクリプタを設定することで、実現する機能を設定します。

今回このデバイスで利用する機能は以下としました。Youtubeの制御では、再生・一時停止しか利用できないようですが、Windows10の動画再生では、このすべての機能が利用できます。

* ボリュームUP
* ボリュームDOWN
* ミュート
* 再生・一時停止
* 次のトラックへ移動(30秒先へ移動)
* 前のトラックへ戻る(10秒前に戻る)

## レポートディスクリプタ
USB HIDの定義書から、これらのデバイスのUsage IDを入手します。15 Consumer Page (0x0C)の一覧表の部分です。
いろいろ試してみるとどうやらこれで目的の動作になりそうです。

| 機能 | Usage ID | 
|:-----------|:------------:|
|ボリュームUP | 0xE9       | 
|ボリュームDOWN | 0xEA      | 
|ミュート| 0xE2 |
| 再生・一時停止 | 0xCD         | 
|次のトラックへ移動| 0xB5      | 
|前のトラックへ戻る| 0xB6     | 

このIDを元に、以下のようにレポートディスクリプタを設定してみました。


```usb_descriptors.c
//Class specific descriptor - HID 
const struct{uint8_t report[HID_RPT01_SIZE];}hid_rpt01={
{
    0x05, 0x0c,                    // USAGE_PAGE (Consumer Devices)
    0x09, 0x01,                    // USAGE (Consumer Control)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x09, 0xe9,                    //   USAGE (Volume Up  RTC)
    0x09, 0xea,                    //   USAGE (Volume Down RTC)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x95, 0x02,                    //   REPORT_COUNT (2)
    0x81, 0x06,                    //   INPUT (Data,Var,Rel)
    0x09, 0xe2,                    //   USAGE (Mute OOC)
    0x09, 0xcd,                    //   Usage(Play/Pause OSC)
    0x09, 0xb5,                    //   Usage(Scan Next Track OSC)　
    0x09, 0xb6,                    //   Usage(Scan Previous Track OSC)
    0x95, 0x04,                    //   REPORT_COUNT (4)
    0x81, 0x06,                    //   INPUT (Data,Var,Rel)
    0x95, 0x02,                    //   REPORT_COUNT (2)
    0x81, 0x07,                    //   INPUT (Cnst,Var,Rel)
    0xc0                           // END_COLLECTION
}
};       
```

この定義により、送信するデータの各ビットの意味は以下のようになります。
上から定義されたボタンが右から割当たる感じですね。
各ビットを1にしてホストに送信すると、これらの定義されたコマンドがPC側で実行されることになります。



| ビット |Bit7|Bit6|Bit5|Bit4|Bit3|Bit2|Bit1|Bit0|  
|:-----------|:------------:|:------------:|:------------:|:------------:|:------------:|:------------:|:------------:|:------------:|
|割り当て | N/A| N/A|Scan Previous Track|Scan Next Track|Play/Pause|Mute|Volume Down|Volume Up| 

Usageによるようですが、例えばVolume upは一度1を送信すると、そのまま繰り返しVolume upが押され続けるようになるので、一度送付した後に、0x00を再度送付するようにしています。

## 赤外線受信部

赤外線リモコンのデータはロジアナで事前に解析し、バイナリデータとして準備しておきます。
```main.c

unsigned int ir_data[IRDATANUM] = 
{
    0b010110010011, //volume up
    0b110110010011, //volume down
    0b001100110011, //mute
    0b010110011100, //play pause
    0b001110011100, //Scan Next Track OSC
    0b110110011100  //Scan Previous Track OSC
};     
```

受信するルーチンはこのようになります。処理中に割り込みが入るとタイミングが狂うため、処理中は割り込みを無効にします。
デバッグ用にRC4に解析した0か1のデータを出力するようにしています。
```main.c
        TMR1ON=1;       //Timer1開始
        while(RC5==1);  // wait until leader start
        TMR1=0;         // Reset TMR1
        while(RC5==0);  // wait until leader end
        if (TMR1>=IR_LEADER)    // if frame is longer than leader value.
        { 
            INTCONbits.GIE=0;   // Disable interrupt to handle IR correctly.
            LATBbits.LATB7=1;   // IR受信している間光る
            for (j=IRDIGITS-1;j>=0;j--) { // for bit 0 to 11
                while(RC5==1);  //wait until data bit change to 0
                TMR1=0;         //Reset TMR1
                while(RC5==0);  //wait until data bit chnage to 1
                if (TMR1>=IR_ONE)  // if frame is longer tha 1 value.
                { 
                    rcv_data = rcv_data | (0b00000001 << j);
                    RC4=1;
                }
                    else {
                    RC4=0;
                }
            }
            LATBbits.LATB7=0;   // IR受信完了したのでOFFにする。
            INTCONbits.GIE=1;   //Enable interrupt again.
            TMR1ON=0; // stop timer1
            // Check if data match with ir_data and set data value
            
            for (j=0;j<IRDATANUM;j++)
            {
                if(rcv_data ==ir_data[j]) 
                {
                    SendData[0] = (0b00000001 << j); 
                    return true;
                    break;
                }
            }
         }
```


## USB送信部

Check_IRでtrueが帰ってきたらUSBにデータを送信します。
その後100ms後に0x00を送信します。
ループ毎に50msの待ちもいれていますが、この100ms、50msにはあまり根拠はなく、試してみた結果、動作が安定した値です。


```main.c
        SYSTEM_Tasks();
        //if (Check_Button()) {
        if (Check_IR()) {
            Read_Write_USB();
            __delay_ms(100) ;
            SendData[0]=0;
            Read_Write_USB();
        }
        __delay_ms(50) ;
```



# 動作確認

まだ動作不安定な部分はありますが、それなりに想定の動作をしてくれています。
家にあるリモコンでWindows 10の動画再生を制御できています。


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


このままでは利用しずらいので、その後[小型プリント基板で製作しました](https://qiita.com/keitanak/items/97cacd198faa40c52b51 "家にあるリモコンでWindowsの動画再生制御してみる(製作編)")




# 参考リンク

もともとこのコンセプトのデバイスを作られていた方の記事です。HIDデバイスのConsumer pageのことなどが非常に勉強になります。
[PICでなんか作るばい](http://tylercsf.blog123.fc2.com/blog-category-30.html "PICでなんか作るばい")

MicroChip社のサンプルから、個別のプロジェクトにするための方法などの手順があり、こちらも参考になりました。
[PIC18F14K50 で USB HID デバイスを作る](https://krr910183393.wordpress.com/2021/01/17/pic18f14k50-usb-hid-device/ "PIC18F14K50 で USB HID デバイスを作る")

PICでなんか作るばいの方からリンクされていた海外の方のページです。同じようなデバイスの製作をされています。
[USB volume control with a PIC18F4550](http://kentie.net/article/usbvolume/index.htm "USB volume control with a PIC18F4550")

USBのHIDデバイスの定義書です。結構読むのは大変ですが、レポートディスクリプタ定義の部分はここを理解することが重要です。
[HID Usage Tables](https://www.usb.org/sites/default/files/hut1_12.pdf "HID Usage Tables")

赤外線リモコンの通信フォーマットに関してはこちらのページを参考にしました。
[赤外線リモコンの通信フォーマット](http://elm-chan.org/docs/ir_format.html "赤外線リモコンの通信フォーマット")