# Arduino 測試 : PIR 紅外線移動偵測 (一)
# Arduino 測試 : PIR 紅外線移動偵測 (二)
HC-SR501 模組只有三支接腳, 左右兩邊是電源接地 GND 與 VCC (5V), 中間那支腳才是信號輸出 (3.3V), 參考 :
我是將其直接與 ESP-01 的 GPIO 0 相連, 然後在程式中將 GPIO 0 定義為輸入腳, GPIO 2 定義為輸出腳, GPIO 2 連接一個 LED 與 220 歐姆電阻後接地, 參考下列測試紀錄中的 GPIO 測試部分 :
# MicroPython on ESP8266 (二) : 數值型別測試
# MicroPython on ESP8266 (三) : 序列型別測試
# MicroPython on ESP8266 (四) : 字典與集合型別測試
# MicroPython on ESP8266 (五) : WiFi 連線與 WebREPL 測試
# MicroPython on ESP8266 (六) : 檔案系統測試
# MicroPython on ESP8266 (七) : 時間日期測試
# MicroPython on ESP8266 (八) : GPIO 測試
MicroPython 文件參考 :
# MicroPython tutorial for ESP8266 (官方教學)
# http://docs.micropython.org/en/latest/micropython-esp8266.pdf
# http://docs.micropython.org/en/latest/pyboard/library/usocket.html#class-socket
# http://docs.micropython.org/en/v1.8.7/esp8266/library/usocket.html#module-usocket
# https://gist.github.com/xyb/9a4c8d7fba92e6e3761a (驅動程式)
下列測試 1 是參考 "Arduino 測試 : PIR 紅外線移動偵測 (一)" 這篇的 Arduino 程式修改為 MicroPython 版本 :
測試 1 : 偵測到移動使 LED 閃爍
from machine import Pin
import time
p0=Pin(0, Pin.IN) #接 PIR 感測器信號輸出 (中間腳)
p2=Pin(2, Pin.OUT) #接 LED + 220 歐姆電阻
def LED_blink(): #閃爍 LED 一次的函數
p2.value(1)
time.sleep_ms(50) #暫停 50 ms
p2.value(0)
time.sleep_ms(50) #暫停 50 ms
while True:
if p0.value()==1: #PIR 偵測到人體移動時輸出 High (3.3V)
LED_blink() #讓 LED 閃爍
else: #移動停止時 PIR 輸出 Low
p2.value(0) #讓 LED 熄滅
將此程式存成 main.py 檔案後上傳 ESP-01, 參考下面這篇 :
# MicroPython on ESP8266 (六) : 檔案系統測試
可利用 ampy, mpfshell, 或 webrepl 將 main.py 上傳到 ESP-01 模組 :
D:\test>ampy --port COM4 put main.py #上傳檔案
然後將 ESP-01 重開機或在 REPL 按 Ctrl+D 軟開機, 當偵測到有人體移動時, LED 就會閃爍.
PIR 感測器偵測到人體移動輸出 High 的時間有多長? 我參考下列這篇測試 3 的 Arduino 程式, 將其改寫為 Python 版如下 :
# Arduino 測試 : PIR 紅外線移動偵測 (二)
測試 2 : 計算 PIR 輸出 High 的時間長度
from machine import Pin
import time
p0=Pin(0, Pin.IN)
p2=Pin(2, Pin.OUT)
waitForLow=False #狀態旗標:用來計算輸出 High 的時間
timerStart=time.ticks_ms() #紀錄 High 時長的計時器
def LED_blink(): #閃爍 LED 一次的函數
p2.value(1)
time.sleep_ms(50) #暫停 50 ms
p2.value(0)
time.sleep_ms(50) #暫停 50 ms
while True:
if p0.value()==1: #PIR 偵測到人體移動時輸出 High (3.3V)
if not waitForLow: #目前是 Low 狀態
timerStart=time.ticks_ms() #取得目前時戳更新計時器開始計時
waitForLow=True #更新狀態旗標 : 等待 Low 到來
LED_blink()
else: #PIR 輸出 Low
if waitForLow: #原先狀態為 High
print(time.ticks_ms()-timerStart) #計算時長
waitForLow=False #狀態旗標歸零
p2.value(0) #熄滅 LED
上面程式在 PIR 由 Low 變 High 時設定狀態旗標並使用 time.ticks_ms() 函數來取得以毫秒計算的時戳 (開機以來的毫秒數) 開始計時, 等到 PIR 由 High 變 Low 時再取一次時戳, 與開始時戳相減即得 PIR 為 High 之時長, 同時將狀態旗標歸零以備下次觸發. 計算時間差也可以使用 time_ticks(time_ticks_ms(), timerStart), 參考 :
# https://docs.micropython.org/en/latest/esp8266/esp8266/quickref.html#delay-and-timing
# https://docs.micropython.org/en/latest/pyboard/library/utime.html#utime.ticks_diff
在 REPL 介面可看到 print() 函數所輸出的時間長度 (ms) :
MicroPython v1.9-8-gfcaadf92 on 2017-05-26; ESP module with ESP8266
Type "help()" for more information.
>>>
2622
4538
1513
2420
1816
1210
3429
2018
1211
4134
3732
2218
1412
807
605
1009
1109
605
2319
605
1311
908
2420
下列測試 3 程式用一個變數指定當 PIR 感測器被觸發後持續點亮 LED 的時間 :
測試 3 : 指定 PIR 觸發後 LED 點亮時間
from machine import Pin
import time
p0=Pin(0, Pin.IN)
p2=Pin(2, Pin.OUT)
highDelay=4.5 #LED 持續點亮之秒數
while True:
if p0.value()==1:
p2.value(1) #點亮 LED
time.sleep(highDelay) #持續點亮指定之秒數
p2.value(0) #時間到熄滅 LED
上面的範例適合用在感應燈的應用, 如果是要在 PIR 觸發後讓 LED 閃爍指定的時間長度該怎麼做? 這時就需要一個變數來記錄觸發後的狀態了, 如下列測試 4 所示 :
測試 4 : 指定 PIR 觸發後 LED 閃爍時間
from machine import Pin
import time
p0=Pin(0, Pin.IN)
p2=Pin(2, Pin.OUT)
duration=8 #LED 持續閃爍之秒數
def LED_blink():
p2.value(1)
time.sleep_ms(50)
p2.value(0)
time.sleep_ms(50)
while True:
if p0.value()==1: #偵測到觸發訊號
expire=duration*1000 + time.ticks_ms() #計算結束時戳 (duration 換算成 ms)
while time.ticks_ms() <= expire: #若還未到達結束時戳就持續閃爍
LED_blink()
上面的兩個範例在指定時間結束後即使偵測物體繼續移動, 雖然 PIR 感測器是在 repeat trigger 模式 (有些可用 Jumper 設定, 我的 PIR 沒有 Jumper, 固定為重複觸發), 但可能還是會稍停一下, 這對於移動照明而言不實用, 要如何才能在有持續移動情況下讓燈維持不滅呢? 這需要外部硬體中斷來協助才行, 參考下面這篇 Arduino 的測試 6 :
# Arduino 測試 : PIR 紅外線移動偵測 (二)
MicroPython 的硬體中斷用法請參考官網教學文件 :
# 6.1. External interrupts
# http://docs.micropython.org/en/latest/wipy/library/machine.Pin.html#machine.Pin.irq
# MicroPython on ESP8266 (八) : GPIO 測試
測試 5 : PIR 持續觸發使 LED 延遲熄滅
from machine import Pin
import time
p0=Pin(0, Pin.IN)
p2=Pin(2, Pin.OUT)
state=0
triggerCount=0 #觸發次數紀錄器 (最高次數為 triggerLimit 所規範)
triggerLimit=5 #觸發次數紀錄器上限值
highDelay=10.0 #點亮 LED 之秒數
def int0(p): #中斷處理函數 (必須傳入一個參數 : IRQ 腳位)
global state #取用全域變數
global triggerCount #取用全域變數
global triggerLimit #取用全域變數
state=1 #LED 點亮狀態
if triggerCount < triggerLimit: #觸發次數紀錄器低於上限就增量
triggerCount=triggerCount + 1
print(p,"IRQ Trigger Count:",triggerCount)
p0.irq(trigger=Pin.IRQ_RISING, handler=int0) #設定 GPIO 0 為上升緣觸發 IRQ 中斷
while True:
if state==1: #LED 點亮狀態
p2.value(1) #點亮 LED
time.sleep(highDelay) #暫停秒數
triggerCount=triggerCount - 1 #觸發紀錄器減量
print("Trigger Count:", triggerCount)
if triggerCount <= 0: #觸發紀錄器歸零時熄滅 LED
state=0
else:
p2.value(0) #熄滅 LED
上面的程式利用 state 變數紀錄 LED 明滅狀態, 並將 GPIO 0 設定為外部上升緣觸發 IRQ 中斷, 當中斷發生時, 中斷處理函數 int0() 會將 state 變更為 1, 點亮 LED, 同時若未達上限將觸發紀錄器增量. 在無限迴圈裡則依據 state 值來使 LED 明滅; 若要點亮 LED, 則在持續時間 highDelay 結束後將觸發紀錄器減量, 如果觸發紀錄器因此歸零, 則將 LED 熄滅.
設定 Pin 物件外部硬體中斷只要呼叫 irq() 方法, 並傳入 trigger 以及 callback 參數即可. 其中 trigger 可用選項如下 (可用 | 組合) :
- Pin.IRQ_LOW_LEVEL (LOW 觸發)
- Pin.IRQ_HIGH_LEVEL (HIGH觸發)
- Pin.IRQ_RISING (上升緣都觸發)
- Pin.IRQ_FALLING (下降緣都觸發)
MicroPython v1.9-8-gfcaadf92 on 2017-05-26; ESP module with ESP8266
Type "help()" for more information.
>>>
PYB: soft reboot
#9 ets_task(40100164, 3, 3fff829c, 4)
Traceback (most recent call last):
File "boot.py", line 5, in
File "webrepl.py", line 21, in setup_conn
OSError: [Errno 12] ENOMEM
TypeError: function takes 0 positional arguments but 1 were given
TypeError: function takes 0 positional arguments but 1 were given
TypeError: function takes 0 positional arguments but 1 were given
REPL 執行結果如下 :
可見即使在 LED 點亮狀態 (state=1) 程式暫停時, IRQ 仍持續觸發, triggerCount 繼續增值使得 LED 能持續點亮. 在停止移動後, triggerCount 計數器將從最高值 5 遞減至 0, 因此以上例之設定值 trggerLimit=5, highDelay=10 來說, 最後一次偵測到移動後, 若 5*10=50 秒內未再偵測到移動, 則 LED 就會熄滅. 調整 triggerLimit 與 highDelay 之值即可改變熄燈的延遲時間, 這樣就不會在有持續移動情況下, 發生 LED 一下子亮, 一下子又熄滅的情形了.
參考 :
# update data to thingspeak
# TEST BOARD REVIEW WITH - EXERCISER / WEB SERVER / WEB CLIENT
# IoT Tutorial for ESP8266
# https://junkhack.wordpress.com/2015/08/25/初めてのiotデバイス完成/
2017-08-23 補充 :
關於 PIR 感測模組 HC-SR501 的說明, 可參考下面這網頁 :
# HC-SR501人體紅外線感應模組
另外, PIR 只能做動態偵測, 無法做靜態偵測, 若人保持不動, 例如在床上睡覺, 就無法偵測到房間內有人存在, 如果要用在冷氣機節能用途, 只用 PIR 是做不到的. 下面這篇論文則是運用相對運動概念, 使用步進馬達以 6 RPM 速率驅動一個開口為 60 度的掃掠盤讓 PIR 變成主動偵測裝置, 即使人處於靜態, 亦能偵測出來 :
# 動態感測器於靜態人體偵測之應用
沒有留言:
張貼留言