モチベ
- 各部屋のライトをSwitchBotで制御したい。
- 可能ならSwitchBot Hubを各部屋に置きたいが、コストがかかる。(1個5000円以上する)
- 安く自作できないかやってみよう。
やったこと
リモコンのパルスをラズパイで受信 ▶ 記憶して
ラズパイから 赤外線LEDを制御してパルスを再現する。
動作に成功したのは以下のリポ。制作者に感謝を。
Arduinoで実現するなら以下のリポが結構有名らしい。
一応手持ちのArduinoでやってみたが、私の環境ではうまくできなかったという背景がある。
(うちのライトは オーデリック株式会社 リモコン送信機 NRL-351N-JP というもの)
今回は1つめの RemotePy を使う。
パーツ
(2025/10 現在)
リンク先は秋月電子です。
| パーツ | 金額 |
|---|---|
| タクトスイッチ | 15円 |
| 抵抗10[Ω] | 100本入100円 |
| 抵抗1[kΩ] | 100本入100円 |
| トランジスタ(2SC1815L-GR-T92-K) | 20個入100円 |
| 赤外線LED(5mm赤外線LED 940nm OSI5FU5111C-40 50V150mA ) | 5個入100円 |
| 赤外線受信モジュール(OSRB38C9AA) | 2個入100円 |
| Raspi Pico H (ピンヘッダ実装済み完成品) | 920円 |
| 合計 | 1435円 |
トータル 1435円 (ブレッドボード、ジャンパ、USBケーブル除く)
.....................これ、安いかと言われると微妙なラインだよ。
....まぁでもでもでも、今回使った数だけで勘定すれば、1000円切るに決まってるかr
| パーツ | 金額 |
|---|---|
| タクトスイッチ | 15円 |
| 抵抗10[Ω]x1 | 1円 |
| 抵抗1[kΩ]x1 | 1円 |
| トランジスタ(2SC1815L-GR-T92-K) | 5円 |
| 赤外線LED(5mm赤外線LED 940nm OSI5FU5111C-40 50V150mA ) | 20円 |
| 赤外線受信モジュール(OSRB38C9AA) | 50円 |
| Raspi Pico H (ピンヘッダ実装済み完成品) | 920円 |
| 合計 | 1012円 |
トータル 1012円 (ブレッドボード、ジャンパ、USBケーブル除く)
...........?
回路図
これは赤外線を送信、受信両方を回路の組み換えなしでできるようにしてあるが、
送信だけで十分なら、赤外線受信モジュールはもちろん不要となる。

抵抗値について
今回、赤外線LEDは150mAまで耐えられる大出力なものを使う。
raspi picoは直接150mAも流せないらしいので、トランジスタを使う。
計算したり測ったりした結果、コレクタの抵抗を10[Ω]、ベースの抵抗を1[kΩ]とした。1
コード
ベースを書いてくれた人に感謝しながらforkしたのが以下
(今回の構成に合わせたコードを含む)
まずは本家のreadmeをよく読んで実行方法やコードの構造を大体でいいので把握しておこう。
パルスのフォーマットについても軽く触れられており、優しい。
まずはリモコンの赤外線を受信してみる
demoを動かしてみる。
今回はraspi picoを使うため、以下のRP2040フォルダ以下のデモを使う。
オリジナルからコードの変更箇所は無い。うpすればそのまま実行可能。
from machine import Pin
from micropython import const
from gc import collect
import json
from UpyIrTx import UpyIrTx
from UpyIrRx import UpyIrRx
# RP2040 RX=Pin18, TX=Pin19
_GROVE_PIN = {'ATOM': (32, 26),
'CORE2': (33, 32),
'BASIC': (22, 21),
'GRAY': (22, 21),
'FIRE': (22, 21),
'GO': (22, 21),
'Stick': (33, 32),
'Else': (18, 19)}
_DEVICE = 'Else'
_TX_IDLE_LEVEL = const(0)
_TX_FREQ = const(38000)
_TX_DUTY = const(30)
_RX_IDLE_LEVEL = const(1)
_RX_SIZE = const(1023)
rx_pin = Pin(_GROVE_PIN[_DEVICE][0], Pin.IN)
rx = UpyIrRx(rx_pin, _RX_SIZE, _RX_IDLE_LEVEL)
tx_pin = Pin(_GROVE_PIN[_DEVICE][1], Pin.OUT)
tx = UpyIrTx(0, tx_pin, _TX_FREQ, _TX_DUTY, _TX_IDLE_LEVEL)
cmd = input()
while cmd != 'q':
if len(cmd) > 0:
if cmd[0] == 'r':
# ex. cmd: 'r[3000, 200, 1023]
try:
_wait, _blank, _size = json.loads(cmd[1:])
if rx.record(_wait, _blank, _size) == UpyIrRx.ERROR_NONE:
print(rx.get_calibrate_list())
else:
print('[]')
except:
print('[]')
elif cmd[0] == 'w':
# ex. cmd: 'w[420, 1260, 420, ...]
try:
if tx.send(json.loads(cmd[1:])):
print('OK')
else:
print('NG')
except:
print('NG')
else:
print('NG')
del cmd
collect()
cmd = input()
受光部分にリモコンを近づけて部屋の電気をオンしてみる。
>>> r[3000, 200, 1023]
>>> # ここでリモコンを操作
>>> # タイムアウト200ms経過後に以下が出力される
>>> [8816, 4408, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 1653, 551, 1653, 551, 551, 551, 1653, 551, 551, 551, 551, 551, 551, 551, 1653, 551, 1653, 551, 1653, 551, 1653, 551, 551, 551, 1653, 551, 1653, 551, 551, 551, 551, 551, 1653, 551, 551, 551, 551, 551, 1653, 551, 551, 551, 551, 551, 1653, 551, 1653, 551, 551, 551, 54549, 10469, 4959, 551, 551, 551, 551, 551, 2204, 551, 551, 551, 551, 551, 551, 551, 551, 551, 2204, 551, 2204, 551, 551, 551, 551, 551, 551, 551, 2204, 551, 551, 551, 2204, 551, 551, 551, 2204, 551, 1653, 551, 551, 551, 2204, 551, 2204, 551, 551, 551, 551, 551, 2204, 551, 551, 551, 551, 551, 2204, 551, 551, 551, 551, 551, 1653, 551, 2204, 551, 551, 551]
おー、なんか出てきた。
最後のリストをコピっておく。
(電源オフのリモコン操作もこの後やって、同様にリストをメモしておく。)
パルスを再現
タクトスイッチを押して先程のリストのパルスを赤外線LEDで再現するコードを書いてみた。
スイッチの押下検出にはirqももちろん使えるが今回めんどくさいのでポーリング。
"LIVING_ROOM_ON"、"LIVING_ROOM_OFF"に先程取得したリストを貼り付ける。
# MicroPython v1.26.0 on 2025-08-09; Raspberry Pi Pico with RP2040
# で動作確認済み
from machine import Pin
from UpyIrTx import UpyIrTx
import utime
SW_UP = 1
SW_DOWN = 0
LIGHT_ON = 0
LIGHT_OFF = 1
tx_pin = Pin(22, Pin.OUT) # GP22 (Pin No.29)
tx = UpyIrTx(0, tx_pin, idle_level=1) # 0ch
# タクトスイッチ(GP16)
sw = Pin(16, Pin.IN, Pin.PULL_UP)
LIGHT_PULSE={
"LIVING_ROOM_ON":[8896, 4448, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 1668, 556, 1668, 556, 556, 556, 1668, 556, 556, 556, 556, 556, 556, 556, 1668, 556, 1668, 556, 556, 556, 1668, 556, 556, 556, 1668, 556, 1668, 556, 556, 556, 556, 556, 1668, 556, 1668, 556, 556, 556, 1668, 556, 556, 556, 556, 556, 1668, 556, 1668, 556, 556, 556],
"LIVING_ROOM_OFF":[9072, 4536, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 1701, 567, 1701, 567, 567, 567, 1701, 567, 567, 567, 567, 567, 567, 567, 1701, 567, 1701, 567, 567, 567, 567, 567, 567, 567, 1701, 567, 567, 567, 567, 567, 567, 567, 567, 567, 1701, 567, 1701, 567, 1701, 567, 567, 567, 1701, 567, 1701, 567, 1701, 567, 1701, 567],
}
# タクトスイッチを押すとリモコン信号を送信する。スイッチを押すたびにトグルする。
# スイッチの検出はポーリング
light_status = LIGHT_OFF
while True:
# swが押された
if sw.value()==SW_DOWN:
if light_status==LIGHT_OFF:
# オフ状態ならオンにする
tx.send(LIGHT_PULSE["LIVING_ROOM_ON"])
light_status = LIGHT_ON
utime.sleep_ms(200)
else:
# オン状態ならオフにする
tx.send(LIGHT_PULSE["LIVING_ROOM_OFF"])
light_status = LIGHT_OFF
utime.sleep_ms(200)
これでタクトスイッチをカチカチするとリビングの電気がオンオフして家族に怒られる。
今後
micropython動作とGPIO制御が最低限できるマイコンなら
これらは動作するので、もっと安く抑えようと思えばできるはず。
また、これだけではもちろん実用的ではなくて、もっといいものにするなら
- 電池駆動化(省電力化)
- 家の中のライト全部制御できるようにする
- 赤外線受信と送信の切り替えを簡単にできるようにする
とか。ちょっと楽しそう。
参考
オリジナルのリポジトリ
本記事のリポジトリ(オリジナルのフォークからの編集)
raspi pico のピン配置とか、出せる電流とか
ArduinoでIRremoteを動かしている方の記事。OSRB38C9AA は5V駆動と書いてあるが、
3.3Vでも動作する。(データシート参照)
※注釈
抵抗値の計算について
hFE = 280とする。Ic=150mA流したい。
Ib=Ic / hFE = 150x10^-3 / 280 = 0.54 [mA]
(データシートより) Vbe = 1
Ib x R2 + Vbe = 3.3 [V]
R2 = (3.3-1) / Ib = 2.3 / 0.54x10^-3 = 4.3 [kΩ]
Vc + Ic x R1 = 3.3
Vc = 1.35 とすると
R1 = (3.3-1.35) / 150x10^-3 = 13 [Ω]
-
今回使用したnpnトランジスタの増幅率hFEは家の室温環境下の安物のテスタで測るとhFE=280だった。
机上の計算だとLEDに150mA流すためには、R1=13[Ω]、R2=4.3[kΩ]を繋ぐことになる気がするが、電流値を実測しながら抵抗を付けたり外したりしてたらあの値に落ち着いた。なぜ...
一応、今後再計算することになるかもしれないので、途中式を残しておく。間違ってることに後で気づけるかもしれないし。 ↩
