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) # メインループで軽く待機