(58)【連載2-3】リモコン作り:キャプチャ機能付きリモコンの完成!?

投稿者: | 2024年11月28日

98 views

ブログ記事はまた後日書こう。
まずは Pico2に載せて動作確認出来たプログラムを置いておく。

【1】やりたいこと

家電製品を制御するための赤外線リモコンが発信する信号のパターンをモニタリングしたい。

ただし…
ブラックボックスな便利ライブラリを使うのではなく、生の信号データをそのまま見たい。

以下、3回のブログ投稿で完成を目指す。
(56)【連載2-1】リモコン作り:赤外線リモコンの信号パターンをキャプチャする。
(57)【連載2-2】リモコン作り:赤外線LEDで信号パターンを再生する。
(58)【連載2-3】リモコン作り:キャプチャ機能付きリモコンの完成!? ←今回

【2】回路の結線

後日追記

【3】使い方(操作仕様)

・赤ボタンを押下すると、記録を開始する。
 基板上のLEDが点灯している2秒間だけ信号をキャプチャし、そのデータをファイル保存する。

・白ボタンを押下すると、赤外線信号を発信する。
 先に記録したファイルをロードし、その情報に従って赤外線LEDを点灯制御する。
 

【4】プログラミング

main.py

from machine import Pin, PWM
import time
import os

#///////////////////////////////////////////////////////////////////////////////
# GPIO設定
btn_rec = Pin(17, Pin.IN, Pin.PULL_UP)      # 記録ボタン
btn_tx  = Pin(14, Pin.IN, Pin.PULL_UP)      # 再生ボタン
rec_led = Pin(25, Pin.OUT)                  # 記録実行中表示LED
ir_pin  = Pin(16, Pin.IN)                   # 赤外線受光モジュールの信号入力用ピン
ir_led  = Pin(15, Pin.OUT)                  # 赤外線発光LEDの出力用ピン
pwm = PWM(ir_led)                           # PWMインスタンス作成

#///////////////////////////////////////////////////////////////////////////////
# 定数、変数定義
RECORDING_DURATION = 2                      # 記録時間 [sec]
rec_signal_flag  = False                    # 記録開始ボタン押下フラグ
play_signal_flag = False                    # 再生開始ボタン押下フラグ
last_press_time  = 0                        # 最後にボタンが押された時刻
DEBOUNCE_DELAY = 500                        # チャタリング対策、デバウンス時間(ms)

# 赤外線受光信号の記録処理に使う変数
recorded_pattern = []                       # 赤外線信号のパターンの保存用リスト
rec_last_time = 0                           # 前回の信号変化検出時間
is_first_pulse = True                       # 初回のエッジを無視するフラグ

#///////////////////////////////////////////////////////////////////////////////
# 受信処理
#///////////////////////////////////////////////////////////////////////////////

#///////////////////////////////////////////////////////////////////////////////
# 赤外線受光信号エッジ検出割り込みハンドラ
def evh_record_pulse(pin):
    global recorded_pattern
    global rec_last_time
    global is_first_pulse
    #---------------------------------------------------------------------------
    now = time.ticks_us()                   # 現在時刻を取得 [us]
    #---------------------------------------------------------------------------
    if is_first_pulse:                      # 初回の信号? → skip
        is_first_pulse = False              # 初回フラグをクリア
    #---------------------------------------------------------------------------
    else:
        # パルス長を記録
        pulse_length = time.ticks_diff(now, rec_last_time)
        recorded_pattern.append(pulse_length)
    #---------------------------------------------------------------------------
    rec_last_time = now                     # 信号変化の検出時刻を更新

#///////////////////////////////////////////////////////////////////////////////
# 赤外線受光信号記録の開始処理
def record_ir_signal():
    global recorded_pattern
    global rec_last_time
    global is_first_pulse
    #---------------------------------------------------------------------------
    print('now recording IR signal...')
    recorded_pattern = []                   # 信号パターン記録用リストを初期化
    is_first_pulse = True                   # 初回フラグをセット
    #---------------------------------------------------------------------------
    rec_led.high()                          # 記録中LEDを点灯
    ir_pin.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=evh_record_pulse)  # 割り込み許可設定
    time.sleep(RECORDING_DURATION)          # 指定された期間だけ、信号を受信する。
    ir_pin.irq(handler=None)                # 割り込み解除
    rec_led.low()                           # 記録中LEDを消灯
    #---------------------------------------------------------------------------
    print('Recording completed : ', recorded_pattern)
    return recorded_pattern                 # 記録した信号パターンを返す。

#///////////////////////////////////////////////////////////////////////////////
# 記録ボタン押下時の割り込みハンドラ
def evh_btn_rec_pressed_handler(pin):
    global rec_signal_flag, last_press_time
    #---------------------------------------------------------------------------
    current_time = time.ticks_ms()          # 現在時刻を取得 [ms]
    #---------------------------------------------------------------------------
    # チャタリング対策のデバウンス処理: 最後の押下検出から指定時間が経過している場合のみ処理する。
    if time.ticks_diff(current_time, last_press_time) > DEBOUNCE_DELAY:
        print('Start recording')
        rec_signal_flag = True              # 記録開始ボタン押下フラグをセット → 記録開始
        last_press_time = current_time      # 最後の押下検出時刻を更新

################################################################################
# 再生処理
################################################################################

#///////////////////////////////////////////////////////////////////////////////
# PWM設定
def setup_pwm(frequency=38000, duty_cycle=33):
    pwm.freq(frequency)                         # 38kHzに設定
    pwm.duty_u16(int(65535 * duty_cycle / 100)) # Duty ratioを 33%に設定

#///////////////////////////////////////////////////////////////////////////////
# PWM ON
def enable_pwm():
    pwm.init()

#///////////////////////////////////////////////////////////////////////////////
# PWM OFF
def disable_pwm():
    pwm.deinit()

#///////////////////////////////////////////////////////////////////////////////
# IR点灯パターンファイルをロード
def load_ir_pattern(filename='ir_pattern.txt'):
    #---------------------------------------------------------------------------
    if not file_exists(filename):               # ファイルが存在しない場合
        return []                               # 空リストを返す
    #---------------------------------------------------------------------------
    with open(filename, 'r') as f:
        pattern = f.read().strip()
    return list(map(int, pattern.split(',')))  # 記録したパターンをリストとして返す。

#///////////////////////////////////////////////////////////////////////////////
# IR点灯実行
def play_ir_signal(pattern):
    print('[BEGIN] Play IR Signal')
    setup_pwm()                             # PWMを設定
    #---------------------------------------------------------------------------
    for i, pulse in enumerate(pattern):
        if i % 2 == 0:                      # 偶数インデックスはON期間
            enable_pwm()                    #   PWMを有効化(赤外線信号送信)
        else:                               # 奇数インデックスはOFF期間
            disable_pwm()                   #   PWMを無効化(信号を送らない)
        #-----------------------------------------------------------------------
        time.sleep_us(pulse)                # 指定された期間待機
    
    #---------------------------------------------------------------------------
    # 再生終了後はPWMを無効化
    disable_pwm()
    print('[END] Play IR Signal')

#///////////////////////////////////////////////////////////////////////////////
# 再生ボタン押下時の割り込みハンドラ
def evh_btn_tx_pressed_handler(pin):
    global play_signal_flag, last_press_time
    #---------------------------------------------------------------------------
    current_time = time.ticks_ms()          # 現在時刻を取得 [ms]
    #---------------------------------------------------------------------------
    # チャタリング対策のデバウンス処理: 最後の押下検出から指定時間が経過している場合のみ処理する。
    if time.ticks_diff(current_time, last_press_time) > DEBOUNCE_DELAY:
        print('Start playing')
        play_signal_flag = True             # 再生開始ボタン押下フラグをセット → 再生開始
        last_press_time = current_time      # 最後の押下検出時刻を更新

################################################################################
# その他の処理
################################################################################

#///////////////////////////////////////////////////////////////////////////////
# ファイルの存在確認  ※MicroPythonでは os.path.exists()が使えない。
def file_exists(filename):
    try:
        os.stat(filename)                           # ファイル情報を取得
        return True
    except OSError:
        return False

################################################################################
# メインループ
################################################################################

# ボタン押下割込みを許可設定
btn_rec.irq(trigger=Pin.IRQ_FALLING, handler=evh_btn_rec_pressed_handler)
btn_tx.irq( trigger=Pin.IRQ_FALLING, handler=evh_btn_tx_pressed_handler)

# 無限ループ
while True:
    #---------------------------------------------------------------------------
    # 記録処理
    if rec_signal_flag:
        rec_signal_flag = False                     # 記録開始フラグをリセット
        recorded_pattern = record_ir_signal()       # 記録実行
        if len(recorded_pattern) > 0:               # 要素数が0でない?
            with open('ir_pattern.txt', 'w') as f:  # 記録したパターンをファイルに保存
                f.write(','.join(map(str, recorded_pattern)))
    #---------------------------------------------------------------------------
    # 再生処理
    if play_signal_flag:
        play_signal_flag = False                    # 再生開始フラグをリセット
        recorded_pattern = load_ir_pattern()        # 記録済みの信号パターンを読み込む
        if len(recorded_pattern) > 0:               # 要素数が0でない?
            play_ir_signal(recorded_pattern)        # 信号パターンを再生
    #---------------------------------------------------------------------------
    time.sleep(0.1)  # メインループで軽く待機

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です


日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)