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