🌏

電子工作 その19(赤外線リモコン受信モジュール:VS1838Bの使い方)

2024/01/03に公開

こんにちは、Ideagearの鈴木陽介です。

2024年一発目の記事は、安価な赤外線リモコン受信モジュールであるVS1838Bの使い方についてご説明します。

はじめに

なぜ赤外線センサーを試そうと思ったか?

このように思ったキッカケは、遠隔通信・指示の方法として、無線通信、特にBluetoothやWi-Fiを通じたスマホアプリからの操作にこだわりすぎていることに気づいたからです。もっと端的に言えば、リモコンでいいんじゃないかと。

たとえば、デジペをレストランやお店の店頭に置いたとして、単に「いらっしゃいませ」や「ご来店ありがとうございます。」などのいくつかの決まったフレーズをしゃべらせるだけであれば、リモコンのみならず、レストランなどでよく見られる呼び出しベルのようなものでも十分です。

他方で、今時は誰でもBluetooth内蔵のスマホを持っていますし、たとえWi-Fi環境に居なくても、SIMカードのデータローミングで常時ネットにつながっています。

ですので、ほとんどの方にとってスマホを使うことが一番便利だと思い込んでいました。

ところが、スマホを使ったオペレーションをよくよく考えてみると、まずスマホのロックを解除し、ペアリングしたり、Wi-Fiにつなげたりするのも手間がかかります。また、いくらスマホアプリのUIをシンプルにしても、操作にも慣れる必要があります。

このように考えると、スマホを介すと高度化せざるを得ませんし、使う部品も多くなって部材費もかかります。また、技適などの法規制も気にしなければなりませんし、BluetoothだとさらにBluetooth SIGという組織にお金を払わなければなりません。つまり、開発費が爆上がりしてしまうわけです。

正直、デジペが最終的にどのようになっていくかまだ流動的ですが、少なくとも、おもちゃはおもちゃ、ロボットはロボットとしてハッキリ分けた方が良いと考えています。

同時に、ぬいぐるみのおもちゃ路線かロボット路線かという議論の前に、そもそもIP(Intellectual Property:知的財産)として確立させる方がより重要です。つまり、キャラクターをつくり、それを普及させることの方がより重要だという当たり前のことに、ここ数ヶ月間の試行錯誤の結果気づきました。

そして、キャラの普及が最優先事項だとした場合、ファーストステップとしては、まずはおもちゃから始めるのが適切です。そして、おもちゃであれば、まずは機能を限定し、できる限りシンプルにしてコストを抑え、より多くの方に使っていただくのが重要だと思い至りました。

余談ですが、実は、今回私は10年ぶりに年末年始フルで日本にいたため、年末年始イベントに飢えていましたw

そこで、ファブラボ浜松、TAKE-SPACEで年末に開催されたインドネシア人アーティストによるペインティングイベントや餅つきイベントに参加し、様々な刺激を受け、いろんな方とおしゃべりしている中で今回のことに気づくことができました。

前置きが長くなりましたが、できる限り早く、レストラン、カフェ、お店の店頭、ご家庭などさまざまなシチュエーションで使っていただけるようにしたいと思っています。

参考記事

https://burariweb.info/electronic-work/arduino-learning/arduino-ir-module-use.html

https://shinog.jp/computer/arduino/赤外線受信モジュールを使ってみる(ir-ledで送信も/

配線

VS1838BのVCCには5Vを供給してください。

Arduino Uno VS1838B
5V VCC
GND GND
デジタル11番ピン OUT

センサー側の実際の配線は下記の通りです。

なお、今回のセンサーはAmazonの下記ページから購入しました。
データシートなどの詳細は下記リンクよりご確認ください。
https://www.amazon.co.jp/オーディオファン-赤外線センサー-受信モジュール-Arduino-8個セット/dp/B07TNDLH86/ref=cm_cr_arp_d_product_top?ie=UTF8

ライブラリー

スケッチ > ライブラリーをインクルード > ライブラリーを管理 > ライブラリマネージャ
から、「IRremote」というライブラリーをインクルードしてください。

ライブラリーインクルード時の注意点

ライブラリーのバージョンは最新版ではダメです。
以下は、各バージョンとそのサンプルスケッチ名の比較したものです。

今回は、上記の通り、最終的に以下のバージョンを使うことで落ち着きました。

ライブラリVer:2.6.1
サンプルスケッチ名:IRrecvDumpV2

詳細は後述しますが、これ以外のバージョンでは上手くいきませんでした。

まぁ私が購入したVS1838Bの特有の問題かもしれませんが、Arduinoでも最新版のArduino IDE(統合開発環境)を使うと上手くいかない場合が多いですし、どのようなマイコンにせよ、センサーにせよ、たとえソフトはどんどん更新されてもハードがそれに追いつかないことがほとんどです。

したがって、ここは最新版にこだわらず、使えることを優先して使えるバージョンを使いましょう。

ソースコード

VS1838Bに必要なライブラリーは先程インクルードしました。
次に、サンプルスケッチをそのまま使ってテストしましょう。

ファイル > スケッチ例 > IRremote > IRrecvDumpV2

念のために、「IRrecvDumpV2」のスケッチをそのまま記載します。

//------------------------------------------------------------------------------
// Include the IRremote library header
//
#include <IRremote.h>

//------------------------------------------------------------------------------
// Tell IRremote which Arduino pin is connected to the IR Receiver (TSOP4838)
//
#if defined(ESP32)
int IR_RECEIVE_PIN = 15;
#else
int IR_RECEIVE_PIN = 11;
#endif
IRrecv irrecv(IR_RECEIVE_PIN);

//+=============================================================================
// Configure the Arduino
//
void setup() {
    pinMode(LED_BUILTIN, OUTPUT);

    Serial.begin(115200);   // Status message will be sent to PC at 9600 baud
#if defined(__AVR_ATmega32U4__)
    while (!Serial); //delay for Leonardo, but this loops forever for Maple Serial
#endif
    // Just to know which program is running on my Arduino
    Serial.println(F("START " __FILE__ " from " __DATE__));

    irrecv.enableIRIn();  // Start the receiver

    Serial.print(F("Ready to receive IR signals at pin "));
    Serial.println(IR_RECEIVE_PIN);
}

//+=============================================================================
// Display IR code
//
void ircode(decode_results *results) {
    // Panasonic has an Address
    if (results->decode_type == PANASONIC) {
        Serial.print(results->address, HEX);
        Serial.print(":");
    }

    // Print Code
    Serial.print(results->value, HEX);
}

//+=============================================================================
// Display encoding type
//
void encoding(decode_results *results) {
    switch (results->decode_type) {
    default:
    case UNKNOWN:
        Serial.print("UNKNOWN");
        break;
    case NEC:
        Serial.print("NEC");
        break;
    case SONY:
        Serial.print("SONY");
        break;
    case RC5:
        Serial.print("RC5");
        break;
    case RC6:
        Serial.print("RC6");
        break;
    case DISH:
        Serial.print("DISH");
        break;
    case SHARP:
        Serial.print("SHARP");
        break;
    case SHARP_ALT:
        Serial.print("SHARP_ALT");
        break;
    case JVC:
        Serial.print("JVC");
        break;
    case SANYO:
        Serial.print("SANYO");
        break;
    case MITSUBISHI:
        Serial.print("MITSUBISHI");
        break;
    case SAMSUNG:
        Serial.print("SAMSUNG");
        break;
    case LG:
        Serial.print("LG");
        break;
    case WHYNTER:
        Serial.print("WHYNTER");
        break;
    case AIWA_RC_T501:
        Serial.print("AIWA_RC_T501");
        break;
    case PANASONIC:
        Serial.print("PANASONIC");
        break;
    case DENON:
        Serial.print("Denon");
        break;
    case BOSEWAVE:
        Serial.print("BOSEWAVE");
        break;
    }
}

//+=============================================================================
// Dump out the decode_results structure.
//
void dumpInfo(decode_results *results) {
    // Check if the buffer overflowed
    if (results->overflow) {
        Serial.println("IR code too long. Edit IRremoteInt.h and increase RAW_BUFFER_LENGTH");
        return;
    }

    // Show Encoding standard
    Serial.print("Encoding  : ");
    encoding(results);
    Serial.println("");

    // Show Code & length
    Serial.print("Code      : 0x");
    ircode(results);
    Serial.print(" (");
    Serial.print(results->bits, DEC);
    Serial.println(" bits)");
}

//+=============================================================================
// Dump out the decode_results structure.
//
void dumpRaw(decode_results *results) {
    // Print Raw data
    Serial.print("Timing[");
    Serial.print(results->rawlen - 1, DEC);
    Serial.println("]: ");

    for (unsigned int i = 1; i < results->rawlen; i++) {
        unsigned long x = results->rawbuf[i] * MICROS_PER_TICK;
        if (!(i & 1)) {  // even
            Serial.print("-");
            if (x < 1000)
                Serial.print(" ");
            if (x < 100)
                Serial.print(" ");
            Serial.print(x, DEC);
        } else {  // odd
            Serial.print("     ");
            Serial.print("+");
            if (x < 1000)
                Serial.print(" ");
            if (x < 100)
                Serial.print(" ");
            Serial.print(x, DEC);
            if (i < results->rawlen - 1)
                Serial.print(", "); //',' not needed for last one
        }
        if (!(i % 8))
            Serial.println("");
    }
    Serial.println("");                    // Newline
}

//+=============================================================================
// Dump out the decode_results structure.
//
void dumpCode(decode_results *results) {
    // Start declaration
    Serial.print("unsigned int  ");          // variable type
    Serial.print("rawData[");                // array name
    Serial.print(results->rawlen - 1, DEC);  // array size
    Serial.print("] = {");                   // Start declaration

    // Dump data
    for (unsigned int i = 1; i < results->rawlen; i++) {
        Serial.print(results->rawbuf[i] * MICROS_PER_TICK, DEC);
        if (i < results->rawlen - 1)
            Serial.print(","); // ',' not needed on last one
        if (!(i & 1))
            Serial.print(" ");
    }

    // End declaration
    Serial.print("};");  //

    // Comment
    Serial.print("  // ");
    encoding(results);
    Serial.print(" ");
    ircode(results);

    // Newline
    Serial.println("");

    // Now dump "known" codes
    if (results->decode_type != UNKNOWN) {

        // Some protocols have an address
        if (results->decode_type == PANASONIC) {
            Serial.print("unsigned int  addr = 0x");
            Serial.print(results->address, HEX);
            Serial.println(";");
        }

        // All protocols have data
        Serial.print("unsigned int  data = 0x");
        Serial.print(results->value, HEX);
        Serial.println(";");
    }
}

//+=============================================================================
// Dump out the raw data as Pronto Hex.
//
void dumpPronto(decode_results *results) {
    Serial.print("Pronto Hex: ");
    irrecv.dumpPronto(Serial, results);
    Serial.println();
}

//+=============================================================================
// The repeating section of the code
//
void loop() {
    decode_results results;        // Somewhere to store the results

    if (irrecv.decode(&results)) {  // Grab an IR code
        dumpInfo(&results);           // Output the results
        dumpRaw(&results);            // Output the results in RAW format
        dumpPronto(&results);
        dumpCode(&results);           // Output the results as source code
        Serial.println("");           // Blank line between entries
        irrecv.resume();              // Prepare for the next value
    }
}

上記をArduinoへ書き込み後、シリアルモニタを開いてください。

そして、手元にある適当な赤外線リモコンの送信部をVS1838Bへ向け、ボタンを押した時にシリアルモニタ上で下記のように表示されれば成功です!

動作テスト

テレビ用でもエアコン用でも何でもよいので、お近くにある赤外線リモコンを使ってテストしてください。

ただ、VS1838Bは赤外線に反応するモジュールですので、もし配線、ライブラリーのバージョン、ソースコードなどに問題が無いにもかかわらず反応しなかった場合は、そのリモコンが出しているのは赤外線ではない(電波とか)かもしれないということを疑ってください。

動作成功例

以下は、ライブラリー:IRremoteのVer.2.6.1での動作確認時の様子です。
https://twitter.com/Ideaport_Suzuky/status/1742496672637452628
このように、ボタンを一回押すごとに反応が一回あれば成功です。

動作失敗例

Ver.4.2.0(現時点で最新バージョン)ですと、私が購入したVS1838Bでは無反応でした。
https://twitter.com/Ideaport_Suzuky/status/1742453446891262220
むしろ、上記のとおりArduino Unoに衝撃を与えることで反応するといった始末です。

https://twitter.com/Ideaport_Suzuky/status/1742514373044904355
最初の参考記事で推奨されていたVer.2.8.0を試したところ、リモコンに一度反応するとそのまま反応しっ放し状態となりました。(動画前半)

最終的に、2番目の参考記事で推奨されていたVer.2.6.1を試して成功しましたが、同時に、スマホで写真を撮ろうとした際にセンサーが誤反応することで気づきました。(動画後半)

最近のスマホはピント合わせのために赤外線測距センサーが内蔵されており、どうやらそれに反応しているようです。その証拠に、スマホのカメラ(正確にはカメラの脇についている赤外線測距センサーの出力部)をVS1838Bに向けていない時はシリアルモニタ上で反応を確認できません。当然ですが、スマホの赤外線測距センサーの出力部分を指で塞いだ場合もレーザーを遮るため同じ結果になります。

ともかく、思わぬ知見を得られましたが、後者に関しては、実際の運用上は、リモコンから必要なデータだけ受け取られるようソフトウェア的にフィルタリングすれば良いと思います。

まとめ

いかがでしたでしょうか?

今回使用したVS1838Bは、リモコンで動くいろんな製品の受光部が壊れた際の交換品として使われているようです。私が元旦にアマゾンでポチった時は8個で773円でしたが、レビューを見ると、ある製品の純正部品だと8,000円もかかったり、出張修理で8,000円かかったりとまぁまぁのお値段のようです。

部品自体は一個100円もしないので、自分で製品を分解してはんだづけできればかなりの節約ができますね。

それはともかく、これだけの安い部品で、しかもその辺にある普通の赤外線リモコンからの光線を受光でき、データを受け取れるというのを確認できたのは大きな収穫です。

これならわざわざスマホアプリやウェブアプリをつくらずとも、デジペに搭載したうえで、用途を限定した非常に安価なリモコンを付けることでコストを低く抑えられます。そして、これだけでも遠隔操作の最低限の需要に応えることができると考えています。

しかも、赤外線は電磁波であり、電波ではないため、技適などの無線関係の法規制の制限もありません。

もちろん、遮蔽物があると通信できないというデメリットもありますが、そもそもBluetoothやWi-Fiなどの無線技術もまだまだ不安定なため、またおもちゃとして考えた場合は、赤外線通信の方がメリットの方がデメリットを打ち消して余りあると感じました。

今回はまずざっくりと赤外線通信が使えそうか確認しただけですので、次はもう少しオペレーション面まで踏み込んでテストする予定です。

それではまた!

追記

翌日は1月4日ですので、この日から正月明けで始まるお店も多いだろうということで、浜松マルツのX(旧:ツイッター)を確認したところ、10時から営業しているということで行ってきました。

目的は、扇風機のリモコンなどで使われている平べったいシンプルなリモコンを手に入れたいからです。元旦でアマゾンでポチッた際に、赤外線受光モジュールと一緒に赤外線リモコンも買いたかったのですが、後者は買ってもすぐに届きそうになかったので一旦保留にしていました。

話を戻しますが、リモコンは単独では買えなかったため、下記を購入しました。

マルツ電波さんで購入したテープLED用のリモコン。

ただ、VS1838Bに向けてボタン押してもArduinoのシリアルモニタで反応を確認できません。


赤外線受光モジュールに対して試してみるが反応なし。


送信側ランプは赤く光っているが、単に赤く光っているだけの模様。

そこで、VS1838Bが壊れたのかと思い、改めてエアコンのリモコンで試しましたがちゃんと反応しました。

エアコン用の赤外線リモコンの送信部は、肉眼では光が見えないが、カメラを通すとレーザー光を視認できる。

どうしたものかと思っていたところ、今回購入したリモコンをよく見ると、「RF Wireless」との刻印が、、、

つまり、今回購入したテープLED用のリモコンは、赤外線ではなくRF(ラジオ周波数 = 電波)でした。そりゃぁ赤外線の受光モジュールでは反応しないわけです。

ただ、一点気になることが、

RFリモコンなので電波法の制限の範囲内のはずですが、リモコンの筐体裏面には技適マークがありません。一方で、マルツ電波のような大きな会社で売られているならそもそも技適が不要なのでは?という意見もありましたが、いかんせんこのあたりの知見が少ないため何とも言えません。

他方で、ネットで他の商品も調べていたところ、下記のような文言を発見しました。

(ここから)
※本商品は、電波法令で定められている技術基準に適合していることを証明する技適マークが貼付されていない無線機器であり、日本国内で使用する場合は、電波法違反になるおそれがございます。
(ここまで)

つまり、当該製品に関しては法律通ってないので自己責任で使ってね。ということのようで、今回買ったリモコンもこの線の可能性が高いかなと。

まぁ確かに、たとえば山奥で誰もいないところで使うとかならほぼ誰にも影響を与えないですし、そこまで極端でなくても、ファブラボ浜松のように周りの民家と一定の距離があればこれもまた他人に迷惑をかけないでしょう。それに、そもそも以下のような例外規定もあります。

技適未取得機器を用いた実験等の特例制度:
https://www.tele.soumu.go.jp/j/sys/others/exp-sp/)

よって、技適が通っていないものを売っているからといって処罰するのはナンセンスですが、一消費者としてはこのような技適が通っていないものをうっかり買ってしまうリスクがあるということは知っておくべきでしょう。

いずれとしても、当たり前ですが、筐体の見た目だけではIR(赤外線)かRF(ラジオ電波)かはわからないということがよくわかりました。

と同時に、繰り返しになってしまいますが、赤外線はレーザーなので、遮蔽物があると透過しないというデメリットがありますが、面倒な法律のことを考えなくてもいいですし、コストもおそらく一番安くできます。

ということで、まずは今回と同等のサイズ・仕様のIRリモコンを探すことにし、それが難しければ次善の策として、赤外線LED(今回RFリモコンと一緒にマルツで購入済み)を使ったカンタンなデバイスをつくりたいと思います。RFリモコンを試すのは赤外線系がすべてダメとなってからですね。

以上、余談を長々と失礼しました。

Discussion