紅外線遙控在家電的使用非常的普遍,主要利用紅外線 LED 來發射大約在 940nm 波長的紅外線以傳送訊息,詳細的說明可以參閱 wiki 有關紅外線遙控的部分。
有時候,當遙控器不動作的時候,我們常常會看看是否遙控器發出了紅外光,這時候我們可以使用手機的攝影功能來看 LED,請看 這篇文章:用手機看紅外線 的說明。由於在週遭的環境中有紅外線的產生,因此干擾的雜訊也不少。
在變頻風扇 DIY 實作的專案中,我們只使用到接收的部分,因此在 Micropython 程式上,我們也只考慮到從遙控器上接收紅外線信號。
我看到很多書或文章指出,使用 Python 語言最主要的原因之一是開發快速,而且又有豐富的程式庫可以用,當然,這代表前人的貢獻很多,我們是站在巨人的肩膀上面。然而,Micropython 並未到達這個地步,目前支援的驅動或是程式庫還很有限,比較齊全的反而是 Adafruit 的 CircuitPython,畢竟 Adafruit 也在銷售各式各樣的感測器元件,也有詳細的教學。這也是我常常引導您善用 google 搜索,並且選取您使用的感測器規格書,以及評估可用的驅動模組,這有些好處,很多感測器並不見得相容,您多看看這些訊息,會逐漸增強您 DIY 的功力,而不只是 copy & paste 而已。
紅外線遙控器的發射與接收,必須是遵循著某些通信協議,這樣彼此才能溝通。但非常可惜的是,這樣的通信協議並沒有一個國際標準,通常是廠商的 de facto standard,所以我們常聽說的有 Philip 的,有 NEC 的,有 Sony 的,...,等等。我們今天主要以 NEC 的紅外線遙控器協議來說明。
NEC 協議使用的是 38K 的載波,因此我們可以把非 38K 頻率的信號都當作是雜訊,這樣就可以大幅的過濾掉雜訊,而且這樣的功能已經被整合成 IC 了:
而最便宜的莫過於 VS1838 這個型號的 紅外線感測器元件,這元件看起來有三枝腳,但其實已經是積體電路(IC)。
我們從 VS1838 的規格書看應用電路節錄如下:
標示 >20K 歐姆的是上拉電阻,這部分越靠近開發板越好,100 歐姆是供電給紅外線 IC 的電源保護,我們有時候會將紅外線感測器拉出一段距離,以便接收到訊號,在這麼常的距離裡,若是拉出的電線有短路,這 100 歐姆的電阻也起到了保護的作用,因此這電阻也是越接近開發板越好。至於有兩個電容,主要是電源與訊號濾波之用,這部分是越接近紅外線元件越好。其實,在實驗中,接線到麪包板,這些電阻與電容都沒接,應該也沒關係!
我們上網搜尋 “micropython remote control" 或者中文 "micropython 紅外線遙控",可以查到很多訊息,我建議您可以參考一下兩個教學:
接下來,我們把 VS1838 紅外線感測器的 OUT 接腳,連接到 ESP32-S3 的 GPIO 2,依照上面兩個教材說明,看看您是否可以把 VS1838 用起來!
前面兩個說明教材,不約而同的使用了 Micripython 大神級的開發者 Peter Hinch 所開發出來的程式庫模組:micropython_ir。這位大神的 Github 也有許多其它的寶藏等着您去探索。
我這裡列出 https://github.com/peterhinch/micropython_ir/blob/master/ir_rx/init.py 的程式碼:
# ir_rx __init__.py Decoder for IR remote control using synchronous code
# IR_RX abstract base class for IR receivers.
# Author: Peter Hinch
# Copyright Peter Hinch 2020-2021 Released under the MIT license
from machine import Timer, Pin
from array import array
from utime import ticks_us
# Save RAM
# from micropython import alloc_emergency_exception_buf
# alloc_emergency_exception_buf(100)
# On 1st edge start a block timer. While the timer is running, record the time
# of each edge. When the timer times out decode the data. Duration must exceed
# the worst case block transmission time, but be less than the interval between
# a block start and a repeat code start (~108ms depending on protocol)
class IR_RX():
# Result/error codes
# Repeat button code
REPEAT = -1
# Error codes
BADSTART = -2
BADBLOCK = -3
BADREP = -4
OVERRUN = -5
BADDATA = -6
BADADDR = -7
def __init__(self, pin, nedges, tblock, callback, *args): # Optional args for callback
self._pin = pin
self._nedges = nedges
self._tblock = tblock
self.callback = callback
self.args = args
self._errf = lambda _ : None
self.verbose = False
self._times = array('i', (0 for _ in range(nedges + 1))) # +1 for overrun
pin.irq(handler = self._cb_pin, trigger = (Pin.IRQ_FALLING | Pin.IRQ_RISING))
self.edge = 0
self.tim = Timer(-1) # Sofware timer
self.cb = self.decode
# Pin interrupt. Save time of each edge for later decode.
def _cb_pin(self, line):
t = ticks_us()
# On overrun ignore pulses until software timer times out
if self.edge <= self._nedges: # Allow 1 extra pulse to record overrun
if not self.edge: # First edge received
self.tim.init(period=self._tblock , mode=Timer.ONE_SHOT, callback=self.cb)
self._times[self.edge] = t
self.edge += 1
def do_callback(self, cmd, addr, ext, thresh=0):
self.edge = 0
if cmd >= thresh:
self.callback(cmd, addr, ext, *self.args)
else:
self._errf(cmd)
def error_function(self, func):
self._errf = func
def close(self):
self._pin.irq(handler = None)
self.tim.deinit()
您可以看出,在 IR_RX class 中使用了 Timer,使用了 GPIO 中斷等等,是否覺得有點熟悉呢?或許您慢慢的這些程式就全看懂了,甚至還有能力修改!