1.前言 冬天很冷,買了一個鍋爐,需要迴圈泵的。簡單來說就是鍋爐水熱了之後迴圈泵自動開啟,然後將熱水輸送走,送到暖 氣,熱水抽走,涼水進入鍋爐,溫度降低,迴圈泵關閉,等待下一次水燒熱。因為需要取暖的房子距離燒鍋爐的地方比較遠,所以需要迴圈 泵,如果距離近的話水燒熱後利用熱水上流冷水迴流的原理會自動完 ...
1.前言
冬天很冷,買了一個鍋爐,需要迴圈泵的。簡單來說就是鍋爐水熱了之後迴圈泵自動開啟,然後將熱水輸送走,送到暖
氣,熱水抽走,涼水進入鍋爐,溫度降低,迴圈泵關閉,等待下一次水燒熱。因為需要取暖的房子距離燒鍋爐的地方比較遠,所以需要迴圈
泵,如果距離近的話水燒熱後利用熱水上流冷水迴流的原理會自動完成迴圈。當然目前市場上有這種利用溫度自動控制迴圈泵開啟關閉的設
備:
原理就是有一個熱敏電阻探頭(帶有磁鐵吸附,可以吸附到鍋爐壁上),然後一個繼電器控制的。當溫度達到設定的值後,繼電器開啟,循
環泵啟動,迴圈過後鍋爐壁溫度下降,繼電器關閉,迴圈泵關閉。
迴圈泵:
由於我家的迴圈泵功率較大,小繼電器啟動幾次就燒壞了,所以中間又接入了一層交流接觸器(我的老父親加的)。
這種市面上的設備可以大致解決水迴圈的問題,但是也有一些細節問題解決不了,例如:當迴流管水溫度也達到設定的溫度值後,迴圈泵就
會一直開啟狀態,這時候就需要手動去調節旋鈕溫度(調高),讓迴圈泵停下來(大功率迴圈泵很耗電)。又如 當爐子內部煤漸漸燒結束後
(東北話叫澇了),鍋爐溫度達不到設定值,這個時候就需要降低旋鈕,讓迴圈泵啟動,讓這些煤產生的餘溫不浪費掉。
由於以上的不足,就需要有人隔幾個小時去調節旋鈕。這部分工作都是我老父親在做,所以我想做一個自動的東西降低一下老父親的工作
量。
2.項目結構
我最初的構思是通過手機可以遠程調節溫度,這樣至少不用手動去調節旋鈕了,我半夜起來在被窩裡用手機調節一下就可以了。基於最初的
構思設計的結構:
3.硬體搭建
esp01模塊+繼電器模塊,220v轉5v模塊 + 插排 = 聯網插座
esp8226 + 溫度感測器 + 數位管 = 實時溫度檢測顯示聯網模塊
我的控制系統都寫在 esp8226中。
溫度感測器探頭製作:要驅動ds18b20感測器,需要在數據匯流排(DQ)與 VDD 引腳之間加入一個4.7k歐姆的電阻(上拉電阻),這個是必須
有的,用一個鐵紐扣包裹起來,裡面放了兩個小塊的釹磁鐵,最後用哥倆好膠水灌滿,這樣就形成了一個溫度感測器探頭,可以吸附在鍋爐
壁上:
這一步我犯了一個錯誤,將ds18b20感測器和上拉電阻都放入到探頭裡面,測溫的時候發現溫度升高以後溫度感測器讀取溫度失敗,是因為
溫度升高導致電阻阻值增大。後來我將其電阻放到尾部就解決了這個問題。
封裝後的探頭:
esp8226 + 數位管模塊:
殼子是我用3D印表機列印的,裡面放著esp8226模塊。
4.軟體編寫
固件
esp8226 和 esp01 我都是燒錄的micropython固件。
通信
通信開始我想的是用socket實現,簡單弄個HTTP協議進行通信,但是手機上這樣控制是比較麻煩的,沒有好用的軟體,我還要做個web界
面,另外socket會阻塞線程。
後來我發現了MQTT這種協議,這是一種針對物聯網的硬體網路通信協議,可以應對高延遲的網路環境。簡單介紹一下MQTT協議,首先需
要一個服務端(也有叫代{過}{濾}理伺服器的,運行在電腦上,這裡我運行在樹莓派上),然後你的所有物聯網硬體設備(如esp8226)都是客
戶端,物聯網硬體設備(客戶端)之間不會直接進行通信,它們都是與服務端直接進行通信,硬體設備之間如果需要進行通信是需要發佈或
者訂閱主題的。例如:如果硬體A 和硬體B 之間需要進行通信,那麼首先它們都需要連接到 服務端,然後 A 發佈一個名為topic的主題,如果
硬體B要接受A的信息就需要訂閱 topic這個主題,這樣就實現了 A —> B 的通信,那麼反向通信也是一個原理,B 發佈主題,A來訂閱。這個
協議還有一個優勢,可以一個主題多個客戶端去訂閱,這樣就能實現多端通信。mqtt協議通信中每個客戶端不知道其它客戶端的存在,他們
都是與服務端進行直接通信。
mqtt協議在micropython固件(我不知道安信可預設固件是不是也有)通信有一個很大的問題:LmacRxBlk:1報錯。例如,如果你在開始的時
候訂閱了一個主題,然後你使用非阻塞的方法check_msg()通過回調函數處理 訂閱的主題消息,這個時候如果你訂閱的主題發佈的次數超過
你 check_msg()的次數,那麼就會在micropython固件底層報一個錯誤 LmacRxBlk:1,然後通信中斷,簡單來說就是你訂閱一個主題後進入
事件迴圈,來不及處理訂閱主題的消息,就會導致這個錯誤。這個錯誤官方解釋是tcp buffer資源沒有釋放導致的,因為esp8226可用資源非
常有限,但是在mqtt通信中我不可能每次迴圈後都去釋放連接,然後每次都重新建立連接,並且這個報錯噁心的地方在於,try except 是捕
獲不到的,那麼對於處理這個錯誤只能通過設計上來解決了,也就是一開始就保證其訂閱的主題發佈的間隔遠小於其迴圈的間隔,保證每次
都能及時的處理訂閱的消息。這個問題我解決了很久,因為try except 捕獲不到,並且不常出現,無法定位,完全不知道為什麼通信會中
斷。
代碼核心邏輯編寫
最核心的控制邏輯我都寫在esp8226模塊中,迴圈採集溫度,然後將溫度作為一個主題發佈出來,手機可以訂閱主題就可以實時查看鍋爐溫
度了,在迴圈中有一個設定溫度的變數 ,如果採集的溫度高於 設定溫度 那麼就發佈一個 開關主題開啟的主題,如果溫度低於 設定溫度,那
麽就發佈一個 開關主題關閉,並且每次訂閱 設置主題,用於修改設定溫度變數。
esp01 控制繼電器,每次訂閱 開關主題 就可以了,然後每次再把 開關當前狀態 作為一個主題發佈出來。
這個代碼邏輯最開始我是這樣寫的,手機上只需要發佈設置溫度主題就可以了,但是也是需要人一段時間用手機去調節一次,還是不能實現自動化,後來我又修改的 esp8226 中的代碼邏輯,複雜了一些。
esp8226 核心邏輯還是和上述一樣,每次檢測溫度高於設定溫度後還會開啟開關,但是啟動後檢測到溫度低於設定溫度不會立刻關閉開關,
而是等待檢測溫度低於設定溫度減去一個變數再發佈一個開關關閉的主題。這是為瞭解決 檢測溫度在設定溫度臨界反覆跳變,從而導致開關
在短時間內反覆的開啟關閉,這裡引入了一個減去變數,我將其叫做溫度步長 。這裡就有一個問題,如果鍋爐一直在升溫,那麼就算迴圈泵
一直開啟 ,鍋爐溫度也不會低於設定溫度(迴流管溫度已經高於設定溫度了),那麼迴圈泵就會處於一直開啟狀態,所以這裡我引入了第二
個變數:啟動最大時長,每次啟動後我開啟一個計時器,如果計時器時間超過 啟動最大時長,那麼無論此時的溫度是否低於設定溫度,都會
關閉迴圈泵,並且將設定溫度提高,提高溫度為溫度步長加當前溫度,這裡就實現了 自動調節 設定溫度的(升高方向)。還有一個問題:
當爐子煤燃燒殆盡的時候,溫度逐漸降低,檢測到的溫度肯定會遠低於設定溫度,迴圈泵就永遠不會開啟了。所以這裡我引入了第三個變數
(回調檢測時間),當esp8226 上電啟動時,啟動一個計時器,如果計時時間 等於回調檢測時間,那麼將設定溫度 調節到當前溫度,用於
設定溫度 自動回調,假設回調檢測時間 為 30分鐘,那麼即使設定溫度 高於 檢測溫度,也會實現30分鐘一啟動迴圈泵,和剩餘邏輯實現一
個閉環,這樣就能實現 設定溫度,上下自動調節。這裡我還加入了一個變數最低溫度,如果當前溫度低於最低溫度,那麼就算 當前溫度高
於設定溫度 也不會啟動迴圈泵,用於沒燒煤炭的時候,這樣就防止了無論什麼時候都30分鐘啟動一次迴圈泵,最低溫度 設置成高於環境溫
度一些就可以了。
這裡 設定溫度,溫度步長,啟動最大時長,回調檢測時間,最低溫度,都可以通過手機端進行設置,我讓esp8226訂閱這些主題用於設置這
些變數。
esp8226代碼:
Python學習交流Q群:906715085### 複製代碼 隱藏代碼 from machine import Pin,reset import onewire from ds18x20 import DS18X20 import time import tm1637 from umqtt.simple import MQTTClient def main(): client_id = "esp_temperature" mserver = '192.168.0.99' #mserver = '192.168.3.200' #mserver = 'mq.tongxinmao.com' tm = tm1637.TM1637(clk=Pin(14), dio=Pin(12)) ow=onewire.OneWire(Pin(4)) d = DS18X20(ow) rom = d.scan() def sub_callback(topic, msg): # print((topic, msg)) nonlocal setTemperature nonlocal lowTemperature nonlocal startTime nonlocal scanTime nonlocal step nonlocal startTimeTemp nonlocal scanTimeTemp nonlocal switchStatus data = int(msg.decode()) if topic == b'setTemperature': setTemperature = data elif topic == b"setLowTemperature": lowTemperature = data elif topic == b"setStartTime": startTime = data startTimeTemp = startTime elif topic == b"setScanTime": scanTime = data*60 scanTimeTemp = scanTime elif topic == b"setStep": step = data elif topic == b"switchWell": switchStatus = data else: print("錯誤") def publishInfo(): nonlocal client client.publish("setTemperatureR",str(setTemperature),retain=True) client.publish("setLowTemperatureR",str(lowTemperature),retain=True) client.publish("setStartTimeR",str(startTime),retain=True) client.publish("setScanTimeR",str(int(scanTime/60)),retain=True) client.publish("setStepR",str(step),retain=True) client = MQTTClient(client_id, mserver, 0) client.set_callback(sub_callback) client.connect() client.subscribe(b'setTemperature') client.subscribe(b'setLowTemperature') client.subscribe(b'setStartTime') client.subscribe(b'setScanTime') client.subscribe(b"setStep") client.subscribe(b"switchWell") showSet = True setTemperature = 40 lowTemperature = 40 startTime = 120 #啟動時間 scanTime = 1800 #30分鐘 step = 7 switchStatus = 0 # 0表示關閉,1表示開啟 startTimeTemp = startTime scanTimeTemp = scanTime while True: try: d.convert_temp() # 顯示溫度和設定溫度 nowTemperature = d.read_temp(rom[0]) if showSet: tm.temperature(int(setTemperature)) else: tm.number(int(nowTemperature*10)) if int(nowTemperature) >= setTemperature and (not switchStatus) and int(nowTemperature) > lowTemperature:#高於設定溫度啟動 client.publish("switch",'1',retain=True) startTimeTemp = startTime if (int(nowTemperature) <= setTemperature - step) and switchStatus: client.publish("switch",'0',retain=True) if startTimeTemp <= 0: #如果時間超過3分鐘,自動停止,並提高設定溫度 client.publish("switch",'0',retain=True) setTemperature = int(nowTemperature + step) startTimeTemp = startTime if switchStatus: startTimeTemp -= 1 client.check_msg() client.publish("temperature",str(round(nowTemperature,2)),retain=True) publishInfo() if scanTimeTemp <= 0: setTemperature = int(nowTemperature - step) #回調降溫 scanTimeTemp = scanTime scanTimeTemp -= 1 except Exception as e: reset() time.sleep(1) showSet = not showSet
esp01中代碼:
Python學習交流Q群:906715085### 複製代碼 隱藏代碼 from machine import Pin from umqtt.simple import MQTTClient import time def main(): def sub_callback(topic, msg): nonlocal client nonlocal pin """ 收到訂閱消息回調 """ if msg == b'0': pin.off() else: pin.on() client.publish("switchWell",str(pin.value()),retain=True) client_id = "switch_id" mserver = '192.168.0.99' #mserver = 'mq.tongxinmao.com' pin = Pin(0,Pin.OUT) client = MQTTClient(client_id, mserver, 0) client.set_callback(sub_callback) client.connect() client.subscribe(b'switch') while True: client.check_msg() client.publish("switchStatus",str(pin.value()),retain=True)
5.客戶端監控調節軟體
1.手機端:
MQTT Dash:
IoTMQTTPanel:
手機上我還是推薦IoTMQTTPanel,因為將一套面板的配置發佈到另一臺手機很方便,只需要發佈一個主題,然後接收端訂閱一個同名主題
就可以把整個面板發佈過去。MQTT Dash 也具有這個功能,但是發佈過去後會卡死,不知道為什麼。
2.電腦端:
電腦端我還不知道有什麼好用的軟體,所以我用 PyQt5 簡單寫了一個監控的軟體,沒有調節功能,因為當時設備還不是很穩定,所以我一直
在監控運行狀態。
6.後記
目前,整個東西已經穩定運行二周了,再也不需要人進行調節了。我反反覆復弄了挺長時間,才把所有問題都解決,特別是那個
LmacRxBlk:1報錯,花費了我很長時間。從零開始做一個能實際有用的東西還是挺困難的,因為有些問題只能通過實際的環境才能暴露出
來,例如上拉電阻升溫後導致阻值變化。還有很多細節我沒提及,例如燒錄固件,3d建模外殼,mosquitto服務在樹莓派上部署,埠轉發,
內容太多不便贅述,只能把主要內容和邏輯進行簡單敘述。