在這篇文章中,將介紹使用 Python 與 C 語言接收人體紅外線感應 ( PIR) 模組觸發訊號後,發送預警電子郵件給指定的收件人的程式寫作方法。若你是使用智慧型手機的話,開啟收信軟體更能即時的收到預警郵件通知。
最後延伸使用以配合繼電器模組的方式,另外控制照明設備的開或關,若以警報裝置做取代的話,更能達到嚇阻效果!
賣場中的 PIR 模組附有三條杜邦線,依照上圖連接到樹莓派上,完成後就像下圖一樣
PIR 模組與樹莓派的連接 |
PIR 模組測試:
一開始先簡單的測試 PIR 模組受感應時的觸發訊號,是否可正確的讓樹莓派接收,如能正確輸出就會在命令提示視窗下看到輸出。
所以一開始先將程式碼下載到你的樹莓派的 Home 目錄下。如果您的 Home 目錄下沒有 codes 資料夾請自行建立,等一下檔案會直接下載並自行解開到所在目錄下,在程式說明時可以再另一視窗開啟程式內容做為比較。
請輸入下面指令取得程式原始碼:
pi@raspberrypi ~ $ cd codes pi@raspberrypi ~/codes $ ls -l pi@raspberrypi ~/codes $ mkdir PIR pi@raspberrypi ~/codes $ cd PIR pi@raspberrypi ~/codes/PIR $ wget -O - http://goo.gl/9C5n3 | tar -xvf - --2013-03-21 12:46:43-- http://goo.gl/9C5n3 正在查找主機 goo.gl (goo.gl)... 74.125.31.101, 74.125.31.102, 74.125.31.113, ... 正在連接 goo.gl (goo.gl)|74.125.31.101|:80... 連上了。 已送出 HTTP 要求,正在等候回應... 301 Moved Permanently 位置: http://dl.dropbox.com/s/ig42n7n599x5y6k/PIR_Codes_RPi.tar [跟隨至新的 URL] --2013-03-21 12:46:43-- http://dl.dropbox.com/s/ig42n7n599x5y6k/PIR_Codes_RPi.tar 正在查找主機 dl.dropbox.com (dl.dropbox.com)... 54.243.103.51 正在連接 dl.dropbox.com (dl.dropbox.com)|54.243.103.51|:80... 連上了。 已送出 HTTP 要求,正在等候回應... 200 OK 長度: 61440 (60K) [application/x-tar] Saving to: `STDOUT' 0% [ ] 0 --.-K/s PIR_Relay_sendemail.c PIR_Relay_sendemail.py 25% [==============> ] 15,555 64.7K/s PIR_sendemail.c PIR_sendemail.py PIR_test 100%[============================================================>] 61,440 125K/s in 0.5s 2013-03-21 12:46:45 (125 KB/s) - written to stdout [61440/61440] PIR_test.c PIR_test.py pi@raspberrypi ~/codes/PIR $ ls -l 總計 68 -rwxr-xr-x 1 pi pi 6832 3月 21 12:42 PIR_Relay_sendemail.c -rwxr-xr-x 1 pi pi 2411 3月 21 12:42 PIR_Relay_sendemail.py -rwxr-xr-x 1 pi pi 6329 3月 21 12:42 PIR_sendemail.c -rwxr-xr-x 1 pi pi 2004 3月 21 12:42 PIR_sendemail.py -rwxr-xr-x 1 pi pi 34455 3月 21 12:42 PIR_test -rwxr-xr-x 1 pi pi 2347 3月 21 12:42 PIR_test.c -rwxr-xr-x 1 pi pi 922 3月 21 12:42 PIR_test.py pi@raspberrypi ~/codes/PIR $
~/codes/PIR 目錄下有七個檔案,沒有附檔名的檔案是 *.c 檔案編譯之後的執行檔;但其中兩個 *.c 檔案未做編譯,這要先設定郵件信箱資料資料才能做編譯,下一段會說到。
在列出程式碼之前,先做 Python 與 C 語言程式碼的測試,確認 PIR 能夠正常動作。
輸入下面指令,先測試 C 編譯後執行檔
pi@raspberrypi ~/codes/PIR $ sudo ./PIR_test Waitting for PIR to stable, 1 sec READY ... PIR sensor get ready for movement detection
程式會開始倒數 3、2、1 秒,完成之後就會開始以迴圈的方式一直讀取 PIR 模組的觸發輸出訊號。這時用手在 PIR 前方晃晃,PIR 模組就會觸發,當樹莓派接收訊號時,上面最後一行的文字就會變成 "THE PIR SENSOR DETECTED MOVEMENT"
pi@raspberrypi ~/codes/PIR $ sudo ./PIR_test Waitting for PIR to stable, 1 sec READY ... THE PIR SENSOR DETECTED MOVEMENT
要結束程式請按下 "Ctrl + C"
程式一開始的延遲時間是為了讓 PIR 模組穩定用的,這段穩定時間只用在 PIR 模組一通上電後的 20 ~ 60 秒 (一般設 20 秒就夠了),但因為 PIR 模組在這個例子插上後不會馬上使用,所以這段穩定時間可有可無,設 3 秒是意思意思,主要是為了讓閱讀程式碼的人能夠了解 PIR 模組使用上有這個限制!
不過一旦你使用接腳來控制 PIR 模組電源,每一次開啟就必須要設這段穩定時間,沒有這段穩定時間會造成一開始的接收訊號不正確,造成誤動作,這很重要的!
接著,輸入下面指令執行 Python 程式碼的測試:
pi@raspberrypi ~/codes/PIR $ sudo python PIR_test.py Waitting for PIR to stable, 1 sec READY ... PIR sensor get ready for movement detection
程式執行的結果跟 C 語言的一樣。同樣的也用手在 PIR 模組前面晃晃,最後一行的文字就會產生變化。要結束就按下 "Ctrl + C" ,比較不同的是,會另外出現 "Quit!" 的文字,告訴你離開程式了。
pi@raspberrypi ~/codes/PIR $ sudo python PIR_test.py Waitting for PIR to stable, 1 sec READY ... PIR sensor get ready for movement detection Quit! pi@raspberrypi ~/codes/PIR $
上面就是兩個不同程式語言測試的結果。
兩個程式原始碼如下:
C 語言
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #include <stdio.h> // printf, scanf #include <stdlib.h> #include <bcm2835.h> #define PIN_PIR 18 #define STB_TIME 3 int main(int argc, char** argv) { // bcm2835 函式庫初始化 if(!bcm2835_init()) return 1; // IO 初始化與初始狀態設定 bcm2835_gpio_fsel(PIN_PIR, BCM2835_GPIO_FSEL_INPT); // pull-down the control pin of PIR bcm2835_gpio_set_pud(PIN_PIR, BCM2835_GPIO_PUD_DOWN); // PIR 接上電源後須要等待 20 秒才會穩定,穩定時間內會有不穩定的輸出, // 因此不需要偵測此時的 PIR 狀態 printf("\n"); for(int i = 0; i < STB_TIME; i++) { bcm2835_delay(1000); printf(" Waitting for PIR to stable, %d sec\r", STB_TIME - i); fflush(stdout); } printf("\n\n READY ...\n\n"); while(1) { // 現在可以用手或是身體在 PIR 前面晃動 if(bcm2835_gpio_lev(PIN_PIR)) { printf("THE PIR SENSOR DETECTED MOVEMENT \r"); fflush(stdout); bcm2835_delay(3000); // 此時間的設定必須要大於 PIR 的延遲時間 } else printf("PIR sensor get ready for movement detection\r"); } bcm2835_close(); return 0; } |
Line 05 - 06:使用樹莓派 GPIO #18 做為 PIR 模組的輸入接腳
Line 15 - 18:設定 GPIO #18 為輸入,並設為下拉 (與 GND 接在一起);也就是 GPIO #18 無輸入訊號時所讀取到的輸入為 0
Line 23 - 28:PIR 模組啟用暖機時間倒數
Line 32 - 43:PIR 模組暖機完成,樹莓派可以開始接收輸入訊號。函數 fflush(stdout) 是將 stdout 裡的東西強制輸出緩衝並清空,沒有這一行,文字 " THE PIR SENSOR DETECTED MOVEMENT" 是不會被輸出的,你可以試試!
上面那兩個程式如果有做修改需要重新編譯,就輸入下面指令:
sudo gcc PIR_test.c -l rt -l bcm2835 -std=gnu99 -o PIR_test
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #!/usr/bin/python #!-*- coding: utf-8 -*- import time import sys import RPi.GPIO as GPIO # 使用 BCM GPIO 取代實際接腳號碼 GPIO.setmode(GPIO.BCM) # 定義樹莓派連接到 PIR 的接腳 PIN_PIR = 18 STB_TIME = 3 try: GPIO.setup(PIN_PIR, GPIO.IN) print "\n", for i in range(0, STB_TIME): print " Waitting for PIR to stable, %d sec\r" %(STB_TIME - i), sys.stdout.flush() time.sleep(1) print "\n" print " READY ...", print "\n" # 按下 Ctrl + C 離開 while True: if(GPIO.input(PIN_PIR)): print "THE PIR SENSOR DETECTED MOVEMENT \r", sys.stdout.flush() time.sleep(3) # 此時間的設定必須要大於 PIR 的延遲時間 else: print "PIR sensor get ready for movement detection\r", except KeyboardInterrupt: print "\n\n Quit! \n" |
Python 的程式碼就不再講了,因為兩個程式架構幾乎一樣,所不同的是:當您按下 "Ctrl + C" 時的中斷訊號會被程式碼的 "KeyboardInterrupt" 截取到,因此會輸出 "Quit!" 文字。
加入電子郵件傳送功能:
發送電子郵件必須先知道要使用哪個電子郵件伺服器發送,因此郵件伺服器的網址以及所用的 PORT 號碼也必須先知道,另外就是需不需要使用者的名稱與密碼才能登入 ?
延續上面兩個程式,各在其中加入傳送電子郵件功能,所使用的郵件伺服器是 GMail,如果你之後要使用其他郵件伺服器的話,就必須自己再做其他修改。不過如果你使用智慧型手機 ( Android ) 又需要即時的郵件通知,那真的建議你使用 GMail 做郵件伺服器。
接下來,我們就以 GMail 郵件伺服器做例子 ( 其他郵件伺服器設定也大同小異 ),先看看 GMail 郵件伺服器連線的提示:在郵件用戶端內設定 POP
內送郵件 (POP3) 伺服器 - 需要安全資料傳輸層 (SSL): | pop.gmail.com 使用安全資料傳輸層 (SSL):是 通訊埠:995 |
外寄郵件 (SMTP) 伺服器 - 需要 TLS 或安全資料傳輸層 (SSL): | smtp.gmail.com 使用驗證:是 TLS/STARTTLS 通訊埠:587 安全資料傳輸層 (SSL) 通訊埠:465 |
伺服器逾時 | 1 分鐘以上,建議設定為 5 分鐘 |
姓名或顯示名稱: | [您的姓名] |
帳戶名稱或使用者名稱: | 您的完整電子郵件地址 (包含 @Gmail.com 或 @<您的網域>.com ) |
電子郵件地址: | 您的電子郵件地址 (使用者名稱@gmail.com 或使用者名稱@您的網域.com) |
密碼: | 您的 Gmail 密碼 |
上面表格比較重要的是第二個欄位,使用需驗證的外部郵件伺服器 smtp.gmail.com,TLS/STARTTLS 通訊埠號碼為 586,這兩個就是 GMail 外送郵件伺服器連線的資訊,請記下來,下面就會用到。GMail 使用者帳號與密碼就是只有你才會知道的資料,等一下也請一併輸入。
我們先假設一個虛擬的帳號與密碼,讓說明方便一些。實際使用上,除了伺服器的資訊之外,帳號與密碼請輸入你自己的來做取代。
外送郵件伺服器 :smtp.gmail.com
TLS/STARTTLS 通訊埠 :587
使用者名稱 ( GMAIL_USER ):proteus
使用者密碼 ( GMAIL_PASS ) :1234
寄給誰 ( TO ) :someone@mail.com
郵件主旨 ( SUBJECT ) :Intrusion!!
郵件內文 ( TEXT ) :Your PIR sensor detected movement
有了上面的資訊之後,只要將發送電子郵件的程式碼寫好,做成 send_email() 副程式來呼叫就可以了。
Python - 傳送電子郵件
樹莓派本身已有安裝 Python 以及 smtplib 函式庫,因此在使用時只要在檔案開頭處將其引入之後就可以開始使用
import smtplib
接著將電子郵件傳送的相關資料定義在程式開頭處
TO = 'someone@mail.com' GMAIL_USER = 'proteus' GMAIL_PASS = '1234' SUBJECT = 'Intrusion!!' TEXT = 'Your PIR sensor detected movement'
傳送電子郵件有一定的傳送格式 ( 請參閱 RFC 5322 - Internet Message Format,或電子郵件標頭解析 ),需要將上面的資料放在一塊後做成郵件資料再一起發送。
連線伺服器 --> 登入 --> 傳送郵件資料 --> 離線伺服器
使用 Python 撰寫比較直覺,程式用不了幾行
1 def send_email(): 2 print "\n" 3 print "Sending Email" 4 smtpserver = smtplib.SMTP("smtp.gmail.com",587) # 不是使用 GMail 的要修改這裡 5 smtpserver.ehlo() 6 smtpserver.starttls() 7 smtpserver.ehlo 8 smtpserver.login(GMAIL_USER, GMAIL_PASS) 9 header = 'To:' + TO + '\n' + 'From: ' + GMAIL_USER 10 header = header + '\n' + 'Subject:' + SUBJECT + '\n' 11 print header 12 msg = header + '\n' + TEXT + ' \n\n' 13 smtpserver.sendmail(GMAIL_USER, TO, msg) 14 smtpserver.close()
Line 04:GMail 外送郵件伺服器的位址與 PORT 的號碼,這裡是分開輸入的
主程式的地方,只要加入 send_email() 這行在 time.sleep(3) 上方就完成增加傳送電子郵件的功能
while True: if(GPIO.input(PIN_PIR)): print "THE PIR SENSOR DETECTED MOVEMENT \r", sys.stdout.flush() # 如果要傳送電子郵件的話,就將下一行前面的 # 拿掉 send_email() time.sleep(3) # 此時間的設定必須要大於 PIR 的延遲時間 else: print "PIR sensor get ready for movement detection\r",
一旦程式接收到 PIR 模組的觸發訊號,就會開始傳送電子郵件
pi@raspberrypi ~/codes/PIR $ sudo python PIR_sendmail.py Waitting >for PIR to stable, 1 sec READY ... THE PIR SENSOR DETECTED MOVEMENT Sending Email To:someone@mail.com From: proteus Subject:Intrusion!! PIR sensor get ready for movement detection Quit! pi@raspberrypi ~/codes/PIR $
C - 傳送電子郵件
使用 C 語言傳送電子郵件,我所選擇的函式庫是 libcurl,原因是看起來比較直覺,設定上與 Python 方式差不多,即便是程式碼撰寫 ( 跟 Python 比 ) 長了一點,但比較其他多個函式庫後,這個是最容易使用且功能很強的。
輸入下面指令進行函式庫的安裝 ( 如果您超過一星期未更新過系統了,第一行就一定要做 ),順便也更新一下 Python 的 RPi.GPIO 函式庫 ( 版本 0.5.1a,這一版支援中斷,請看 RasPi.TV 的 說明 "How to use interrupts with Python on the Raspberry Pi and RPi.GPIO",若是使用中斷的方式,可以省下很多的 CPU 時間,而不用使用像本文所使用的輪詢方式,效率會更好!這部分就請看倌自己弄了!)。請進行相關套件的安裝:
pi@raspberrypi ~ $ sudo apt-get update && sudo apt-get upgrade -y ... << 中間省略 >> ... pi@raspberrypi ~ $ sudo apt-get install python-rpi.gpio 正在讀取套件清單... 完成 正在重建相依關係 正在讀取狀態資料... 完成 The following packages were automatically installed and are no longer required: icelib libblas3gf libdns81 libisc83 liblapack3gf libqt4-dbus libtinfo-dev qdbus Use 'apt-get autoremove' to remove them. 下列【新】套件將會被安裝: python-rpi.gpio 升級 0 個,新安裝 1 個,移除 0 個,有 0 個未被升級。 需要下載 28.2 kB 的套件檔。 此操作完成之後,會多佔用 158 kB 的磁碟空間。 下載:1 http://archive.raspberrypi.org/debian/ wheezy/main python-rpi.gpio armhf 0.5.0a-1 [28.2 kB] 取得 28.2 kB 用了 1s (15.6 kB/s) 選取了原先未選的套件 python-rpi.gpio。 (讀取資料庫 ... 目前共安裝了 63447 個檔案和目錄。) 解開 python-rpi.gpio(從 .../python-rpi.gpio_0.5.0a-1_armhf.deb)... 設定 python-rpi.gpio (0.5.0a-1) ... pi@raspberrypi ~ $ sudo apt-get install libcurl4-openssl-dev 正在讀取套件清單... 完成 正在重建相依關係 正在讀取狀態資料... 完成 The following packages were automatically installed and are no longer required: icelib libblas3gf libdns81 libisc83 liblapack3gf libqt4-dbus libtinfo-dev qdbus Use 'apt-get autoremove' to remove them. 下列的額外套件將被安裝: comerr-dev krb5-multidev libgcrypt11-dev libgnutls-dev libgnutls-openssl27 libgnutlsxx27 libgpg-error-dev libgssrpc4 libidn11-dev libkadm5clnt-mit8 libkadm5srv-mit8 libkdb5-6 libkrb5-dev libldap2-dev libp11-kit-dev librtmp-dev libssh2-1-dev libtasn1-3-dev 建議套件: doc-base krb5-doc libcurl3-dbg libgcrypt11-doc gnutls26-doc krb5-user 下列【新】套件將會被安裝: comerr-dev krb5-multidev libcurl4-openssl-dev libgcrypt11-dev libgnutls-dev libgnutls-openssl27 libgnutlsxx27 libgpg-error-dev libgssrpc4 libidn11-dev libkadm5clnt-mit8 libkadm5srv-mit8 libkdb5-6 libkrb5-dev libldap2-dev libp11-kit-dev librtmp-dev libssh2-1-dev libtasn1-3-dev 升級 0 個,新安裝 19 個,移除 0 個,有 0 個未被升級。 需要下載 5,323 kB 的套件檔。 此操作完成之後,會多佔用 11.2 MB 的磁碟空間。 是否繼續進行 [Y/n]?Y ... << 中間省略 >> ... pi@raspberrypi ~ $
libcurl 的功能以及範例程式很多,在它的官網 libcurl - small example snippets 可以看到,網頁右邊列出所有的範例程式,請點擊 smtp_.tls.c 並看一下它裡面的內容,我們將改寫這個程式成為 send_email() 副程式。
在 PIR_test.c 程式的標頭檔定義的地方,加入
#include <curl/curl.h> // libcurl 標頭檔
同樣的,在程式最前面定義幾個會用到的資料,比 Python 定義的多出一個 CC
#define GMAIL_USER "proteus" // GMail 帳號 #define GMAIL_PASS "1234" // GMail 密碼 #define TO "<someone@mail.com>" // GMail 信箱 // 傳送給何人 // define CC "<another@mail.com>" // 如果有副本寄件人的話就把前面的雙斜線拿掉 #define SUBJECT "Intrusion!!" #define TEXT "Your PIR sensor detected movement"
在 main() 的上方輸入下面程式碼,這是結合上面資料的郵件訊息內容,有些內容因為沒用到我加上了雙斜線取消掉,不過你可以對照一下 smtp_tls.c 原始程式內容,就會知道有哪裡不同
static const char *payload_text[]={ "To: " TO "\n", "From: " GMAIL_USER "\n", //"Cc: " CC "\n", // 如果有副本寄件人的話就把前面的雙斜線拿掉 "Subject: " SUBJECT "\n", "\n", /* empty line to divide headers from body, see RFC5322 */ // 下面開始就是文件內文 //"The body of the message starts here.\n", //"\n", //"It could be a lot of lines, could be MIME encoded, whatever.\n", //"Check RFC5322.\n", TEXT, NULL };
下面是兩個 send_email() 裡會用到的副程式,主要是 payload_text 陣列裡的字數計算
struct upload_status { int lines_read; }; static size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp) { struct upload_status *upload_ctx = (struct upload_status *)userp; const char *data; if ((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) { return 0; } data = payload_text[upload_ctx->lines_read]; if (data) { size_t len = strlen(data); memcpy(ptr, data, len); upload_ctx->lines_read ++; return len; } return 0; }
下面就是 send_email() 的程式碼,需要修改的地方在程式碼下方做說明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | void send_email(void) { CURL *curl; CURLcode res; struct curl_slist *recipients = NULL; struct upload_status upload_ctx; upload_ctx.lines_read = 0; curl = curl_easy_init(); if (curl) { /* 設定郵件伺服器,請注意到網址後面要加上 587 取代 SMTP 正常使用的 PORT 25 * ,Port 587 是常用於安全郵件的提交, 但是這裡需視所使用的郵件伺服器加以設定 * 以符合自己的情形 */ curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.gmail.com:587"); /* In this example, we'll start with a plain text connection, and upgrade * to Transport Layer Security (TLS) using the STARTTLS command. Be careful * of using CURLUSESSL_TRY here, because if TLS upgrade fails, the transfer * will continue anyway - see the security discussion in the libcurl * tutorial for more details. */ curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL); /* 因為是使用 Gmail 伺服器所以要登入之後才能使用*/ curl_easy_setopt(curl, CURLOPT_USERNAME, GMAIL_USER); // GMAIL_USER curl_easy_setopt(curl, CURLOPT_PASSWORD, GMAIL_PASS); // GMAIL_PASS /* value for envelope reverse-path */ curl_easy_setopt(curl, CURLOPT_MAIL_FROM, GMAIL_USER); // GMAIL_USER /* Add two recipients, in this particular case they correspond to the * To: and Cc: addressees in the header, but they could be any kind of * recipient. */ recipients = curl_slist_append(recipients, TO); //recipients = curl_slist_append(recipients, CC); // 如果有副本寄件人的話就把前面的雙斜線拿掉 curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); /* In this case, we're using a callback function to specify the data. You * could just use the CURLOPT_READDATA option to specify a FILE pointer to * read from. */ curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source); curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); /* Since the traffic will be encrypted, it is very useful to turn on debug * information within libcurl to see what is happening during the transfer. */ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* send the message (including headers) */ res = curl_easy_perform(curl); /* Check for errors */ if(res != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); /* free the list of recipients and clean up */ curl_slist_free_all(recipients); curl_easy_cleanup(curl); } printf("\n\n"); } |
Line 15:GMail 外送郵件伺服器的位址和 PORT 號碼,這裡兩個是寫在一起的,因此要輸入 smtp://smtp.gmail.com:587,PORT 號碼接在網址後面要在前面加上冒號 (:)
Line 25 - 26:登入 GMail 郵件伺服器的使用者資訊,這裡使用我們自己定義的使用者名稱 ( GMAIL_USER ) 與密碼 ( GMAIL_PASS )
Line 29:輸入寄件者的名稱,也就是回信的位址。因為是使用 GMail,所以只要輸入使用者名稱 ( GMAIL_USER ) 即可
Line 34:這一行主要是用在寄送郵件副本的,如果有使用到就將前方的雙斜線移除掉,就可在發送郵件時另外寄送副本給指定的郵件帳戶。
主程式的部分,也是增加 send_email() 在時間函數 bcm2835_delay(3000) 的上方就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 | while(1) { // 現在可以用手或是身體在 PIR 前面晃動 if(bcm2835_gpio_lev(PIN_PIR)) { printf("THE PIR SENSOR DETECTED MOVEMENT \r\n\n"); fflush(stdout); send_email(); bcm2835_delay(3000); // 此時間的設定必須要大於 PIR 的延遲時間 } else printf("PIR sensor get ready for movement detection\r"); } |
完成程式修改之後,記得這程式使用 libcurl 函式庫,因此原本的編譯方式變成
sudo gcc PIR_sendemail.c -l rt -l bcm2835 -l curl -std=gnu99 -o PIR_sendemail
編譯好之後就可以做測試,一旦 PIR 模組觸發,就會開始與外寄郵件伺服器連線,並開始在視窗中輸出兩方通訊訊息。要結束就 "Ctrl + C"
pi@raspberrypi ~/codes/PIR $ sudo ./PIR_sendemail Waitting for PIR to stable, 1 sec READY ... THE PIR SENSOR DETECTED MOVEMENT * About to connect() to smtp.gmail.com port 587 (#0) * Trying 74.125.142.108... * connected * Connected to smtp.gmail.com (74.125.142.108) port 587 (#0) * SMTP 0x1f52218 state change from STOP to SERVERGREET < 220 mx.google.com ESMTP s8sm14378176igs.0 - gsmtp > EHLO raspberrypi * SMTP 0x1f52218 state change from SERVERGREET to EHLO < 250-mx.google.com at your service, [36.239.198.208] * Closing connection #0 ... << 中間省略 >> ... PIR sensor get ready for movement pi@raspberrypi ~/codes/PIR $
加入繼電器開關功能:
最後一個部份就是加入了繼電器的功能。繼電器可以外接不同電壓的設備,只要在其額定限制之下都可以 (例如,蜂鳴器、馬達和電燈等),可以依自己的需求加入所要控制的設備。
使用 PIR 模組的觸發訊號,除了上一節中加入了發送電子郵件的功能,這一節就是加入繼電器來控制交流電源的開或關,交流電源與電燈接在一起。詳細的電路接線圖如下:
使用樹莓派連接 PIR 模組與繼電器控制電燈開或關的線路圖 |
根據上面的線路圖,先將繼電器與 PIR 模組接到樹莓派上去;除非你有把握能一次將線接好,不然接好之後確認沒問題之後,再插上網路線與電源開機
PIR 模組與繼電器模組與樹莓派的接線 |
好了之後就可以開始測試加入了繼電器功能的程式了!
C - 繼電器控制電燈開或關
繼續上一節的程式,加入繼電器部分所要加入的程式碼不多,可以開啟 PIR_Relay_sendemail.c 做對照。
首先,在 STD_TIME 的下方輸入繼電器控制接腳的定義
#define STB_TIME 3 #define PIN_RELAY 23 // 定義樹莓派控制繼電器模組的接腳
在 mail() 主程式開頭處,將接腳 GPIO #23 設為輸出且初始狀態為 HIGH ( 還記得賣場的繼電器模組是低準位觸發嗎 ? )
// IO 初始化與初始狀態設定 bcm2835_gpio_fsel(PIN_PIR, BCM2835_GPIO_FSEL_INPT); bcm2835_gpio_fsel(PIN_RELAY, BCM2835_GPIO_FSEL_OUTP); //定義連接繼電器的接腳為輸出 // pull-down the control pin of PIR bcm2835_gpio_set_pud(PIN_PIR, BCM2835_GPIO_PUD_DOWN); bcm2835_gpio_write(PIN_RELAY, HIGH); // 設定繼電器初始狀態
接著在無窮迴圈 while(1) 裡加入控制繼電器模組開或關的指令 (也就是下面的 Line 9 和 Line 14),並加入 fflush (stdio) 在 Line 16,加入繼電器的功能就算完成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | while(1) { // 現在可以用手或是身體在 PIR 前面晃動 if(bcm2835_gpio_lev(PIN_PIR)) { printf("THE PIR SENSOR DETECTED MOVEMENT \r\n\n"); fflush(stdout); send_email(); bcm2835_gpio_write(PIN_RELAY, LOW); // 繼電器輸出 bcm2835_delay(3000); // 此時間的設定必須要大於 PIR 的延遲時間 } else { bcm2835_gpio_write(PIN_RELAY, HIGH); // 繼電器輸出 printf("PIR sensor get ready for movement detection\r"); fflush(stdout); } } |
程式的編譯請在程式所在目錄下輸入下面指令
sudo gcc PIR_sendemail.c -l rt -l bcm2835 -l curl -std=gnu99 -o PIR_sendemail
Python - 繼電器控制電燈開或關
繼續上一節的程式,加入繼電器部分所要加入的程式碼不多,可以開啟 PIR_Relay_sendemail.py 做對照。
在 Python 前方 STM_TIME 下面定義繼電器的接腳
STB_TIME = 3 PIN_RELAY = 23 # 定義樹莓派控制繼電器模組的接腳
在 try 區段中,初始化 PIN_RELAY 接腳為輸出,初始狀態為 HIGH
try: GPIO.setup(PIN_PIR, GPIO.IN) GPIO.setup(PIN_RELAY, GPIO.OUT, GPIO.HIGH) # 定義連接繼電器的接腳為輸出,初始狀態為 HIGH
繼續在 try 區塊的無窮迴圈 while 裡,time.sleep(3) 上面加入輸出高準位的指令 GPIO.output(PIN_RELAY, HIGH),並在 else 下輸入一行低準位輸出的指令 GPIO.output(PIN_RELAY, LOW)
while True: if(GPIO.input(PIN_PIR)): print "THE PIR SENSOR DETECTED MOVEMENT \r", sys.stdout.flush() send_email() GPIO.output(PIN_RELAY, GPIO.LOW) # 繼電器輸出 time.sleep(3) # 此時間的設定必須要大於 PIR 的延遲時間 else: GPIO.output(PIN_RELAY, GPIO.HIGH) # 繼電器輸出 print "PIR sensor get ready for movement detection\r",
依照上面的說明,Python 程式碼就算完成了,接下來就是測試。
測試:
程式碼的執行,可參考上一兩節的說明,現在要做的就是將電燈的電源線與繼電器相連接。
交流電很危險,若對接線沒把握,請有經驗的人幫您做確認,確認沒問題之後再插上插座通電 !!
繼電器與電燈的接線如下圖所示,白色線是同一條線切斷之後接上端子在接到繼電器端子座的 K2-NO 與 K2-COM 處
繼電器與 AC 電源接線近觀 |
當你開始執行程式的時候,一旦 PIR 模組被觸發,電燈就會像下圖一樣亮起一段時間之後再熄滅
返回 "人體紅外線 (PIR) 感應模組系列文章" 看其他單元
<<樹莓派編輯環境設置系列文章>>
- 在 Windows 設置 Raspberry Pi (樹莓派) 遠端編輯環境 [第一發] 工欲善其事,必先利其器
- 把 Windows 桌面當作是 Raspberry Pi (樹莓派) 的螢幕 [第二發] 工欲善其事,必先利其器
- 遠端桌面 - VNC [第三發] 工欲善其事,必先利其器
Sublime Text 2 與遠端 (樹莓派) 資料夾連線畫面 |
MobaXterm: Windows 作業系統下開啟樹莓派作業系統的 LXDE 桌面 |
VNC |
請問程式執行後出現下面這個錯誤訊息該麼解決?
回覆刪除Traceback (most recent call last):
File "/home/pi/Desktop/PIR_test.py", line 41, in
GPIO.setup(PIN_RELAY, GPIO.OUT, GPIO.HIGH) # 定義連接繼電器的接腳為輸出,初始狀態為 HIGH
ValueError: pull_up_down parameter is not valid for outputs
網頁中的 python 程式或許會因為版本的更新讓函式使用有所不同,但是可以先查閱一下該函式庫的使用說明網頁 https://sourceforge.net/p/raspberry-gpio-python/wiki/BasicUsage/ 就可以知道不同的地方。
刪除試試改成:GPIO.setup(PIN_RELAY, GPIO.OUT, initial=GPIO.HIGH)
問題已解決! 感謝:)
刪除大大您好:
刪除我是最近剛學 Arduino 的新手, 請問您 樹莓派的主板 和 Arduino有一樣嗎? 因為我也想用Arduino的主板和PIR Sensor模組 做一個 警報發送郵件 電路 和LED都有感應作動 但我不會寫發送郵件的程式, 想說如果有一樣的話, 可以照抄你的程式碼到Arduino編譯上傳嗎
兩者是不一樣的! 如果要單獨用 Arduino 發送電子郵件的話,可以外掛網路模組或是 ESP8266 來做!
刪除