ラズパイでWALL-E（すごいラジコン）のリモコン信号をゼロから実装して遊ぶ


https://github.com/gitnabeshin/UCommand_WALL-E

# はじめに
* 電子工作を始めた時、ほぼ全ての方が試してみるデバイスの中に赤外線LEDを使った家電リモコン制御があります。
 * 動かせるものは、テレビ、蛍光灯、扇風機、エアコン、いろいろありますね。
 * 最近はESP32使ってWifiリモコンにしたり、ALEXAで音声制御したり、いろいろ出来ちゃいます。
* 自分も調子に乗っていろんなものを動かしている中、ネットで数年前に「U-Command WALL-E」という素敵なラジコンを発見し入手しました。
 * しかし、後述の通り、こちら教科書に記載されているようなリモコン信号をスキャンして送ると言うやり方が通用しません。
 * 「う〜ん、、、ここまでか。」と一瞬、諦めかけましたが、偶然にもこのリモコン信号をハックしたデータを海外の掲示板で発見し、そこから一気に大逆転！！
 * RaspBerryPiでフルスクラッチでコードを書き、自作リモコン回路を使ってWALL-Eを動かせるようになりました。
* と言う事で、これら一連の経緯のまとめです。

# U-Command WALL-E とは

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

* 2008年に公開されたPixer映画のWALL-Eをリモコンで動かせるおもちゃです。
 * リモコンにいくつもボタンが付いていて、前進、回転、おしゃべり、ダンス、機械音など10種類のコマンドが動かせます。
 * これらのコマンドを自由にプログラムして（並べて順番に）WALL-Eを動かすことができます。（1000通り以上の組み合わせ）
 * サイズも大きく、両腕、頭が動き、眼のLEDが光ったりと再現度も高くて頑丈なのでフィギュアとして飾っても満足できる仕上がりです。
 * 10年以上前の製品ですが、こんな凄いのがあったなんて知らなかった。。

![IMG_0086.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/1c6ac25a-39d9-934b-956a-be72c580bb8a.jpeg)


# ネットで中古品をゲットする。
* 言うまでもなく、こちら絶版品です。
 * 未使用状態のものはプレミアム価格で10万円で出てたりしますが、それはガチのマニア向けで手が出ない。。。
 * ちなみに販売当時は1万円くらいで売られていた様です。

## しっかり遊ばれていた**「リモコン無しの中古品」**を狙うのがオススメ。
* 当時の幸せな子供がこれを買って貰い、しっかり遊ばれていたものが狙い目です。
 * しかもリモコンが無かったりするとオモチャとしての価値は無いので、3000円くらいで買えます（中古なので売り手に寄りますが）。
 * またキズや汚れについては、逆にそれは原作の雰囲気が再現されるので問題ないかと。
* 自分はヤフオク、メルカリで半年ほど探し続け、数年前にリモコン無しの中古品を3000円でゲットしました。
 * リモコンが無いので、純粋にフィギュアとしての価格です。
 * 分解して分かりましたが、メカ部分、部材、着色、どれを取っても本当に良く出来てます。

# 赤外線リモコンを自作する。

* ブツが手に入ったら、あとはリモコンの制作です。
 * こちらは教科書通りのごく普通の赤外線LED回路になります。
 * おもちゃの場合、家電と違って制御対象が動くので、LEDを3個広げて配置し、広い範囲で信号が飛ぶ形にします。
 * fritzingの回路図には赤外線受信モジュールも記載していますが、今回は使いません。
* パーツは秋月で購入しました。
 * [5mm赤外線LED OSI5FU511C(5個入り)](https://akizukidenshi.com/catalog/g/gI-03261/) ¥100
 * [トランジスタ 2SC1815(20個入り) ](https://akizukidenshi.com/catalog/g/gI-13491/) ¥100
 * ~~赤外線リモコン受信モジュール~~ 今回の用途では使えない

![IMG_0389.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/4c5cbbc5-63fb-a548-264b-f4bf853c5181.png)


# リモコン信号がコピーできない。

　家電のリモコン信号についてはWEB記載の教科書を読んでいたので、素直な自分は同じくWALL-Eのリモコン信号をスキャンしました。
が、何回やっても動きません。
そもそも、受信機で信号を受けてもまともな波形になりません。
と言っても、我が家に計測器なんて物はないので、ネットで調べまくりました。

* 普通、家電のリモコン信号は38kHzの周波数で構成されている。
* 秋月で購入できる赤外線受信モジュールはどれも38kHz仕様である。
 * これで遊んでいてテレビが一緒についたり消えたりしたら、子供は安心して遊べないので、おもちゃのリモコンはこの周波数を避けている可能性が高い。

と言う事で、きっと原因はこれです。
そして、つまり、自分の持ってる機材では信号をコピーできない。

もうここまでか・・・・。

# 海外の掲示板でリモコン信号を発見！！
* １週間何も進展が無く、ほぼ諦めていました。
* 最後に、これを試したらやめようと思っていた１つの可能性を試してみました。
 * 「WALL-EってPixerだよな。これって世界中で売られてたんだよな。世界にはめちゃめちゃマニアが居るよな？」
 * 「こんな素敵なおもちゃなら、信号ハックとかした人居るんじゃないか？」
* って事で、調べ方を変えたら、、、ビンゴ！！！
 * 海外の掲示板でこれを発見！ [JP1 Remotes: Automated analysis of IR protocols](http://www.hifi-remote.com/forums/viewtopic.php?t=12137)
 * 後にも先にもここ以外の情報源は見つからない。よく見つけたな、じぶん。。。
* この掲示板によると、こう言う事らしい。
 * このリモコン信号は44kHz。
 * 広い周波数の信号を解析できる[USB-UIRT (Universal Infrared Receiver/Transmitter)](http://www.usbuirt.com/overview.htm) なるデバイスを使用。
 * リモコンのボタンを1回押すと、それぞれのコマンドで以下の信号を3回連続して送信
 * 各コマンドのLeadoutのOFF区間の長さは不明

```
Freq:    44 kHz
 1:       +1500 -500
 0:       +500  -1500
 Leadin:  +6000 -2000
 Leadout: +2000 -[enough long]
 Fixed data:    4 bits
 Variable data: 8 bits (repeated twice)
 ------------------------------------------------
 1: Joystick Forward
 +6000 -2000 1010 11111110 11111110 +2000 -46500
 
 2: Joystick Backward
 +6000 -2000 1010 11111101 11111101 +2000 -46500
 
 3: Ahead Button
 +6000 -2000 1010 10101110 10101110 +2000 -87000
 
 4: Turn Button (once only)
 +6000 -2000 1010 10101100 10101100 +2000 -?????
 
 5: Rotate Button
 +6000 -2000 1010 10101101 10101101 +2000 -64000
 
 6: Circle Button
 +6000 -2000 1010 10101011 10101011 +2000 -123500
 
 7: Light Button
 +6000 -2000 1010 10100100 10100100 +2000 -59000
 
 8: Music Button
 +6000 -2000 1010 10100110 10100110 +2000 -79500
 
 9: Dance Button
 +6000 -2000 1010 10100101 10100101 +2000 -86000
 
 10: Eyes Button
 +6000 -2000 1010 10100111 10100111 +2000 -68000
 
 11: Talk Button
 +6000 -2000 1010 10101010 10101010 +2000 -68000
 
 12: Mecha Button
 +6000 -2000 1010 10101001 10101001 +2000 -68000
```

* で、上記は多分、こう言う意味だと思われます。 
 * U-Command WALL-E リモコンの周波数は44kHz
 * 信号の開始は、6000(usec) HIGHで、2000(usec) LOW
 * 次に`1010`の4bit固定データを送信
 * 次に`11111110 11111110`みたいに8bitのデータを２つ送信
 * 信号の終了は、2000(usec) HIGHで、十分な区間 LOW
 * 上記`1`は、1500(usec)HIGH, 500(usec)LOW
 * 上記`0`は、500(usec)HIGH, 1500(usec)LOW
* これに家電リモコン信号で勉強した知識を加えます。
 * 信号でHIGHになってる部分については、電力消費を抑えるために`Duty比を1/3`にする。
 * 要は、LEDを光らせるために電気通す時間を1/3に減らす。（減らしても信号的に影響がない）


![IMG_0092.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/6b5f515a-35e4-b7fb-7865-3ab050db35e6.jpeg)

## コードを実装
* ここまで分かったので、後はコードを書いてみます。
 * https://github.com/gitnabeshin/UCommand_WALL-E

```c:walle_control.c
/**************************************************************
 //  walle_control.c
 //  IR Control for WALL-E by Raspberry Pi3 with wiringPI
 //
 //  Created by Nabeshin on 2017/05/25.
 **************************************************************/

/*
 * gcc ./walle_control.c -o ./walle_control.out -lwiringPi
 */

#include <wiringPi.h>
#include <stdio.h>      // for printf()

#define PIN_IRLED        (23)    // 赤外線LEDピン (物理PIN 16)

#define COSTOM_CODE      "1010"

#define JOYSTICK_FORWARD  "1111111011111110"
#define JOYSTICK_BACKWARD "1111110111111101"
#define BUTTON_AHEAD      "1010111010101110"
#define BUTTON_TURN       "1010110010101100"
#define BUTTON_ROTATE     "1010110110101101"
#define BUTTON_CIRCLE     "1010101110101011"
#define BUTTON_LIGHT      "1010010010100100"
#define BUTTON_DANCE      "1010011010100110"
#define BUTTON_MUSIC      "1010010110100101"
#define BUTTON_EYES       "1010011110100111"
#define BUTTON_TALK       "1010101010101010"
#define BUTTON_MECHA      "1010100110101001"

#define MENU_QUIT         "0"


/*
 Turn LED ON [num] times @44kHz, 1/3Duty
 */
void send_on(int num){
    int i=0;
    for(i = 0; i < num; i++){
        
        /*
         44kHz -> 1/44*1000(sec/clock) -> 23(usec/clock)
         1/3Duty -> ON:23*1/3=8(usec/clock), OFF:23*2/3=15(usec/clock)
         */
        digitalWrite( PIN_IRLED, 1 );  // ON
        delayMicroseconds(8);          // wait 8 usec
        digitalWrite( PIN_IRLED, 0 );  // OFF
        delayMicroseconds(15);         // wait 15 usec
    }
}

/*
 Send bit code
 1: +1500 -500
 0: +500 -1500
 */
void send_bytes(char* bytes, int byte_length){
    char* byteData = bytes;
    int i=0;
    for (i = 0; i < byte_length; i++){
        if('1' == byteData[i]){
            send_on(65);              // ON (blink 1500/23 = 22 times)
            delayMicroseconds(500);   // wait 500 usec
        } else {
            send_on(22);              // ON (blink 500/23 = 22 times)
            delayMicroseconds(1500);  // wait 1500 usec
        }
    }
}


/*
 Send Command
 */
void send_command(char* command){
    
    /* LEADER CODE signal(+6000, -2000) usec  */
    send_on(264);             // ON (blink 6000/23 = 264 times)
    delayMicroseconds(2000);  // OFF
    
    /*
     fixed data: 4 bits
     variable data: 8 bits (repeated twice)
     */
    send_bytes(COSTOM_CODE, 4);   // fix code (Fixed Size 4bit)
    send_bytes(command, 16);      // variable command(Fixed Size 16bit)
    
    /* STOP CODE signal(+2000, -****) usec */
    send_on(87);               // ON (blink 2000/23 = 87 time)
    delayMicroseconds(123500); // OFF for enough time
}

/*
 Exec Command
 */
void exec_command(int command){

    /*
     char* menuStr = "MENU \n"
     " 1: BUTTON_AHEAD\n"
     " 2: BUTTON_LIGHT\n"
     " 3: BUTTON_TURN\n"
     " 4: BUTTON_ROTATE\n"
     " 5: BUTTON_CIRCLE\n"
     " 6: BUTTON_DANCE\n"
     " 7: BUTTON_MUSIC\n"
     " 8: BUTTON_EYES\n"
     " 9: BUTTON_TALK\n"
     "10: BUTTON_MECHA\n"
     "11: JOYSTICK_FORWARD\n"
     "12: JOYSTICK_BACKWARD\n"
     " 0: QUIT\n"
     "INPUT COMMAND>";
     */
    
    switch (command) {
        case 1:
            send_command( BUTTON_LIGHT );
            send_command( BUTTON_LIGHT );
            send_command( BUTTON_LIGHT );
            delayMicroseconds(1523500); // OFF for enough time
            break;
        case 2:
            send_command( BUTTON_AHEAD );
            send_command( BUTTON_AHEAD );
            send_command( BUTTON_AHEAD );
            delayMicroseconds(1523500); // OFF for enough time
            break;
        case 3:
            send_command( BUTTON_TURN );
            send_command( BUTTON_TURN );
            send_command( BUTTON_TURN );
            delayMicroseconds(6123500); // OFF for enough time
            break;
        case 4:
            send_command( BUTTON_ROTATE );
            send_command( BUTTON_ROTATE );
            send_command( BUTTON_ROTATE );
            delayMicroseconds(6123500); // OFF for enough time
            break;
        case 5:
            send_command( BUTTON_CIRCLE );
            send_command( BUTTON_CIRCLE );
            send_command( BUTTON_CIRCLE );
            delayMicroseconds(6123500); // OFF for enough time
            break;
        case 6:
            send_command( BUTTON_MUSIC );
            send_command( BUTTON_MUSIC );
            send_command( BUTTON_MUSIC );
            delayMicroseconds(12123500); // OFF for enough time
            break;
        case 7:
            send_command( BUTTON_DANCE );
            send_command( BUTTON_DANCE );
            send_command( BUTTON_DANCE );
            delayMicroseconds(12123500); // OFF for enough time
            break;
        case 8:
            send_command( BUTTON_EYES );
            send_command( BUTTON_EYES );
            send_command( BUTTON_EYES );
            delayMicroseconds(6123500); // OFF for enough time
            break;
        case 9:
            send_command( BUTTON_TALK );
            send_command( BUTTON_TALK );
            send_command( BUTTON_TALK );
            delayMicroseconds(12123500); // OFF for enough time
            break;
        case 10:
            send_command( BUTTON_MECHA );
            send_command( BUTTON_MECHA );
            send_command( BUTTON_MECHA );
            delayMicroseconds(6123500); // OFF for enough time
            break;
        default:
            printf("NO COMMAND....\n");
            break;
    }
}

/*
 setup Wiring PI
 */
void setup(void){
    
    // Set up Wiring Pi
    wiringPiSetupGpio();
    // Set GPIO as Output
    pinMode( PIN_IRLED, OUTPUT );
}

/**
 * Main
 */
int main(int argc, const char * argv[]) {
    
    // Setup wiring Pi
    setup();

    char* menuStr = "ウォーリー　そうさメニュー \n"
    " 1: スイッチオン\n"
    " 2: 前に進む\n"
    " 3: 左に曲がる\n"
    " 4: 後ろを向く\n"
    " 5: ぐるっと１回転\n"
    " 6: いろんな音\n"
    " 7: ダンス\n"
    " 8: 起きる\n"
    " 9: しゃべる\n"
    "10: ロボットの音\n"
    " 0: 終わり\n"
    "番号を選んでください>";
    
    int command;
    
    while (1){
        printf("%s", menuStr);
        scanf("%d", &command);
        
        if(0 == command){
            break;
        }
        exec_command(command);
    }
    
    printf("bye.....\n");
    
    return 0;
}
```

# コンパイル＆実行

```
> sudo apt install wiringpi
> gcc ./walle_control.c -o ./walle_control.out -lwiringPi
> ./walle_control.out
```

* 以下で無事にリモコン信号が送れ、WALL-Eを動かす事ができました。

![IMG_0120.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/68c4e02d-5df3-c319-a12a-cd929a0f0894.jpeg)



# WALL-EをIOT化して音声コマンドで動かす。

もうここまで来たらこっちの物です。

こちらの記事を参考に、
　「[ラズパイ(Raspberry Pi)で家電をコントロールする:HomeBridgeの使い方](https://www.apollomaniacs.com/ipod/howto_homekit_raspberrypi.htm)」
ラズパイにHomeBridgeをインストールして、iPhoneのSiriから音声でWALL-Eを動かす事ができました。

![IMG_0601.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/b832a382-6856-1ed3-6cca-52bc7e99cf7a.jpeg)

![IMG_0600.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/933f5b71-38c8-dff0-e0e8-7244e9d45732.png)

![IMG_0602.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/806c3a25-8e1a-02f8-5daf-197d1c0030e3.png)

# まとめ
* U-Command WALL-Eを格安で手に入れ、ラズパイを使ったリモコン制御に成功しました。
 * リモコン信号の周波数が44kHzと特殊で信号を読み取る事が出来ませんでしたが、幸運にも海外の掲示板で信号コードを発見しました。
 * LEDを直接光らせて信号を作り出すコードを書いてみる事で、リモコン制御信号の理解を深める事が出来ました。
* U-Command WALL-E、10年以上前のおもちゃですが、作りが素晴らしい。
 * リモコンが壊れたり、無くしてしまっても大丈夫！こんな感じで復活出来ちゃいますよ！