スキップしてメイン コンテンツに移動

IRリモートでRGBLED点灯

今回は Raspberry Pi Pico W を使って、赤外線リモコンの信号を受信し、RGB LED を光らせる実験に挑戦してみましょう。

テレビのリモコンなどに使われている赤外線(IR)リモコンは、ボタンを押すと「特定のパターンの信号」を赤外線で送信しています。この信号を、赤外線受信モジュール(たとえば VS1838B など)で受け取り、Pico W に接続して解析します。

リモコンのボタンによって異なる信号が届くので、受信したパルスの長さや間隔を読み取り、「どのボタンが押されたか」を判定できます。今回は、その判定結果に合わせて RGB LED の色を変える のが目標です。たとえば

  • 1ボタンを押すと白色

  • 2ボタンで赤色

  • 3ボタンで緑色

  • 4ボタンで青色

というように、ボタンごとに LED の色が変わるようにします。

RGB LED は赤・緑・青の 3 つの LED が 1 つにまとまった部品で、それぞれの色を ON/OFF(あるいは明るさを変える)ことで多彩な色を作れます。まずは 「IR 信号を受け取って LED を光らせる」 という基本動作ができれば OK。慣れてきたら、次のステップとしてなめらかな PWM 制御やグラデーション発光などにも挑戦してみましょう。

回路図

PL9823-F5

📚 使用ライブラリとモジュール

アイコンライブラリ/モジュール主な役割本コードで使っている代表的 API
📡UpyIrRxIR 受信専用の軽量クラス。受信ピンからのパルス列をキャプチャし、キャリブレーション済みリストに変換してくれるrecord(), get_calibrate_list(), ERROR_NONE
🔧machine.PinRaspberry Pi Pico W の GPIO を制御する MicroPython 標準モジュールPin()(モード Pin.IN
time時刻取得とスリープ用の MicroPython 標準モジュールticks_ms(), ticks_diff(), sleep() / sleep_ms()

ライブラリ:DLして下図のようにRaspbery PI Pico Wにアップロードします。


NEC赤外線リモコン信号デコーダ(MicroPython + UpyIrRx用)

from UpyIrRx import UpyIrRx
from machine import Pin
import time

def decode_nec(raw_data):
    if len(raw_data) < 66:
        return None

    lead_mark = raw_data[0]
    lead_space = raw_data[1]
    if not (8500 < lead_mark < 9500 and 4000 < lead_space < 5000):
        return None

    bits = ""
    for i in range(2, 66, 2):
        mark = raw_data[i]
        space = raw_data[i + 1]
        if not (400 < mark < 700):
            return None
        if 400 < space < 700:
            bits += "0"
        elif 1500 < space < 1800:
            bits += "1"
        else:
            return None

    def bits_to_byte(b):
        val = 0
        for i in range(8):
            if b[i] == '1':
                val |= (1 << i)
        return val

    addr     = bits_to_byte(bits[0:8])
    addr_inv = bits_to_byte(bits[8:16])
    cmd      = bits_to_byte(bits[16:24])
    cmd_inv  = bits_to_byte(bits[24:32])

    if addr ^ addr_inv != 0xFF or cmd ^ cmd_inv != 0xFF:
        return None

    return {"address": addr, "command": cmd}

IR_PIN = 16
ir = UpyIrRx(Pin(IR_PIN, Pin.IN))

while True:
    err = ir.record(wait_ms=1500, blank_ms=200)
    if err == ir.ERROR_NONE:
        raw = ir.get_calibrate_list()
        result = decode_nec(raw)
        if result:
            print("✅ NEC信号デコード成功")
            print("📮 アドレス: 0x{:02X}".format(result["address"]))
            print("🔘 コマンド: 0x{:02X}".format(result["command"]))
    # エラーや待機中の表示はなしでスルー
    time.sleep(0.1)



>>> %Run -c $EDITOR_CONTENT

MPY: soft reboot
✅ NEC信号デコード成功
📮 アドレス: 0x00
🔘 コマンド: 0x16
✅ NEC信号デコード成功
📮 アドレス: 0x00
🔘 コマンド: 0x0C
✅ NEC信号デコード成功
📮 アドレス: 0x00
🔘 コマンド: 0x18
✅ NEC信号デコード成功
📮 アドレス: 0x00
🔘 コマンド: 0x5E
✅ NEC信号デコード成功
📮 アドレス: 0x00
🔘 コマンド: 0x08
✅ NEC信号デコード成功
📮 アドレス: 0x00
🔘 コマンド: 0x1C
✅ NEC信号デコード成功
📮 アドレス: 0x00
🔘 コマンド: 0x5A
✅ NEC信号デコード成功
📮 アドレス: 0x00
🔘 コマンド: 0x42
✅ NEC信号デコード成功
📮 アドレス: 0x00
🔘 コマンド: 0x52
✅ NEC信号デコード成功
📮 アドレス: 0x00
🔘 コマンド: 0x4A

📝 処理の流れ

    ⚙️ ライブラリとピンの準備

     UpyIrRx, Pin, time をインポートし、IR 受信用ピン番号を決定

    🛠 NEC デコード関数 decode_nec() を定義

        ・パルス列のフォーマット確認(リードバースト長・ビット長)

        ・“0”/“1” 判定 → 32 bit へ変換 → 補数チェックで妥当性を検証

    🔧 IR 受信モジュールを初期化

      ir = UpyIrRx(Pin(IR_PIN, Pin.IN))

    🔄 メインループ

         ir.record() で最長 1.5 s 待機し、パルス列をキャプチャ

         受信成功 (ERROR_NONE) なら get_calibrate_list() で μs 単位リストを取得

    🧩 NEC デコード

      キャプチャしたリストを decode_nec() へ渡し、アドレス&コマンドに変換

    ✅ デコード成功時の表示

      16 進数でアドレス/コマンドをプリントし、判定の可視化

    💤 CPU 負荷軽減

      0.1 秒スリープでポーリング間隔を調整し省電力化

📌 decode_nec(raw_data)

先頭 9 ms+4.5 ms のリードバーストを確認し、560 µs のマークと 0/1 用スペース長を解析。32 bit の補数ペアで誤受信を排除する

🏷 bits_to_byte()

“01010101” 形式の文字列を整数 0x55 に変換するユーティリティ

📡 UpyIrRx.record()

高速割り込みでパルス幅をバッファリングし、無信号 200 ms で自動終了

🛠 get_calibrate_list()

キャリブレーション済み(π/38 kHz クロック換算済み)の μs リストを返すので解析がシンプル

🚦 デコード結果の利用

今回は print() で確認するだけだが、ここに RGB LED 制御を追加すれば “ボタン → LED 色” が簡単に実現できる

🚀 拡張アイデア

    受信コードを辞書にマッピングして多色点灯/PWM グラデーション

    受信失敗時のタイムアウト表示やリトライ機能を追加

    複数フォーマット(Sony, RC5 など)をデコードする汎用ライブラリ化

🌈 PL9823-F5 用 RGB LED デモプログラム


# pl9823_demo.py
# Raspberry Pi Pico (RP2040) + MicroPython v1.22 以降
from machine import Pin
from neopixel import NeoPixel   # Pico 用の組込みモジュール
import time

LED_PIN   = 17   # データ線を繋いだ GPIO 番号に合わせる
NUM_LEDS  = 1    # PL9823‑F5 を1個だけ接続
BRIGHTNESS = 0.2 # 0.01.0 で輝度スケーリング(省電力)

np = NeoPixel(Pin(LED_PIN, Pin.OUT), NUM_LEDS)

def set_pixel(rgb):
    # 0255 のタプルを輝度スケーリングして書き込む
    r, g, b = rgb
    scale = BRIGHTNESS
    np[0] = (int(g*scale), int(r*scale), int(b*scale))  # 注: GRB 並び
    np.write()

def wheel(pos):
    # 0255 → レインボー (Adafruit 定番アルゴリズム)
    if pos < 85:
        return (255 - pos*3, pos*3, 0)
    elif pos < 170:
        pos -= 85
        return (0, 255 - pos*3, pos*3)
    else:
        pos -= 170
        return (pos*3, 0, 255 - pos*3)

print("PL9823-F5 demo start")
while True:
    # レインボーグラデーション
    for i in range(256):
        set_pixel(wheel(i))
        time.sleep_ms(20)

    # 単色テスト (赤→緑→青→白→消灯)
    for color in [(255,0,0), (0,255,0), (0,0,255), (255,255,255), (0,0,0)]:
        set_pixel(color)
        time.sleep(1)

⚙️ 初期設定

・🔌 GPIO17 を出力モードに設定

・💡 NeoPixel モジュールで LED(1個)を初期化

・🔋 輝度 (BRIGHTNESS) を 0.2 に設定(省電力)

🎨 set_pixel(rgb) 関数

・🎚️ RGB 値を輝度スケーリング(0〜255 → 0〜51)

・🔄 GRB の並びにして NeoPixel に送信

・📝 np.write() で LED に反映

🌈 wheel(pos) 関数

・🔢 入力値(0〜255)を元にレインボーカラーを生成

・🌟 Adafruit の定番アルゴリズムを使用

・🔁 色相:赤 → 緑 → 青 のグラデーションを形成

🔁 メインループの流れ

1.🌈 レインボーグラデーション

 ・wheel(i) で 0〜255 の色を順番に表示

 ・⏱️ 20ms ごとに更新(滑らかな色の遷移)

2.🎯 単色表示テスト

 ・🟥 赤 → 🟩 緑 → 🟦 青 → ⬜ 白 → ⚫ 消灯

 ・⏱️ 各色を 1秒間表示

IRリモコン信号でPL9823-F5フルカラーLEDを制御

from UpyIrRx import UpyIrRx
from machine import Pin
from neopixel import NeoPixel
import time
import gc

# ==== 設定 ====
IR_PIN, LED_PIN = 16, 17
NUM_LEDS        = 1
BRIGHTNESS      = 0.25
TOL             = 0.45  # パルス許容誤差 45%

# ==== NeoPixel 初期化 ====
np = NeoPixel(Pin(LED_PIN, Pin.OUT), NUM_LEDS)

def set_pixel(rgb):
    r, g, b = rgb
    scale = BRIGHTNESS
    np[0] = (int(g * scale), int(r * scale), int(b * scale))  # GRB順
    np.write()
    time.sleep_us(50)

def match(val, target):
    lo = target * (1.0 - TOL)
    hi = target * (1.0 + TOL)
    return lo <= val <= hi

def decode_nec(raw):
    if len(raw) == 0:
        return None, False
    if len(raw) < 4:
        return None, False

    # 明らかに早すぎるノイズパルスは無視(例: <2000us
    if raw[0] < 2000:
        return None, False

    if 500 <= raw[0] <= 10000:
        if match(raw[1], 4500):
            pass  # 正常なヘッダー
        elif match(raw[1], 2250):
            return 0xFFFF, True  # リピート信号
        else:
            return None, False
    else:
        return None, False

    bits = 0
    count = 0
    for i in range(2, min(len(raw) - 1, 66), 2):
        mark, space = raw[i], raw[i + 1]
        if not match(mark, 560):
            return None, False
        if match(space, 560):
            bits |= (0 << count)
        elif match(space, 1690):
            bits |= (1 << count)
        else:
            return None, False
        count += 1

    if count < 32:
        return None, False

    addr  = bits & 0xFF
    addr_i = (bits >> 8) & 0xFF
    cmd   = (bits >> 16) & 0xFF
    cmd_i = (bits >> 24) & 0xFF

    if (addr ^ addr_i) != 0xFF or (cmd ^ cmd_i) != 0xFF:
        return None, False

    return cmd, False

# ==== コマンド → RGB ====
CMD_COLOR = {
    0x16: (128, 128, 0),  # 白(控えめ)
    0x0C: (255, 0, 0),    # 赤
    0x18: (0, 255, 0),    # 緑
    0x5E: (0, 0, 255),    # 青
    0x08: (255, 255, 0),  # 黄
    0x1C: (0, 255, 255),  # シアン
    0x5A: (255, 0, 255),  # マゼンタ
    0x42: (128, 0, 128),  # グレー
    0x52: (255, 128, 0),  # オレンジ
    0x4A: (0, 0, 0),      # 消灯
}
DEFAULT_COLOR = (32, 32, 32)

# ==== IR 初期化 ====
ir = UpyIrRx(Pin(IR_PIN, Pin.IN, Pin.PULL_UP))

print("IR ⇒ RGB LED demo (final tuned version)")

last_cmd = None
gc_counter = 0
error_counter = 0

while True:
    result = ir.record(wait_ms=1000, blank_ms=180)
    if result == ir.ERROR_NONE:
        raw = ir.get_calibrate_list()
        gc.collect()
        cmd_val, is_repeat = decode_nec(raw)
        del raw
        if cmd_val is None:
            error_counter += 1
            if error_counter >= 10:
                print("Resetting IR receiver...")
                ir = UpyIrRx(Pin(IR_PIN, Pin.IN, Pin.PULL_UP))
                error_counter = 0
            time.sleep(0.005)
            continue

        if is_repeat:
            if last_cmd is None:
                continue
            cmd_val = last_cmd
        else:
            last_cmd = cmd_val

        cmd_val &= 0xFF
        color = CMD_COLOR.get(cmd_val, DEFAULT_COLOR)
        set_pixel(color)
        print("cmd=0x{:02X} → RGB {}".format(cmd_val, color))

        gc_counter += 1
        if gc_counter >= 100:
            gc.collect()
            print("Free memory:", gc.mem_free())
            gc_counter = 0
        error_counter = 0
        time.sleep(0.005)

    elif result == ir.ERROR_TIMEOUT:
        pass  # 通常状態:無反応ならスキップ
    else:
        error_counter += 1
        if error_counter >= 10:
            print("Resetting IR receiver...")
            ir = UpyIrRx(Pin(IR_PIN, Pin.IN, Pin.PULL_UP))
            error_counter = 0
        time.sleep(0.005)



>>> %Run -c $EDITOR_CONTENT

MPY: soft reboot
IR ⇒ RGB LED demo (final tuned version)
cmd=0x4ARGB (0, 0, 0)
cmd=0x16RGB (128, 128, 0)
cmd=0x0CRGB (255, 0, 0)
cmd=0x18RGB (0, 255, 0)
cmd=0x5ERGB (0, 0, 255)
cmd=0x08RGB (255, 255, 0)
cmd=0x1CRGB (0, 255, 255)
cmd=0x5ARGB (255, 0, 255)
cmd=0x42RGB (128, 0, 128)
cmd=0x52RGB (255, 128, 0)
cmd=0x4ARGB (0, 0, 0)
cmd=0x0CRGB (255, 0, 0)
cmd=0x18RGB (0, 255, 0)
cmd=0x5ERGB (0, 0, 255)

🔁 処理の流れ(流れ図スタイル+アイコン)

🔧 初期化

・⚙️ GPIO16 を IR受信機(VS1838B等)用に入力+プルアップ設定

・💡 GPIO17 を NeoPixel(PL9823-F5)データ出力に設定

・🧠 NeoPixel オブジェクトと UpyIrRx 赤外線受信オブジェクトを初期化

・🧹 ガベージコレクションカウンタ・エラーカウンタを初期化

🔄 メインループ処理

1.📡 ir.record() で赤外線信号を受信(最大1000ms待機)

2.🧮 受信成功 → get_calibrate_list() で正規化されたパルス列取得

3.🔍 decode_nec() でNECフォーマット解析:

 ・📉 不完全やノイズの短い信号は無視

 ・🔁 リピートコード(0xFFFF)も検出対応

 ・✅ チェックサム検証付きで信頼性高

4.🎮 コマンド取得成功時:

 ・📝 最後のコマンドを保存(リピート対応)

 ・🎨 コマンドに対応するRGB色を辞書から取得

 ・🌈 NeoPixelへ色を書き込み

 ・🖨 色とコマンドをログ出力 cmd=0xXX → RGB (r,g,b)

5.🧹 100回ごとに gc.collect() を実行しメモリ管理

6.⚠️ 受信失敗・ノイズ時はエラーカウント増加 → 10回でIRを再初期化

コメント

このブログの人気の投稿

Raspberry Pi Pico Wを使ってみよう

   Raspberry Pi Pico W(ラズベリーパイ ピコ ダブリュー) は、英国 Raspberry Pi 財団が 2022 年に発売した Wi-Fi 搭載マイコンボードです。従来の「Raspberry Pi」と聞くと Linux が動くシングルボードコンピュータ(SBC)を思い浮かべがちですが、Pico W は マイクロコントローラ (MCU)に分類され、いわゆる “組み込み開発” を手軽に始められるデバイスです。搭載 MCU はデュアルコア Arm Cortex-M0+(133 MHz 動作)の RP2040 。ここに Infineon 製 CYW43439 チップが追加され、 2.4 GHz IEEE 802.11 b/g/n Wi-Fi(BLE 対応 FW も提供中) が使えるのが最大の特徴です。  開発言語は MicroPython や CircuitPython が真っ先に紹介されることが多いのですが、 公式 Pico SDK を使えば C/C++ でも本格的に開発 できます。SDK は CMake ベースで Windows/macOS/Linux いずれでも利用可能。さらに、Arduino Core RP2040 が整備されたことで Arduino IDE 2.x からも “スケッチ感覚” で書き込みが可能 になりました。したがって、 「まずは Python でサッと試す」 「より高速化や省メモリ化が必要になったら C/C++ へ移行」 といった二段構えの学習ルートが取れるのが魅力です。 メニュー(基礎編) 01. Raspberry Pi Pico Wの開発環境を整える。 02. スイッチで 発光ダイオード(LED) を点灯・消灯させる 03. タイマーで発光ダイオード(LED)を1秒ごとに点滅させる 04. シリアル通信(オウム返し ) 05. シリアル通信(発光ダイオードの点灯・消灯) 06. サーボモーターを動かしてみよう(SG90制御入門) 07. DCモータを動かす(PWM) 08. IRリモートでRGBLED点灯 09. アナログ電圧を測定する(ADCの基本) 10. GPIO割り込み処理 11. リレーを駆動してLEDを制御する 12. DFPlayer で MP3 再生 13. 7 セグ 4 ...

スイッチで発光ダイオード(LED)を点灯・消灯させる

 スイッチを使って発光ダイオード(LED)を制御してみましょう。今回は、スイッチをオンにすると LED が点灯し、オフにすると消灯するように動作させます。もちろん、スイッチと LED を直接接続するのではなく、適切な回路を介して制御します。 1.回路 GPIO16 はデジタル入力として使用し、内部プルアップを有効にします。GPIO15 は出力として設定します。なお、接続には 1kΩ(1キロオーム)の抵抗を使用します。 【ソースコード】 # スイッチで LED を ON / OFF する簡単な例 #   GPIO15 : LED(出力)— LOW で消灯、HIGH で点灯 #   GPIO16 : スイッチ(入力)— 内部プルアップ抵抗を使用 # # ※ 配線例 #   LED のアノード → 1 kΩ 抵抗 → GPIO15 #   LED のカソード → GND #   スイッチ片側   → GPIO16 #   スイッチ反対側 → GND # # ★ 動作 #   スイッチを押す(GPIO16 が LOW)   → LED 点灯 #   スイッチを離す(GPIO16 が HIGH) → LED 消灯 from machine import Pin    # GPIO 制御ライブラリ import utime               # 時間関連(今回は使用しないが拡張用に読み込み) led = Pin ( 15 , Pin.OUT)                 # GPIO15 を出力モードに設定(LED 用) sw   = Pin ( 16 , Pin.IN, Pin.PULL_UP)     # GPIO16 を入力モード+内部プルアップ有効 while True:     if sw. value () == 1 :      # プルアップなので押していないときは 1   ...

シリアル通信(オウム返し)

 Raspberry Pi Pico Wは単体で使うのも便利ですが、パソコンや他のマイコンなど、さまざまな機器と通信できるとさらに活用の幅が広がります。 そこで今回は「シリアル通信」について紹介します。 まずは、パソコンから送られてきたデータをPico Wが受け取り、同じデータをそのままパソコンに送り返す、いわゆる「オウム返し(エコー)」を実装してみましょう。 シリアル通信とは? シリアル通信は、マイコンとパソコンや他の機器がデータを1ビットずつ順番に送受信する通信方式です。Raspberry Pi Pico WではUART(Universal Asynchronous Receiver/Transmitter)というハードウェアを使って実現します。 接続回路  id      TX       RX        baudrate     parity    stopbit  0  GPIO0 GPIO1   9600  なし  1  1  GPIO4 GPIO5   9600  なし  1 今回はUART1(GPIO4:TX、GPIO5:RX)を使用します。 ソースコード例 from machine import UART, Pin # UART1ポートを9600bpsで初期化(ボーレートを指定) sirial = UART ( 1 , 9600 ) # UARTの詳細設定: 9600bps 、データビット8、パリティ無し、ストップビット1で初期化 sirial. init ( 9600 , bits = 8 , parity = None, stop = 1 ) # UARTで文字列を送信する(※MicroPythonではバイト列で送るのが正しいため b '' にしたほうが安全) sirial. write (b 'Hello World \n\r ' )  # 起動時に「Hello World」を送信 # メインループ while True:     # 受信バッファにデータが存在するかチェック     if sirial. any () > 0 : ...