在蘑菇云「行空板Python入門教程」第九節(jié)課上,我們設(shè)計了一個智慧農(nóng)業(yè)物聯(lián)網(wǎng)系統(tǒng),通過一塊行空板來檢測植物生長時的土壤濕度情況,將濕度數(shù)據(jù)上傳到SIoT物聯(lián)網(wǎng)平臺進(jìn)行遠(yuǎn)程查看并在濕度較低時從平臺遠(yuǎn)程控制澆水。
然而,在真實的農(nóng)業(yè)場景中,常常需要檢測不同場所內(nèi)的多樣數(shù)據(jù),并匯總到一個平臺總站以便遠(yuǎn)程訪問。
那么,在這節(jié)課上,讓我們借助DFRobot的3塊行空板,一起來設(shè)計一個多節(jié)點的智慧農(nóng)業(yè)系統(tǒng),模擬一下真實的農(nóng)業(yè)場景吧。
行空板模擬智慧農(nóng)業(yè)系統(tǒng)
任務(wù)目標(biāo)
準(zhǔn)備多塊行空板(以3塊為例),將所有行空板與電腦連接到同一個局域網(wǎng)段內(nèi)。之后單獨在第三塊行空板上開啟SIoT應(yīng)用作為服務(wù)器,將通過第一塊行空板檢測到的土壤濕度數(shù)據(jù)和第二塊行空板檢測到的光線值數(shù)據(jù),各自顯示在屏幕上的同時,也發(fā)送到板3的SIoT物聯(lián)網(wǎng)平臺上;最后,使第三塊行空板訂閱物聯(lián)網(wǎng)平臺接收到的消息,將板1的土壤濕度情況和板2的光線強(qiáng)度情況一起顯示在屏幕上,并且,當(dāng)檢測到的兩個環(huán)境數(shù)據(jù)不佳時,自動給我們的郵箱發(fā)送一封警報郵件,以此來提醒我們及時澆水和補(bǔ)光。
知識點
1、學(xué)習(xí)使用3塊行空板搭建多節(jié)點物聯(lián)網(wǎng)系統(tǒng)的方法
2、掌握給行空板供電的多種不同的方式
3、掌握遠(yuǎn)程連接行空板并運行程序的不同方法
4、學(xué)習(xí)使用smtplib庫發(fā)送郵件的流程
5、學(xué)習(xí)使用smtplib庫發(fā)送郵件的具體方法
材料清單
硬件清單:
多節(jié)點智慧農(nóng)業(yè)系統(tǒng)需要的硬件材料
軟件使用:Mind+編程軟件x1
其他: 1、帶植物的花盆 x1
2、盛有水的燒杯 x1
3、十字/一字兩用螺絲刀 x1
知識儲備
1、smtplib庫與email庫
使用Python編程來發(fā)送郵件需要兩個關(guān)鍵步驟,一步是構(gòu)造郵件信息內(nèi)容,另一步是發(fā)送郵件。前者,我們可以借助email庫來實現(xiàn),后者,我們可以使用smtplib庫來進(jìn)行。
2、email.mime.text包MIMEText模塊創(chuàng)建郵件文本
email.mime.text包的MIMEText模塊可在構(gòu)造郵件時創(chuàng)建文本內(nèi)容,使用時,需要先導(dǎo)入該模塊,之后以實例化MIMEText()類的形式來創(chuàng)建文本對象。
from email.mime.text import MIMEText # 導(dǎo)入email.mime.text包中的MIMEText模塊
msg=MIMEText('Hello World','plain','utf-8') # 創(chuàng)建郵件文本對象,‘Hello World’對應(yīng)文本內(nèi)容,'plain'指設(shè)置的文本格式,'utf-8'指設(shè)置的編碼
其中,“Hello World”指要發(fā)送的文本內(nèi)容,“plain”指文本的格式,“utf-8”指的是編碼。
3、email.utils包formataddr模塊格式化內(nèi)容
email.utils包的 formataddr模塊可在構(gòu)造郵件時將輸入的內(nèi)容進(jìn)行格式化操作,以便郵件服務(wù)器能夠識別。使用時,需要先導(dǎo)入該模塊。
from email.utils import formataddr # 導(dǎo)入formataddr模塊,負(fù)責(zé)將輸入的內(nèi)容格式化
'''三個頭部信息:發(fā)件人,收件人,主題'''
msg['From']=formataddr([my_name,my_sender]) # 定義發(fā)件人信息:括號里的對應(yīng)發(fā)件人郵箱昵稱、發(fā)件人郵箱賬號
msg['To']=formataddr([my_user_name,my_user]) # 定義收件人信息:括號里的對應(yīng)收件人郵箱昵稱、收件人郵箱賬號
msg['Subject']= '郵件測試' # 定義郵件的主題,也可以說是標(biāo)題
其中,“msg['From']”表示郵件信息中發(fā)件人信息,包括郵箱、昵稱;“msg['To']”表示郵件信息中收件人的信息,包括郵箱、昵稱;“msg['Subject']”表示郵件的主題,文本內(nèi)容“郵件測試”是它的具體內(nèi)容。
這里,我們通過“formataddr()”函數(shù)將郵件的發(fā)件人信息和收件人信息文本進(jìn)行了格式化操作。
4、smtplib庫各功能函數(shù)
smtplib庫可實現(xiàn)發(fā)送郵件功能,使用時,首先需要先導(dǎo)入該庫,其次,通過其中的“SMTP_SSL()”函數(shù)來創(chuàng)建一個SMTP服務(wù)對象以連接郵箱,接著通過“l(fā)ogin()”函數(shù)登錄郵箱,之后使用“sendmail()”函數(shù)即可發(fā)送郵件,最后通過“quit()”函數(shù)退出與郵箱服務(wù)器的連接。
(1)smtplib庫SMTP_SSL()函數(shù)創(chuàng)建SMTP服務(wù)對象
smtplib庫中的SMTP_SSL()函數(shù)可創(chuàng)建SMTP服務(wù)對象以連接郵箱。使用時,首先需要先導(dǎo)入該庫。 import smtplib # 導(dǎo)入smtplib庫
server=smtplib.SMTP_SSL("smtp.qq.com", 465) # 創(chuàng)建SMTP服務(wù),連接qq郵箱服務(wù)器,發(fā)件人郵箱中的SMTP服務(wù)器,SMTP協(xié)議加密端口是465
其中,“smtp.qq.com”指的是QQ郵箱服務(wù)器,“465”表示 SMTP協(xié)議加密端口。
(2)smtplib庫login()函數(shù)登錄郵箱
smtplib庫中的login()函數(shù)可實現(xiàn)對發(fā)件人郵箱的登錄。使用時,需設(shè)定好所要登錄郵箱的賬號和第三方登錄授權(quán)碼。
my_sender='10******69@qq.com' # 設(shè)置發(fā)件人郵箱賬號,輸入自己的郵箱
my_pass = 'sq******ga' # 設(shè)置發(fā)件人郵箱授權(quán)碼
server.login(my_sender, my_pass) # 登錄郵箱,括號中對應(yīng)的是發(fā)件人郵箱賬號、郵箱密碼
其中,“my_sender”指的是發(fā)件人郵箱賬號,“my_pass”指的是發(fā)件人郵箱授權(quán)碼。
(3)smtplib庫sendmail()函數(shù)發(fā)送郵件
smtplib庫中的sendmail()函數(shù)可實現(xiàn)郵件的發(fā)送。使用時,需設(shè)定好發(fā)件人郵箱賬號、收件人郵箱賬號和所要發(fā)送郵件信息內(nèi)容。
msg=MIMEText(content,'plain','utf-8') # 創(chuàng)建郵件文本對象,Title對應(yīng)文本內(nèi)容,'plain'指設(shè)置的文本格式,'utf-8'指設(shè)置的編碼
my_sender='10******69@qq.com' # 設(shè)置發(fā)件人郵箱賬號,輸入自己的郵箱
my_user='10******69@qq.com' # 設(shè)置收件人郵箱賬號,我這邊發(fā)送給自己
server.sendmail(my_sender,my_user,msg.as_string()) # 發(fā)送郵件,括號中對應(yīng)的是發(fā)件人郵箱賬號、收件人郵箱賬號、郵件信息的字符串格式
其中,“my_sender”指的是發(fā)件人郵箱賬號,“my_user”指的是收件人郵箱賬號,“msg.as_string()”指的是將郵件定義為字符串格式。
(4)smtplib庫quit()函數(shù)關(guān)閉連接
smtplib庫中的quit()函數(shù)可關(guān)閉與郵箱服務(wù)器之間的連接。
server.quit() # 關(guān)閉連接
動手實踐
任務(wù)描述1:前期準(zhǔn)備
1、分析設(shè)計
在這個項目中,我們將完成多節(jié)點智慧農(nóng)業(yè)系統(tǒng)的設(shè)計。首先,我們需要將3塊行空板與電腦都連接在同一個局域網(wǎng)內(nèi)(借助路由器/手機(jī)熱點);之后,我們要將每一塊板子輪流使用USB線連接電腦進(jìn)行網(wǎng)絡(luò)配置;最后,我們再單獨開啟其中一塊板子的SIoT應(yīng)用使其作為服務(wù)器,而其他板子作為客戶端,通過編程將數(shù)據(jù)發(fā)送到SIoT物聯(lián)網(wǎng)平臺上。
2、配置網(wǎng)絡(luò)
STEP1:將電腦連入路由器的Wi-Fi中
Tips :沒有路由器也可通過手機(jī)開熱點的方式實現(xiàn)聯(lián)網(wǎng)。
STEP2:取一塊板子,將其通過USB連接線連接到電腦。
STEP3:打開瀏覽器,登錄“10.1.2.3”行空板網(wǎng)頁菜單,配置網(wǎng)絡(luò),使其連在同一Wi-Fi中。
STEP4:完成后,拔下USB線,用同樣的方式給其他兩塊行空板配置好網(wǎng)絡(luò)。
STEP5:給3塊板子上電,使其都保持開機(jī)狀態(tài)
這里,給板子上電有以下三種方式,任選其一即可:
1、使用USB或type-c口輸出的電源適配器
2、使用USB或type-c口輸出的充電寶
3、通過電腦的USB端口
STEP6:檢查各板子無線連接的IP地址。
依次查看并記錄下3塊板子的無線連接的IP地址,如下圖,前三組數(shù)字應(yīng)該是一樣的,這說明我們成功將3塊板子都連接在了同一局域網(wǎng)段內(nèi)。
Tips:這里,三塊板子的無線連接IP地址分別為“192.168.43.199”、“192.168.43.200”、“192.168.43.201”。
3、開啟SIoT
STEP1:單獨啟動一塊板子的SIoT服務(wù)
找到其中一塊板子,按下HOME鍵進(jìn)入菜單,單擊“應(yīng)用開關(guān)”,找到SIoT應(yīng)用后點擊啟用,如下圖所示。同時,確認(rèn)其余兩塊板子SIoT應(yīng)用關(guān)閉。
Tip1:這里,我們將SIoT服務(wù)啟動在了IP為“192.168.43.201”的板子上。
Tip2:關(guān)閉其他板子的SIoT是為了避免如果輸入地址錯誤導(dǎo)致數(shù)據(jù)不在同一個地方。
STEP2:檢查電腦聯(lián)網(wǎng)
打開瀏覽器,輸入IP地址“192.168.43.201”并回車,進(jìn)入行空板的網(wǎng)頁菜單,找到應(yīng)用開關(guān)下的SIoT服務(wù),點擊“打開頁面”。
Tips:這里,需將“192.168.43.201”改為個人實際的IP地址。
如出現(xiàn)下列界面(物聯(lián)網(wǎng)平臺網(wǎng)頁端),說明電腦和板子都連在了同一局域網(wǎng)內(nèi),且SIoT應(yīng)用啟動成功。
任務(wù)描述2:行空板1檢測土壤濕度
這里,為了便于區(qū)分,我們將開啟SIoT應(yīng)用的板子記作行空板3,其余兩塊板子分別為行空板1和行空板2 。
這個任務(wù)中,我們將在行空板1的屏幕上顯示一張智慧農(nóng)業(yè)的背景圖,使外接土壤濕度傳感器檢測到的濕度數(shù)據(jù)一邊顯示在屏幕上,一邊發(fā)送到板3的SIoT平臺上,這里的整體功能同上節(jié)課。
同時,對板1設(shè)定設(shè)置自動和手動兩個模式,在自動模式下,當(dāng)檢測到的土壤濕度值過低時,通過外接繼電器和水泵來自動澆水;在手動模式下,可通過點擊屏幕上按鈕的方式進(jìn)行澆水;模式之間也可通過按鈕手動切換。
此外,也可在SIoT物聯(lián)網(wǎng)平臺網(wǎng)頁端發(fā)送“relay on”、“relay off”、“auto”和“manual”四個指令,遠(yuǎn)程控制澆水、關(guān)水、切換自動模式以及切換手動模式。
1、硬件搭建
STEP1:將土壤濕度傳感器接入行空板1的P21引腳,繼電器接入P23引腳
STEP2:利用螺絲刀將水泵正負(fù)線與轉(zhuǎn)接頭連接起來
STEP3:利用繼電器將12V電源開關(guān)與水泵的轉(zhuǎn)接頭連接起來
STEP4:將繼電器開關(guān)撥至NC端
STEP5:將水管和土壤濕度傳感器插入花盆中,水泵固定在滿水的燒杯中
STEP6:將12V電源開關(guān)插上220V電源插座
Tips :上述具體步驟皆可參考第8課。
2、程序編寫
STEP1:創(chuàng)建與保存項目文件
啟動Mind+,另存項目并命名為“010、多節(jié)點智慧農(nóng)業(yè)系統(tǒng)01”。
STEP2:遠(yuǎn)程連接行空板
對不同的板子編程,我們可以同時打開多個Mind+,在各個Mind+中手動輸入不同的IP地址,這樣就可以連接訪問不同板子了。
(1)選擇“手動輸入”
(2)輸入行空板1 的無線連接IP地址“192.168.43.199”
Tips:需改為個人實際的行空板1的IP地址,同時這里的板子須為正常開機(jī)狀態(tài)且板子與電腦在同一個Wi-Fi下方可連接成功。
STEP3:在行空板1中創(chuàng)建一個新的文件夾,并命名為“多節(jié)點智慧農(nóng)業(yè)系統(tǒng)”
Tips:這里,我們將程序直接建在板子中,這樣可以便于直接通過行空板的Home菜單來運行程序而不需要啟動MInd+運行,操作方法后續(xù)會介紹。
STEP4:創(chuàng)建與保存Python文件
在“多節(jié)點智慧農(nóng)業(yè)系統(tǒng)”文件夾中創(chuàng)建一個Python程序文件“main1.py”,雙擊打開。
STEP5:導(dǎo)入素材文件夾
在“多節(jié)點智慧農(nóng)業(yè)系統(tǒng)”文件夾中導(dǎo)入素材文件夾。(下載鏈接見附錄1)
STEP6:程序編寫
(1) 導(dǎo)入所需功能庫
from unihiker import GUI # 導(dǎo)入unihiker庫GUI模塊from pinpong.board import Board, Pin # 導(dǎo)入pinpong庫下的Board, Pin模塊import time # 導(dǎo)入time庫import siot # 導(dǎo)入siot庫 |
(2) 初始化板子和引腳
'''初始化板子和硬件'''Board().begin() # 初始化行空板adc0 = Pin(Pin.P21, Pin.ANALOG) # 初始化21引腳為模擬輸入模式relay = Pin(Pin.P23, Pin.OUT) # 初始化23引腳為數(shù)字輸出模式 |
(3) 連接SIoT物聯(lián)網(wǎng)平臺并設(shè)置發(fā)送和訂閱消息數(shù)據(jù)功能
Tips:這里的IP地址寫入的是之前記錄的行空板3的無線熱點IP。
'''設(shè)置物聯(lián)網(wǎng)平臺連接參數(shù)'''SERVER = "192.168.43.201" # MQTT服務(wù)器IP,輸入個人實際IPCLIENT_ID = "" # 在SIoT上,CLIENT_ID可以留空IOT_UserName = 'siot' # 用戶名IOT_PassWord = 'dfrobot' # 密碼IOT_pubTopic1 = '多節(jié)點智慧農(nóng)業(yè)系統(tǒng)/Soil_moisture_value' # 濕度topic,“項目名稱/設(shè)備名稱” # 定義回調(diào)函數(shù)def sub_relay(client, userdata, msg): topic = msg.topic payload = msg.payload.decode() '''定義接收到指令時的操作''' print("\nTopic:" + topic + " Message:" + payload) # 打印接收到的信息 if payload == 'relay on': # 如果接收到“relay on” img.config(w=240, h=320, image='img/澆水1.png') relay.write_digital(1) # 繼電器輸出高電平 elif payload == 'relay off': # 如果接收到“relay off” img.config(w=240, h=320, image='img/關(guān)水1.png') relay.write_digital(0) # 繼電器輸出低電平 elif payload == 'auto': # 如果接收到“auto” print("auto") click_C() # 切換模式--自動模式 elif payload == 'manual': # 如果接收到“manual” print("manual") click_C() # 切換模式--手動模式 siot.init(CLIENT_ID, SERVER, user=IOT_UserName,password=IOT_PassWord) # 初始化,確定輸入的用戶名和密碼正確siot.connect() # 連接siot物聯(lián)網(wǎng)平臺siot.subscribe(IOT_pubTopic1, sub_relay) # 訂閱物聯(lián)網(wǎng)平臺消息siot.loop() # 循環(huán) |
(4) 顯示屏幕界面
'''顯示屏幕界面'''gui = GUI() # 實例化gui對象img = gui.draw_image(w=240, h=320, image='img/關(guān)水1.png') # 顯示背景圖片 # 定義三個按鈕的回調(diào)函數(shù)def click_A(): '''定義點擊按鈕A時的操作--切換顯示澆水圖片''' img.config(w=240, h=320, image='img/澆水1.png') relay.write_digital(1) # 繼電器輸出高電平 def click_B(): '''定義點擊按鈕B時的操作--切換顯示關(guān)水圖片''' img.config(w=240, h=320, image='img/關(guān)水1.png') relay.write_digital(0) # 繼電器輸出低電平 is_auto_mode = 1 # 定義一個標(biāo)志位--自動模式,1為開,0為關(guān) def click_C(): # 定義點擊按鈕C時的操作--切換模式 global is_auto_mode if is_auto_mode == 0: # 如果自動模式標(biāo)志位為0, 即原來為手動模式,那么點擊后 text_mode.config(text="自動") button_A.config(state='disabled') # 設(shè)置按鈕狀態(tài)為disabled,表示無法點擊 button_B.config(state='disabled') is_auto_mode = 1 # 設(shè)置自動模式標(biāo)志位為1 ,即打開自動模式 elif is_auto_mode == 1: # 如果自動模式標(biāo)準(zhǔn)位為1, 即原來為自動模式,那么點擊后 text_mode.config(text="手動") button_A.config(state='normal') # 設(shè)置按鈕狀態(tài)為normal,表示可以被點擊 button_B.config(state='normal') is_auto_mode = 0 # 設(shè)置自動模式標(biāo)志位為0 ,即打開手動模式 # 繪制填充矩形gui.fill_rect(x=65, y=35, w=70, h=30, color="white") # 繪制矩形以顯示“濕度值”gui.fill_rect(x=148, y=35, w=55, h=30, color="white") # 繪制矩形以顯示濕度值數(shù)據(jù)gui.fill_rect(x=100, y=160, w=70, h=30, color="white") # 繪制矩形以顯示“當(dāng)前模式”gui.fill_rect(x=180, y=160, w=50, h=30, color="white") # 繪制矩形以顯示模式類型 # 在矩形框內(nèi)顯示文字gui.draw_text(x=68, y=36, color="red", text='濕度值:') # 顯示文字“濕度值:”text_value = gui.draw_text(x=155, y=36, color="red", text="") # 顯示濕度值數(shù)據(jù)gui.draw_text(x=105, y=163, color="red", text='當(dāng)前模式:', font_size=11) # 顯示文字“當(dāng)前模式:”text_mode = gui.draw_text(x=190, y=163, color="red", text="自動", font_size=11) # 顯示模式類型 # 顯示按鈕'''顯示按鈕并設(shè)置按鈕點擊后觸發(fā)的功能,以及按鈕初始時處于不可被點擊狀態(tài)'''button_A = gui.add_button(x=50, y=260, w=70, h=40,text="澆水", onclick=click_A, state='disabled')button_B = gui.add_button(x=140, y=260, w=70, h=40,text="關(guān)水", onclick=click_B, state='disabled')button_C = gui.add_button(x=10, y=160, w=70, h=30,text="切換模式", onclick=click_C) |
(5) 定義自動補(bǔ)水功能
# 定義濕度值較低時自動補(bǔ)水3秒def auto_watering(): '''定義濕度值較低時自動補(bǔ)水3秒''' if Soil_moisture_value < 2120: click_A() time.sleep(3) click_B() else: click_B() |
(6) 循環(huán)檢測濕度值并發(fā)送數(shù)據(jù)至物聯(lián)網(wǎng)平臺
while True: # 循環(huán) Soil_moisture_value = adc0.read_analog() # 讀取模擬值 print(Soil_moisture_value) # 打印顯示濕度值 siot.publish(IOT_pubTopic1, Soil_moisture_value) # 發(fā)布信息至物聯(lián)網(wǎng)平臺 if is_auto_mode == 1: auto_watering() # 自動補(bǔ)水 text_value.config(text=Soil_moisture_value) # 更新濕度值 time.sleep(1) # delay1秒 |
Tips:完整示例程序如下:
# 板1檢測土壤濕度數(shù)據(jù),發(fā)送到板3開啟的SIoT物聯(lián)網(wǎng)平臺上。from unihiker import GUI # 導(dǎo)入unihiker庫GUI模塊from pinpong.board import Board, Pin # 導(dǎo)入pinpong庫下的Board, Pin模塊import time # 導(dǎo)入time庫import siot # 導(dǎo)入siot庫 '''初始化板子和硬件'''Board().begin() # 初始化行空板adc0 = Pin(Pin.P21, Pin.ANALOG) # 初始化21引腳為模擬輸入模式relay = Pin(Pin.P23, Pin.OUT) # 初始化23引腳為數(shù)字輸出模式 '''設(shè)置物聯(lián)網(wǎng)平臺連接參數(shù)'''SERVER = "192.168.43.201" # MQTT服務(wù)器IP,輸入個人實際IP CLIENT_ID = "" # 在SIoT上,CLIENT_ID可以留空IOT_UserName = 'siot' # 用戶名IOT_PassWord = 'dfrobot' # 密碼IOT_pubTopic1 = '多節(jié)點智慧農(nóng)業(yè)系統(tǒng)/Soil_moisture_value' # 濕度topic,“項目名稱/設(shè)備名稱” # 定義回調(diào)函數(shù)def sub_relay(client, userdata, msg): topic = msg.topic payload = msg.payload.decode() '''定義接收到指令時的操作''' print("\nTopic:" + topic + " Message:" + payload) # 打印接收到的信息 if payload == 'relay on': # 如果接收到“relay on” img.config(w=240, h=320, image='img/澆水1.png') relay.write_digital(1) # 繼電器輸出高電平 elif payload == 'relay off': # 如果接收到“relay off” img.config(w=240, h=320, image='img/關(guān)水1.png') relay.write_digital(0) # 繼電器輸出低電平 elif payload == 'auto': # 如果接收到“auto” print("auto") click_C() # 切換模式--自動模式 elif payload == 'manual': # 如果接收到“manual” print("manual") click_C() # 切換模式--手動模式 siot.init(CLIENT_ID, SERVER, user=IOT_UserName,password=IOT_PassWord) # 初始化,確定輸入的用戶名和密碼正確siot.connect() # 連接siot物聯(lián)網(wǎng)平臺siot.subscribe(IOT_pubTopic1, sub_relay) # 訂閱物聯(lián)網(wǎng)平臺消息siot.loop() # 循環(huán) '''顯示屏幕界面'''gui = GUI() # 實例化gui對象img = gui.draw_image(w=240, h=320, image='img/關(guān)水1.png') # 顯示背景圖片 # 定義三個按鈕的回調(diào)函數(shù)def click_A(): '''定義點擊按鈕A時的操作--切換顯示澆水圖片''' img.config(w=240, h=320, image='img/澆水1.png') relay.write_digital(1) # 繼電器輸出高電平 def click_B(): '''定義點擊按鈕B時的操作--切換顯示關(guān)水圖片''' img.config(w=240, h=320, image='img/關(guān)水1.png') relay.write_digital(0) # 繼電器輸出低電平 is_auto_mode = 1 # 定義一個標(biāo)志位--自動模式,1為開,0為關(guān) def click_C(): # 定義點擊按鈕C時的操作--切換模式 global is_auto_mode if is_auto_mode == 0: # 如果自動模式標(biāo)志位為0, 即原來為手動模式,那么點擊后 text_mode.config(text="自動") button_A.config(state='disabled') # 設(shè)置按鈕狀態(tài)為disabled,表示無法點擊 button_B.config(state='disabled') is_auto_mode = 1 # 設(shè)置自動模式標(biāo)志位為1 ,即打開自動模式 elif is_auto_mode == 1: # 如果自動模式標(biāo)準(zhǔn)位為1, 即原來為自動模式,那么點擊后 text_mode.config(text="手動") button_A.config(state='normal') # 設(shè)置按鈕狀態(tài)為normal,表示可以被點擊 button_B.config(state='normal') is_auto_mode = 0 # 設(shè)置自動模式標(biāo)志位為0 ,即打開手動模式 # 繪制填充矩形gui.fill_rect(x=65, y=35, w=70, h=30, color="white") # 繪制矩形以顯示“濕度值”gui.fill_rect(x=148, y=35, w=55, h=30, color="white") # 繪制矩形以顯示濕度值數(shù)據(jù)gui.fill_rect(x=100, y=160, w=70, h=30, color="white") # 繪制矩形以顯示“當(dāng)前模式”gui.fill_rect(x=180, y=160, w=50, h=30, color="white") # 繪制矩形以顯示模式類型 # 在矩形框內(nèi)顯示文字gui.draw_text(x=68, y=36, color="red", text='濕度值:') # 顯示文字“濕度值:”text_value = gui.draw_text(x=155, y=36, color="red", text="") # 顯示濕度值數(shù)據(jù)gui.draw_text(x=105, y=163, color="red", text='當(dāng)前模式:', font_size=11) # 顯示文字“當(dāng)前模式:”text_mode = gui.draw_text(x=190, y=163, color="red", text="自動", font_size=11) # 顯示模式類型 # 顯示按鈕'''顯示按鈕并設(shè)置按鈕點擊后觸發(fā)的功能,以及按鈕初始時處于不可被點擊狀態(tài)'''button_A = gui.add_button(x=50, y=260, w=70, h=40,text="澆水", onclick=click_A, state='disabled')button_B = gui.add_button(x=140, y=260, w=70, h=40,text="關(guān)水", onclick=click_B, state='disabled')button_C = gui.add_button(x=10, y=160, w=70, h=30,text="切換模式", onclick=click_C) # 定義濕度值較低時自動補(bǔ)水3秒def auto_watering(): '''定義濕度值較低時自動補(bǔ)水3秒''' if Soil_moisture_value < 2120: click_A() time.sleep(3) click_B() else: click_B() while True: # 循環(huán) Soil_moisture_value = adc0.read_analog() # 讀取模擬值 print(Soil_moisture_value) # 打印顯示濕度值 siot.publish(IOT_pubTopic1, Soil_moisture_value) # 發(fā)布信息至物聯(lián)網(wǎng)平臺 if is_auto_mode == 1: auto_watering() # 自動補(bǔ)水 text_value.config(text=Soil_moisture_value) # 更新濕度值 time.sleep(1) # delay1秒 |
3、程序運行
STEP1:運行觀察效果
點擊Mind+軟件上的運行按鈕,觀察行空板1,可以看到板1界面如下,能在手動和自動兩個模式下控制澆水,也可以在瀏覽器中輸入SIoT服務(wù)器所在的行空板的IP地址(此處為“192.168.43.201”),打開網(wǎng)頁菜單應(yīng)用開關(guān)中的SIoT,從物聯(lián)網(wǎng)平臺遠(yuǎn)程查看濕度數(shù)據(jù)并且進(jìn)行澆水等操作。
Tips:只有手動模式下發(fā)送消息才有效
任務(wù)描述3:行空板2檢測光線
這里,我們將在行空板2的屏幕上顯示一張小燈背景圖,將板載光線傳感器檢測到的光線值數(shù)據(jù)一邊顯示在屏幕上,一邊發(fā)送到板3的SIoT平臺上。
同時,對板2設(shè)定設(shè)置自動和手動兩個模式,在自動模式下,當(dāng)檢測到的光線值過低時,通過外接LED來自動補(bǔ)光;在手動模式下,可通過點擊屏幕上按鈕的方式來補(bǔ)光;模式之間也可通過按鈕手動切換。
此外,也可在SIoT物聯(lián)網(wǎng)平臺網(wǎng)頁端發(fā)送“l(fā)ed on”、“l(fā)ed off”、“auto”和“manual”四個指令,遠(yuǎn)程控制LED燈打開、LED燈熄滅、切換自動模式以及切換手動模式。
1、硬件搭建
將LED接入行空板2的P23引腳
2、程序編寫
STEP1:創(chuàng)建與保存項目文件
再啟動一個Mind+軟件,另存項目并命名為“010、多節(jié)點智慧農(nóng)業(yè)系統(tǒng)02”。
STEP2:遠(yuǎn)程連接行空板
(1)選擇“手動輸入”
(2)輸入行空板2 的無線連接IP地址“192.168.43.200”
STEP3:和板1一樣,直接在行空板2中創(chuàng)建一個新的文件夾,并命名為“多節(jié)點智慧農(nóng)業(yè)系統(tǒng)”
STEP4:創(chuàng)建與保存Python文件
在“多節(jié)點智慧農(nóng)業(yè)系統(tǒng)”文件夾中創(chuàng)建一個Python程序文件“main2.py”,雙擊打開。
STEP5:在“多節(jié)點智慧農(nóng)業(yè)系統(tǒng)”文件夾中導(dǎo)入素材文件夾。
STEP6:程序編寫
整體上,這里的編程邏輯同上述板1,因此我們不做過多贅述,注意寫入實際行空板3的IP地址。
Tips:完整示例程序如下:
# 板2檢測光線數(shù)據(jù),發(fā)送到板3開啟的SIoT物聯(lián)網(wǎng)平臺上。from unihiker import GUI # 導(dǎo)入unihiker庫from pinpong.board import Board, Pin # 導(dǎo)入pinpong庫下的Board, Pin模塊from pinpong.extension.unihiker import * # 導(dǎo)入pinpong.extension.unihiker包中所有模塊import time # 導(dǎo)入time庫import siot # 導(dǎo)入siot庫 '''初始化板子和硬件'''Board().begin() # 初始化行空板led = Pin(Pin.P23, Pin.OUT) # 初始化23引腳為數(shù)字輸出模式 '''設(shè)置物聯(lián)網(wǎng)平臺連接參數(shù)'''SERVER = "192.168.43.201" # MQTT服務(wù)器IP,輸入個人實際IP CLIENT_ID = "" # 在SIoT上,CLIENT_ID可以留空IOT_UserName = 'siot' # 用戶名IOT_PassWord = 'dfrobot' # 密碼IOT_pubTopic2 = '多節(jié)點智慧農(nóng)業(yè)系統(tǒng)/light' # 濕度topic,“項目名稱/設(shè)備名稱” # 定義回調(diào)函數(shù)def sub_led(client, userdata, msg): topic = msg.topic payload = msg.payload.decode() '''定義接收到指令時的操作''' print("\nTopic:" + topic + " Message:" + payload) # 打印接收到的信息 if payload == 'led on': # 如果接收到“l(fā)ed on” img.config(w=240, h=320, image='img/開燈1.png') led.write_digital(1) # led輸出高電平 elif payload == 'led off': # 如果接收到“l(fā)ed off” img.config(w=240, h=320, image='img/關(guān)燈1.png') led.write_digital(0) # led輸出低電平 elif payload == 'auto': # 如果接收到“auto” print("auto") click_C() # 切換模式--自動模式 elif payload == 'manual': # 如果接收到“manual” print("manual") click_C() # 切換模式--手動模式 siot.init(CLIENT_ID, SERVER, user=IOT_UserName,password=IOT_PassWord) # 初始化,確定輸入的用戶名和密碼正確siot.connect() # 連接siot物聯(lián)網(wǎng)平臺siot.subscribe(IOT_pubTopic2, sub_led) # 訂閱物聯(lián)網(wǎng)平臺消息siot.loop() # 循環(huán) '''顯示屏幕界面'''gui = GUI() # 實例化gui對象img = gui.draw_image(w=240, h=320, image='img/關(guān)燈1.png') # 顯示背景圖片 # 定義回調(diào)函數(shù)def click_A(): '''定義點擊按鈕A時的操作--切換顯示開燈圖片''' img.config(w=240, h=320, image='img/開燈1.png') led.write_digital(1) # 繼電器輸出高電平 def click_B(): '''定義點擊按鈕B時的操作--切換顯示關(guān)燈圖片''' img.config(w=240, h=320, image='img/關(guān)燈1.png') led.write_digital(0) # 繼電器輸出低電平 is_auto_mode = 1 # 定義一個標(biāo)志位--自動模式,1為開,0為關(guān) def click_C(): # 定義點擊按鈕C時的操作--切換模式 global is_auto_mode if is_auto_mode == 0: # 如果自動模式標(biāo)志位為0, 即原來為手動模式,那么點擊后 text_mode.config(text="自動") button_A.config(state='disabled') # 設(shè)置按鈕狀態(tài)為disabled,表示無法點擊 button_B.config(state='disabled') is_auto_mode = 1 # 設(shè)置自動模式標(biāo)志位為1 ,即打開自動模式 elif is_auto_mode == 1: # 如果自動模式標(biāo)準(zhǔn)位為1, 即原來為自動模式,那么點擊后 text_mode.config(text="手動") button_A.config(state='normal') # 設(shè)置按鈕狀態(tài)為normal,表示可以被點擊 button_B.config(state='normal') is_auto_mode = 0 # 設(shè)置自動模式標(biāo)志位為0 ,即打開手動模式 # 繪制填充矩形gui.fill_rect(x=65, y=35, w=70, h=30, color="white") # 繪制矩形以顯示“光線值”gui.fill_rect(x=148, y=35, w=55, h=30, color="white") # 繪制矩形以顯示光線值數(shù)據(jù)gui.fill_rect(x=100, y=160, w=70, h=30, color="white") # 繪制矩形顯示“當(dāng)前模式”gui.fill_rect(x=180, y=160, w=50, h=30, color="white") # 繪制矩形顯示模式類型 # 在矩形框內(nèi)顯示文字gui.draw_text(x=68, y=36, color="red", text='光線值') # 顯示文字“光線值”text_value = gui.draw_text(x=155, y=36, color="red", text="") # 顯示光線值數(shù)據(jù)gui.draw_text(x=105, y=163, color="red", text='當(dāng)前模式:', font_size=11) # 顯示文字“當(dāng)前模式:”text_mode = gui.draw_text(x=190, y=163, color="red", text="自動", font_size=11) # 顯示模式類型 # 顯示按鈕'''顯示按鈕并設(shè)置按鈕點擊后觸發(fā)的功能,以及按鈕初始時處于不可被點擊狀態(tài)'''button_A = gui.add_button(x=50, y=260, w=70, h=40,text="開燈", onclick=click_A, state='disabled')button_B = gui.add_button(x=140, y=260, w=70, h=40,text="關(guān)燈", onclick=click_B, state='disabled')button_C = gui.add_button(x=10, y=160, w=70, h=30,text="切換模式", onclick=click_C) # 定義光線值較低時自動開燈def auto_light(): '''定義光線值較低時自動開燈''' if Light< 666: click_A() else: click_B() while True: # 循環(huán) Light = light.read() # 讀取光線值 print(Light) # 打印顯示光線值 siot.publish(IOT_pubTopic2, Light) # 發(fā)布信息至物聯(lián)網(wǎng)平臺 if is_auto_mode == 1: auto_light() # 自動補(bǔ)水 text_value.config(text=Light) # 更新光線值 time.sleep(1) # delay1秒 |
3、程序運行
STEP1:運行觀察效果
點擊Mind+軟件上的運行按鈕,觀察行空板2,可以看到板2界面如下,能在手動和自動兩個模式下控制補(bǔ)光(打開LED),也可以在瀏覽器中輸入SIoT服務(wù)器所在的行空板的IP地址(此處為“192.168.43.201”),打開網(wǎng)頁應(yīng)用中的SIoT,從物聯(lián)網(wǎng)平臺遠(yuǎn)程查看光線值數(shù)據(jù)并進(jìn)行補(bǔ)光等操作。
注意板2的Topic與板1的Topic不同,需要返回設(shè)備列表操作名稱為“l(fā)ight”的項目。
Tips:只有手動模式下發(fā)送消息才有效
任務(wù)描述4:行空板3遠(yuǎn)程監(jiān)控
這里,我們將行空板3作為總站,接收行空板1和行空板2發(fā)送到SIoT物聯(lián)網(wǎng)平臺上的土壤濕度和光線值數(shù)據(jù),然后匯總顯示在板3的屏幕上,并通過板3上的按鈕來對板1 和板2進(jìn)行控制,實現(xiàn)各自的澆水、關(guān)水、開燈、關(guān)燈功能。
同時,在板3上設(shè)定自動和手動兩個模式開關(guān)按鈕,分別用來控制板1和板2兩個模式的切換,初始時默認(rèn)為自動模式。
此外,最重要的,板3的總站也可以對接收到的數(shù)據(jù)進(jìn)行實時判別,當(dāng)某一環(huán)境數(shù)據(jù)不佳時,遠(yuǎn)程發(fā)送一封郵件至郵箱作為警報提示,以便我們能及時獲取信息并進(jìn)行澆水補(bǔ)光。
1、郵箱設(shè)置
STEP1:記錄qq郵箱授權(quán)碼
(1)打開qq郵箱,點擊頁面上方的“設(shè)置”
(2)開啟SMTP服務(wù),生成授權(quán)碼并記錄
Tips:需要通過手機(jī)發(fā)送驗證短信,生成授權(quán)碼
(3)微信開啟QQ郵箱提醒
這里,為了方便查看郵件,我們也可在微信上開啟QQ郵箱提醒功能。
Tips:具體操作方法可參考一下鏈接:https://kf.qq.com/touch/faq/1506 ... Uz.html?platform=14
2、程序編寫
STEP1:創(chuàng)建與保存項目文件
再啟動一個Mind+軟件,另存項目并命名為“010、多節(jié)點智慧農(nóng)業(yè)系統(tǒng)03”。
STEP2:遠(yuǎn)程連接行空板
(1)選擇“手動輸入”
(2)輸入行空板3 的無線連接IP地址“192.168.43.201”
STEP3:直接在行空板3中創(chuàng)建一個新的文件夾,并命名為“多節(jié)點智慧農(nóng)業(yè)系統(tǒng)”
STEP4:創(chuàng)建與保存Python文件
在“多節(jié)點智慧農(nóng)業(yè)系統(tǒng)”文件夾中創(chuàng)建一個Python程序文件“main3.py”,雙擊打開。
Step5:程序編寫
(1)導(dǎo)入功能庫
這里,為了能成功發(fā)送郵件,我們首先需要導(dǎo)入smtplib庫,同時,在構(gòu)造郵件時,我們需要處理文本,以及將輸入的內(nèi)容格式化,那么我們還需導(dǎo)入MIMEText模塊和formataddr模塊。
from unihiker import GUI # 導(dǎo)入unihiker庫GUI模塊import time # 導(dǎo)入time庫import siot # 導(dǎo)入siot庫import smtplib # 導(dǎo)入smtplib庫from email.mime.text import MIMEText # 導(dǎo)入email.mime.text庫MIMEText模塊,負(fù)責(zé)處理文本from email.utils import formataddr # 導(dǎo)入email.utils庫formataddr模塊,負(fù)責(zé)將輸入的內(nèi)容格式化 |
(2)設(shè)置物聯(lián)網(wǎng)平臺連接參數(shù)
接著,由于我們將接收板1和板2發(fā)送到物聯(lián)網(wǎng)平臺的數(shù)據(jù),因此我們需要先設(shè)置好物聯(lián)網(wǎng)平臺的連接參數(shù),以便后續(xù)訂閱。
'''設(shè)置物聯(lián)網(wǎng)平臺連接參數(shù)'''SERVER = "192.168.43.201" # MQTT服務(wù)器IP地址,輸入個人實際IPCLIENT_ID = "" # 在SIoT上,CLIENT_ID可以留空IOT_UserName = 'siot' # 用戶名IOT_PassWord = 'dfrobot' # 密碼IOT_pubTopic1 = '多節(jié)點智慧農(nóng)業(yè)系統(tǒng)/Soil_moisture_value' # 濕度topic,“項目名稱/設(shè)備名稱”IOT_pubTopic2 = '多節(jié)點智慧農(nóng)業(yè)系統(tǒng)/light' # 光強(qiáng)topic,“項目名稱/設(shè)備名稱” |
(3)顯示屏幕頁面
之后,我們在行空板3的屏幕上顯示四部分內(nèi)容,首先是板1和板2 的濕度值和光線值數(shù)據(jù);其次是對數(shù)據(jù)進(jìn)行判別后的情況分析,比如在屏幕上顯示“當(dāng)前土壤濕度適當(dāng)”、“當(dāng)前光線強(qiáng)度適當(dāng)”;接著是模式切換按鈕和當(dāng)前模式的顯示;最后是手動模式下的四個按鈕控件。而在按鈕中,我們通過定義回調(diào)函數(shù)的方式,來設(shè)定其功能。
'''顯示屏幕頁面''' # 定義五個按鈕的回調(diào)函數(shù)def click_A(): # 定義點擊按鈕A時的操作--切換圖片 siot.publish(IOT_pubTopic1, 'relay on') # 發(fā)布信息'relay on'至物聯(lián)網(wǎng)平臺 def click_B(): # 定義點擊按鈕B時的操作--切換圖片 siot.publish(IOT_pubTopic1, 'relay off') # 發(fā)布信息'relay off'至物聯(lián)網(wǎng)平臺 def click_C(): # 定義點擊按鈕C時的操作--切換圖片 siot.publish(IOT_pubTopic2, 'led on') # 發(fā)布信息'led on'至物聯(lián)網(wǎng)平臺 def click_D(): # 定義點擊按鈕D時的操作--切換圖片 siot.publish(IOT_pubTopic2, 'led off') # 發(fā)布信息'led off'至物聯(lián)網(wǎng)平臺 is_auto_mode = 1 # 定義一個標(biāo)志位--自動模式,1為開,0為關(guān) def click_E(): # 定義點擊按鈕E時的操作--切換模式 global is_auto_mode if is_auto_mode == 0: text_mode.config(text="自動") button_A.config(state='disabled') button_B.config(state='disabled') button_C.config(state='disabled') button_D.config(state='disabled') siot.publish(IOT_pubTopic1, 'auto') # 發(fā)布信息'auto'至物聯(lián)網(wǎng)平臺 siot.publish(IOT_pubTopic2, 'auto') # 發(fā)布信息'auto'至物聯(lián)網(wǎng)平臺 is_auto_mode = 1 elif is_auto_mode == 1: text_mode.config(text="手動") button_A.config(state='normal') button_B.config(state='normal') button_C.config(state='normal') button_D.config(state='normal') siot.publish(IOT_pubTopic1, 'manual') # 發(fā)布信息'manual'至物聯(lián)網(wǎng)平臺 siot.publish(IOT_pubTopic2, 'manual') # 發(fā)布信息'manual'至物聯(lián)網(wǎng)平臺 is_auto_mode = 0 gui=GUI() # 實例化gui對象 # 顯示填充矩形gui.fill_rect(x=0, y=0, w=240, h=320, color="#99CCFF") # 繪制填充矩形顯示為背景# 顯示標(biāo)題title = gui.draw_text(x=30, y=5, text='多節(jié)點智慧農(nóng)業(yè)系統(tǒng)', font_size=14, color='blue') # 顯示part1數(shù)據(jù)(圓角矩形、文字)gui.draw_round_rect(x=20, y=33, w=200, h=42, r=8, width=1) # 顯示圓角矩形,(起點坐標(biāo)(20,33),寬200,高42,圓角半徑8,線寬1)gui.draw_text(x=35, y=32, text='濕度值:', font_size=11) # 顯示文字“濕度值:”text_value = gui.draw_text(x=110, y=32, color="red", text="", font_size=12) # 顯示光線值數(shù)據(jù)gui.draw_text(x=35, y=52, text='光線值:', font_size=11) # 顯示文字“光線值”text_value_2 = gui.draw_text(x=110, y=52, color="red", text="", font_size=12) # 顯示光線值數(shù)據(jù) # 顯示part2數(shù)據(jù)情況(圓角矩形、文字)gui.draw_round_rect(x=20, y=80, w=200, h=80, r=8,width=1)text1 = gui.draw_text(x=30, y=80, text='土壤濕度情況:', font_size=11, color='black') # 在(30,60)坐標(biāo)位顯示“土壤濕度情況:”,字體大小為11,顏色為黑text2 = gui.draw_text(x=30, y=98, text='當(dāng)前土壤濕度適當(dāng)', font_size=12, color='red')text3 = gui.draw_text(x=30, y=120, text='光線強(qiáng)度情況:', font_size=11, color='black')text4 = gui.draw_text(x=30, y=138, text='當(dāng)前光線強(qiáng)度適當(dāng)', font_size=12, color='red') # 顯示part3切換模式功能(圓角矩形、按鈕、文字)gui.draw_round_rect(x=20, y=165, w=200, h=40, r=8,width=1)button_E = gui.add_button(x=30, y=170, w=70, h=30, text="切換模式", onclick=click_E)gui.draw_text(x=110, y=173, text='當(dāng)前模式:', font_size=11) # 顯示文字“當(dāng)前模式:”text_mode = gui.draw_text(x=178, y=173, color="red", text="自動", font_size=12) # 顯示模式類型 # 顯示part4手動控制模式(圓角矩形、按鈕、文字)gui.draw_round_rect(x=20, y=210, w=200, h=105, r=8,width=1)gui.draw_text(x=33, y=213, text='手動控制:',font_size=11) # 顯示文字“手動控制:”button_A = gui.add_button(x=30, y=240, w=70, h=30, text="澆水", onclick=click_A,state='disabled')button_B = gui.add_button(x=140, y=240, w=70, h=30, text="關(guān)水", onclick=click_B,state='disabled')button_C = gui.add_button(x=30, y=280, w=70, h=30, text="開燈", onclick=click_C,state='disabled')button_D = gui.add_button(x=140, y=280, w=70, h=30, text="關(guān)燈", onclick=click_D,state='disabled') |
(4)郵件設(shè)置
接下來,我們將定義郵件發(fā)送功能,首先設(shè)置好發(fā)送郵件所需的參數(shù),包括發(fā)件人郵箱賬號,發(fā)件人郵箱授權(quán)碼,發(fā)件人郵箱昵稱,收件人郵箱賬號,收件人郵箱昵稱總計五個參數(shù)。之后,定義一個發(fā)送郵件的函數(shù),在其中將郵件內(nèi)容和標(biāo)題作為參數(shù)傳入,以便能多次調(diào)用以發(fā)送郵件。
Tips:須填入實際的發(fā)件人賬號、授權(quán)碼信息,昵稱可隨意編寫,但不可為空。
'''郵件設(shè)置'''my_sender='10******69@qq.com' # 設(shè)置發(fā)件人郵箱賬號,輸入自己的郵箱my_pass = '******' # 設(shè)置發(fā)件人郵箱授權(quán)碼,不可為空my_name = 'IvanD.Mido' # 設(shè)置發(fā)件人郵箱昵稱my_user='10******69@qq.com' # 設(shè)置收件人郵箱賬號,我這邊發(fā)送給自己my_user_name = 'IvanD.Mido' # 設(shè)置收件人郵箱昵稱 # 定義警報發(fā)郵件函數(shù)def mail(content,title): ret=True # 定義一個標(biāo)記ret,記錄發(fā)送郵件事件,初始值為True try: msg=MIMEText(content,'plain','utf-8') # 創(chuàng)建郵件文本對象,Title對應(yīng)文本內(nèi)容,'plain'指設(shè)置的文本格式,'utf-8'指設(shè)置的編碼 '''三個頭部信息:發(fā)件人,收件人,主題''' msg['From']=formataddr([my_name,my_sender]) # 定義發(fā)件人信息:括號里的對應(yīng)發(fā)件人郵箱昵稱、發(fā)件人郵箱賬號 msg['To']=formataddr([my_user_name,my_user]) # 定義收件人信息:括號里的對應(yīng)收件人郵箱昵稱、收件人郵箱賬號 msg['Subject']=title # 定義郵件的主題,也可以說是標(biāo)題 server=smtplib.SMTP_SSL("http://smtp.qq.com", 465) # 創(chuàng)建SMTP服務(wù),連接qq郵箱服務(wù)器,發(fā)件人郵箱中的SMTP服務(wù)器,SMTP協(xié)議加密端口是465 server.login(my_sender, my_pass) # 登錄郵箱,括號中對應(yīng)的是發(fā)件人郵箱賬號、郵箱密碼 server.sendmail(my_sender,my_user,msg.as_string()) # 發(fā)送郵件,括號中對應(yīng)的是發(fā)件人郵箱賬號、收件人郵箱賬號、郵件信息的字符串格式 server.quit() # 關(guān)閉連接 except Exception: # 如果 try 中的語句沒有執(zhí)行,則會執(zhí)行下面的 ret=False ret=False # 將ret標(biāo)記記為False return ret # 返回ret標(biāo)記 |
(6)定義回調(diào)函數(shù)
由于發(fā)送郵件是在檢測到板1和板2 的數(shù)據(jù)不佳的情況下進(jìn)行,因此,我們需要先設(shè)定接收到SIoT物聯(lián)網(wǎng)平臺的數(shù)據(jù)時的功能操作。
當(dāng)接收到Topic1下的土壤濕度的數(shù)據(jù)后,我們做三步處理,首先是將數(shù)據(jù)呈現(xiàn)在屏幕上,其次是對數(shù)據(jù)進(jìn)行判別,于屏幕更新顯示判別分析后的情況,最后是依據(jù)判別結(jié)果,當(dāng)結(jié)果數(shù)據(jù)不佳時設(shè)定發(fā)送一封警報郵件,并且每封主題郵件之間的間隔不少于10s。
而當(dāng)接收到Topic2下的光線值數(shù)據(jù)后,我們做同樣的處理。
Tips:這里的郵件發(fā)送頻率可自行修改。
soil_mail_enable = True # 定義發(fā)送土壤警報郵件使能的標(biāo)志light_mail_enable = True # 定義發(fā)送光線警報郵件使能的標(biāo)志 soil_mail_time = time.time() # 定時郵件發(fā)送時間記錄標(biāo)志light_mail_time = time.time() # 定時郵件發(fā)送時間記錄標(biāo)志 # 定義回調(diào)函數(shù)def sub_cb(client, userdata, msg): global soil_mail_enable,light_mail_enable,soil_mail_time,light_mail_time topic = msg.topic # topic數(shù)據(jù)存入變量 payload = msg.payload.decode() # payload消息數(shù)據(jù)轉(zhuǎn)換為字符串后存入變量 print("\nTopic:" + topic + " Message:" + payload) # 終端打印數(shù)據(jù) if topic == IOT_pubTopic1: # 定義接收到Soil_moisture_value時的操作 if payload.isdigit(): # 接收的消息是數(shù)字類型的則說明是濕度值而不是控制命令 Soil_moisture_value = int(payload) # 轉(zhuǎn)換為數(shù)字類型方便后面做判斷 text_value.config(text = str(Soil_moisture_value)) # 更新屏幕顯示數(shù)據(jù) if Soil_moisture_value < 2120: # 閾值設(shè)置為2120,可根據(jù)實際情況調(diào)整 text2.config(text="當(dāng)前土壤濕度過低") # 更新文字警告 if soil_mail_enable == True and (time.time()-soil_mail_time) > 10: # 如果允許且距離上次發(fā)送郵件時間間隔10秒以上才進(jìn)行發(fā)送的操作 ret1=mail("土壤濕度報警","當(dāng)前土壤濕度值為 " + str(Soil_moisture_value) + ",請及時補(bǔ)水!") if ret1: # 如果有發(fā)送郵件事件 print("郵件發(fā)送成功1") soil_mail_enable = False # 已經(jīng)發(fā)送過郵件,改變標(biāo)記避免重復(fù)發(fā)送 soil_mail_time = time.time() # 記錄本次郵件發(fā)送的時間 else: # 否則 print("郵件發(fā)送失敗1") else: text2.config(text="當(dāng)前土壤濕度適當(dāng)") # 更新文字 soil_mail_enable = True # 如果警報解除了就允許后續(xù)發(fā)郵件 elif topic == IOT_pubTopic2: # 定義接收到Light時的操作 if payload.isdigit(): # 接收的消息是數(shù)字類型的則說明是光線值而不是控制命令 light_value = int(payload) # 轉(zhuǎn)換為數(shù)字類型方便后面做判斷 text_value_2.config(text = str(light_value)) # 屏幕更新光線值 if light_value < 666: # 閾值設(shè)置為666,可根據(jù)實際情況調(diào)整 text4.config(text="當(dāng)前光線強(qiáng)度過低") # 更新文字警告 if light_mail_enable == True and (time.time()-light_mail_time) > 10: # 如果允許發(fā)送郵件才進(jìn)行發(fā)送的操作 ret2=mail("光線強(qiáng)度報警","當(dāng)前光線值為 " + str(light_value) + ",請及時補(bǔ)光!") if ret2: # 如果有發(fā)送郵件事件 print("郵件發(fā)送成功2") light_mail_enable = False # 已經(jīng)發(fā)送過郵件,改變標(biāo)記避免重復(fù)發(fā)送 light_mail_time = time.time() # 記錄本次郵件發(fā)送的時間 else: # 否則 print("郵件發(fā)送失敗2") else: text4.config(text="當(dāng)前光線強(qiáng)度適當(dāng)") # 更新文字警告 light_mail_enable = True # 如果警報解除了就允許后續(xù)發(fā)郵件 |
(7)訂閱物聯(lián)網(wǎng)平臺消息
在定義好回調(diào)函數(shù)后,我們設(shè)定板3訂閱物聯(lián)網(wǎng)平臺的消息,并在開始時發(fā)送“auto”至兩個Topic下,以便設(shè)定起始默認(rèn)模式為自動。
siot.init(CLIENT_ID, SERVER, user=IOT_UserName,password=IOT_PassWord) # 初始化,確定輸入的用戶名和密碼正確siot.connect() # 連接siot物聯(lián)網(wǎng)平臺siot.subscribe(IOT_pubTopic1, sub_cb) # 訂閱Topic1 土壤濕度數(shù)據(jù)siot.subscribe(IOT_pubTopic2, sub_cb) # 訂閱Topic2 光線強(qiáng)度數(shù)據(jù)siot.publish(IOT_pubTopic1,'auto') #發(fā)布信息'auto'至物聯(lián)網(wǎng)平臺 siot.publish(IOT_pubTopic2,'auto') #發(fā)布信息'auto'至物聯(lián)網(wǎng)平臺siot.loop() # 循環(huán) |
(8)循環(huán)保持程序運行
while True: # 循環(huán) time.sleep(0.5) # delay0.5秒 |
Tips:完整示例程序如下:
'''板3訂閱板1&板2發(fā)送到物聯(lián)網(wǎng)平臺的消息,顯示在板3上,數(shù)值不佳時警報發(fā)郵件'''from unihiker import GUI # 導(dǎo)入unihiker庫GUI模塊import time # 導(dǎo)入time庫import siot # 導(dǎo)入siot庫import smtplib # 導(dǎo)入smtplib庫from email.mime.text import MIMEText # 導(dǎo)入email.mime.text包中的MIMEText模塊,負(fù)責(zé)處理文本from email.utils import formataddr # 導(dǎo)入email.utils包中的formataddr模塊,負(fù)責(zé)將輸入的內(nèi)容格式化 '''設(shè)置物聯(lián)網(wǎng)平臺連接參數(shù)'''SERVER = "192.168.43.201" # MQTT服務(wù)器IP地址,輸入個人實際IPCLIENT_ID = "" # 在SIoT上,CLIENT_ID可以留空IOT_UserName = 'siot' # 用戶名IOT_PassWord = 'dfrobot' # 密碼IOT_pubTopic1 = '多節(jié)點智慧農(nóng)業(yè)系統(tǒng)/Soil_moisture_value' # 濕度topic,“項目名稱/設(shè)備名稱”IOT_pubTopic2 = '多節(jié)點智慧農(nóng)業(yè)系統(tǒng)/light' # 光強(qiáng)topic,“項目名稱/設(shè)備名稱” # 定義五個按鈕的回調(diào)函數(shù)def click_A(): # 定義點擊按鈕A時的操作--切換圖片 siot.publish(IOT_pubTopic1, 'relay on') # 發(fā)布信息'relay on'至物聯(lián)網(wǎng)平臺 def click_B(): # 定義點擊按鈕B時的操作--切換圖片 siot.publish(IOT_pubTopic1, 'relay off') # 發(fā)布信息'relay off'至物聯(lián)網(wǎng)平臺 def click_C(): # 定義點擊按鈕C時的操作--切換圖片 siot.publish(IOT_pubTopic2, 'led on') # 發(fā)布信息'led on'至物聯(lián)網(wǎng)平臺 def click_D(): # 定義點擊按鈕D時的操作--切換圖片 siot.publish(IOT_pubTopic2, 'led off') # 發(fā)布信息'led off'至物聯(lián)網(wǎng)平臺 is_auto_mode = 1 # 定義一個標(biāo)志位--自動模式,1為開,0為關(guān) def click_E(): # 定義點擊按鈕E時的操作--切換模式 global is_auto_mode if is_auto_mode == 0: text_mode.config(text="自動") button_A.config(state='disabled') button_B.config(state='disabled') button_C.config(state='disabled') button_D.config(state='disabled') siot.publish(IOT_pubTopic1, 'auto') # 發(fā)布信息'auto'至物聯(lián)網(wǎng)平臺 siot.publish(IOT_pubTopic2, 'auto') # 發(fā)布信息'auto'至物聯(lián)網(wǎng)平臺 is_auto_mode = 1 elif is_auto_mode == 1: text_mode.config(text="手動") button_A.config(state='normal') button_B.config(state='normal') button_C.config(state='normal') button_D.config(state='normal') siot.publish(IOT_pubTopic1, 'manual') # 發(fā)布信息'manual'至物聯(lián)網(wǎng)平臺 siot.publish(IOT_pubTopic2, 'manual') # 發(fā)布信息'manual'至物聯(lián)網(wǎng)平臺 is_auto_mode = 0 '''顯示屏幕頁面'''gui=GUI() # 實例化gui對象 # 顯示填充矩形gui.fill_rect(x=0, y=0, w=240, h=320, color="#99CCFF") # 繪制填充矩形顯示為背景# 顯示標(biāo)題title = gui.draw_text(x=30, y=5, text='多節(jié)點智慧農(nóng)業(yè)系統(tǒng)', font_size=14, color='blue') # 顯示part1數(shù)據(jù)(圓角矩形、文字)gui.draw_round_rect(x=20, y=33, w=200, h=42, r=8, width=1) # 顯示圓角矩形,(起點坐標(biāo)(20,33),寬200,高42,圓角半徑8,線寬1)gui.draw_text(x=35, y=32, text='濕度值:', font_size=11) # 顯示文字“濕度值:”text_value = gui.draw_text(x=110, y=32, color="red", text="", font_size=12) # 顯示光線值數(shù)據(jù)gui.draw_text(x=35, y=52, text='光線值:', font_size=11) # 顯示文字“光線值”text_value_2 = gui.draw_text(x=110, y=52, color="red", text="", font_size=12) # 顯示光線值數(shù)據(jù) # 顯示part2數(shù)據(jù)情況(圓角矩形、文字)gui.draw_round_rect(x=20, y=80, w=200, h=80, r=8,width=1)text1 = gui.draw_text(x=30, y=80, text='土壤濕度情況:', font_size=11, color='black') # 在(30,60)坐標(biāo)位顯示“土壤濕度情況:”,字體大小為11,顏色為黑text2 = gui.draw_text(x=30, y=98, text='當(dāng)前土壤濕度適當(dāng)', font_size=12, color='red')text3 = gui.draw_text(x=30, y=120, text='光線強(qiáng)度情況:', font_size=11, color='black')text4 = gui.draw_text(x=30, y=138, text='當(dāng)前光線強(qiáng)度適當(dāng)', font_size=12, color='red') # 顯示part3切換模式功能(圓角矩形、按鈕、文字)gui.draw_round_rect(x=20, y=165, w=200, h=40, r=8,width=1)button_E = gui.add_button(x=30, y=170, w=70, h=30, text="切換模式", onclick=click_E)gui.draw_text(x=110, y=173, text='當(dāng)前模式:', font_size=11) # 顯示文字“當(dāng)前模式:”text_mode = gui.draw_text(x=178, y=173, color="red", text="自動", font_size=12) # 顯示模式類型 # 顯示part4手動控制模式(圓角矩形、按鈕、文字)gui.draw_round_rect(x=20, y=210, w=200, h=105, r=8,width=1)gui.draw_text(x=33, y=213, text='手動控制:',font_size=11) # 顯示文字“手動控制:”button_A = gui.add_button(x=30, y=240, w=70, h=30, text="澆水", onclick=click_A,state='disabled')button_B = gui.add_button(x=140, y=240, w=70, h=30, text="關(guān)水", onclick=click_B,state='disabled')button_C = gui.add_button(x=30, y=280, w=70, h=30, text="開燈", onclick=click_C,state='disabled')button_D = gui.add_button(x=140, y=280, w=70, h=30, text="關(guān)燈", onclick=click_D,state='disabled') '''郵件設(shè)置'''my_sender='10******69@qq.com' # 設(shè)置發(fā)件人郵箱賬號,輸入自己的郵箱my_pass = '' # 設(shè)置發(fā)件人郵箱授權(quán)碼,不可為空my_name = 'IvanD.Mido' # 設(shè)置發(fā)件人郵箱昵稱my_user='10******69@qq.com' # 設(shè)置收件人郵箱賬號,我這邊發(fā)送給自己my_user_name = 'IvanD.Mido' # 設(shè)置收件人郵箱昵稱 # 定義警報發(fā)郵件函數(shù)def mail(content,title): ret=True # 定義一個標(biāo)記ret,記錄發(fā)送郵件事件,初始值為True try: msg=MIMEText(content,'plain','utf-8') # 創(chuàng)建郵件文本對象,Title對應(yīng)文本內(nèi)容,'plain'指設(shè)置的文本格式,'utf-8'指設(shè)置的編碼 '''三個頭部信息:發(fā)件人,收件人,主題''' msg['From']=formataddr([my_name,my_sender]) # 定義發(fā)件人信息:括號里的對應(yīng)發(fā)件人郵箱昵稱、發(fā)件人郵箱賬號 msg['To']=formataddr([my_user_name,my_user]) # 定義收件人信息:括號里的對應(yīng)收件人郵箱昵稱、收件人郵箱賬號 msg['Subject']=title # 定義郵件的主題,也可以說是標(biāo)題 server=smtplib.SMTP_SSL("http://smtp.qq.com", 465) # 創(chuàng)建SMTP服務(wù),連接qq郵箱服務(wù)器,發(fā)件人郵箱中的SMTP服務(wù)器,SMTP協(xié)議加密端口是465 server.login(my_sender, my_pass) # 登錄郵箱,括號中對應(yīng)的是發(fā)件人郵箱賬號、郵箱密碼 server.sendmail(my_sender,my_user,msg.as_string()) # 發(fā)送郵件,括號中對應(yīng)的是發(fā)件人郵箱賬號、收件人郵箱賬號、郵件信息的字符串格式 server.quit() # 關(guān)閉連接 except Exception: # 如果 try 中的語句沒有執(zhí)行,則會執(zhí)行下面的 ret=False ret=False # 將ret標(biāo)記記為False return ret # 返回ret標(biāo)記 soil_mail_enable = True # 定義發(fā)送土壤警報郵件使能的標(biāo)志light_mail_enable = True # 定義發(fā)送光線警報郵件使能的標(biāo)志 soil_mail_time = time.time() # 定時郵件發(fā)送時間記錄標(biāo)志light_mail_time = time.time() # 定時郵件發(fā)送時間記錄標(biāo)志 # 定義回調(diào)函數(shù)def sub_cb(client, userdata, msg): global soil_mail_enable,light_mail_enable,soil_mail_time,light_mail_time topic = msg.topic # topic數(shù)據(jù)存入變量 payload = msg.payload.decode() # payload消息數(shù)據(jù)轉(zhuǎn)換為字符串后存入變量 print("\nTopic:" + topic + " Message:" + payload) # 終端打印數(shù)據(jù) if topic == IOT_pubTopic1: # 定義接收到Soil_moisture_value時的操作 if payload.isdigit(): # 接收的消息是數(shù)字類型的則說明是濕度值而不是控制命令 Soil_moisture_value = int(payload) # 轉(zhuǎn)換為數(shù)字類型方便后面做判斷 text_value.config(text = str(Soil_moisture_value)) # 更新屏幕顯示數(shù)據(jù) if Soil_moisture_value < 2120: # 閾值設(shè)置為2120,可根據(jù)實際情況調(diào)整 text2.config(text="當(dāng)前土壤濕度過低") # 更新文字警告 if soil_mail_enable == True and (time.time()-soil_mail_time) > 10: # 如果允許且距離上次發(fā)送郵件時間間隔10秒以上才進(jìn)行發(fā)送的操作 ret1=mail("土壤濕度報警","當(dāng)前土壤濕度值為 " + str(Soil_moisture_value) + ",請及時補(bǔ)水!") if ret1: # 如果有發(fā)送郵件事件 print("郵件發(fā)送成功1") soil_mail_enable = False # 已經(jīng)發(fā)送過郵件,改變標(biāo)記避免重復(fù)發(fā)送 soil_mail_time = time.time() # 記錄本次郵件發(fā)送的時間 else: # 否則 print("郵件發(fā)送失敗1") else: text2.config(text="當(dāng)前土壤濕度適當(dāng)") # 更新文字 soil_mail_enable = True # 如果警報解除了就允許后續(xù)發(fā)郵件 elif topic == IOT_pubTopic2: # 定義接收到Light時的操作 if payload.isdigit(): # 接收的消息是數(shù)字類型的則說明是光線值而不是控制命令 light_value = int(payload) # 轉(zhuǎn)換為數(shù)字類型方便后面做判斷 text_value_2.config(text = str(light_value)) # 屏幕更新光線值 if light_value < 666: # 閾值設(shè)置為666,可根據(jù)實際情況調(diào)整 text4.config(text="當(dāng)前光線強(qiáng)度過低") # 更新文字警告 if light_mail_enable == True and (time.time()-light_mail_time) > 10: # 如果允許發(fā)送郵件才進(jìn)行發(fā)送的操作 ret2=mail("光線強(qiáng)度報警","當(dāng)前光線值為 " + str(light_value) + ",請及時補(bǔ)光!") if ret2: # 如果有發(fā)送郵件事件 print("郵件發(fā)送成功2") light_mail_enable = False # 已經(jīng)發(fā)送過郵件,改變標(biāo)記避免重復(fù)發(fā)送 light_mail_time = time.time() # 記錄本次郵件發(fā)送的時間 else: # 否則 print("郵件發(fā)送失敗2") else: text4.config(text="當(dāng)前光線強(qiáng)度適當(dāng)") # 更新文字警告 light_mail_enable = True # 如果警報解除了就允許后續(xù)發(fā)郵件 siot.init(CLIENT_ID, SERVER, user=IOT_UserName,password=IOT_PassWord) # 初始化,確定輸入的用戶名和密碼正確siot.connect() # 連接siot物聯(lián)網(wǎng)平臺siot.subscribe(IOT_pubTopic1, sub_cb) # 訂閱Topic1 土壤濕度數(shù)據(jù)siot.subscribe(IOT_pubTopic2, sub_cb) # 訂閱Topic2 光線強(qiáng)度數(shù)據(jù)siot.publish(IOT_pubTopic1,'auto') #發(fā)布信息'auto'至物聯(lián)網(wǎng)平臺 siot.publish(IOT_pubTopic2,'auto') #發(fā)布信息'auto'至物聯(lián)網(wǎng)平臺siot.loop() # 循環(huán) while True: # 循環(huán) time.sleep(0.5) # delay0.5秒 |
Tips:須填入實際的發(fā)件人賬號、授權(quán)碼等信息,不可為空。
3、程序運行
STEP1:運行程序觀察效果
點擊Mind+軟件上的運行按鈕,觀察行空板3,可以看到初始時模式為自動,此時澆水、關(guān)水、開燈、關(guān)燈四個按鈕為灰色,不可被點擊,點擊切換模式后,按鈕恢復(fù)正常狀態(tài),同時檢測到的土壤濕度值和光線值數(shù)據(jù)也顯示在了屏幕上。此時,在板3上點擊“澆水”和“開燈”后,板1上的水泵開始工作,板2上的LED亮起。當(dāng)點擊“關(guān)水”和“關(guān)燈”后,水泵和LED則停止了工作。
觀察物聯(lián)網(wǎng)平臺,可以看到在兩個不同的Topic下,皆有數(shù)據(jù)傳入。
同時,觀察QQ郵箱,當(dāng)土壤濕度或光線值數(shù)據(jù)不佳時,我們會接收到一封郵件。
Tip1:這里,由于我們直接將Python程序文件建在了行空板的內(nèi)存中,因此,我們也可以直接在板子上運行程序,以板3為例,操作方法如下:
Tip2:這里的路由器或手機(jī)熱點須能正常上網(wǎng),方可發(fā)送郵件。
挑戰(zhàn)自我
想一想,在本節(jié)課介紹的三種給行空板供電的方式中(電源適配器、充電寶、電腦USB口),對于需要長期運行程序的場景,該選擇哪種方式更適合呢,而對于需要在戶外使用的場景,又適合哪種方式呢?
除了發(fā)郵件警報作為一種手段之外,還可以有哪些可行的遠(yuǎn)程提醒的方法呢?
在既沒有路由器,也沒有手機(jī)熱點的場景下,我們還能搭建多節(jié)點的物聯(lián)網(wǎng)系統(tǒng)嗎?
Tips:答案見附錄2。
附錄
附錄2:
在既沒有路由器和手機(jī)也無法開熱點的情況下,我們也可以通過開啟其中一塊行空板自身的熱點功能創(chuàng)建一個Wi-Fi無線熱點,其他行空板都連接這個Wi-Fi熱點,來使各塊板子連在同一局域網(wǎng)內(nèi)。開啟方法如下。
-
物聯(lián)網(wǎng)
+關(guān)注
關(guān)注
2909文章
44557瀏覽量
372754 -
溫濕度傳感器
+關(guān)注
關(guān)注
5文章
579瀏覽量
35706 -
python
+關(guān)注
關(guān)注
56文章
4792瀏覽量
84627 -
IOT
+關(guān)注
關(guān)注
187文章
4202瀏覽量
196680 -
智慧農(nóng)業(yè)
+關(guān)注
關(guān)注
4文章
785瀏覽量
19832
發(fā)布評論請先 登錄
相關(guān)推薦
評論