設計與實現)
1. 為什么我要用STM32做一套實驗室消防預警系統(tǒng)實驗室這個場景跟普通的辦公室、住宅有本質區(qū)別。普通場所著火大概率是電線老化或者明火引燃實驗室里可能同時存在酒精燈、乙醚、氫氣鋼瓶、鋰電池充放電測試臺甚至還有學生半夜跑數據忘了關的加熱臺。火源類型復雜燃燒速度可能極快而且很多試劑燃燒產生的煙霧是有毒的。等煙大到人眼能看見再報警基本已經晚了。我讀研那會兒隔壁材料實驗室就出過一次小事故——一臺老化測試箱的溫控模塊失效內部溫度從80度飆到200多度塑料外殼開始冒煙好在當時有人通宵做實驗聞到了味道。事后復盤大家一致認為如果有一套能同時監(jiān)測溫度、煙霧濃度、并且能在異常時自動斷電聲光報警遠程通知的系統(tǒng)風險會小很多。這就是這個項目的出發(fā)點。整套系統(tǒng)基于STM32F103C8T6最小系統(tǒng)板搭建核心功能包括多傳感器融合檢測DHT11采集環(huán)境溫濕度MQ-2煙霧傳感器檢測可燃氣體和煙霧濃度火焰?zhèn)鞲衅髯鳛檩o助確認。分級預警邏輯不是簡單的“超閾值就響”而是根據溫度變化速率、煙霧濃度絕對值、以及兩者是否同時異常來做分級判斷減少誤報。自動斷電控制通過繼電器模塊控制實驗臺插座電源確認火情后直接切斷供電避免電氣火災擴大。聲光報警與本地顯示蜂鳴器LED閃爍OLED屏幕實時顯示各項數據。仿真驗證在Proteus里完成電路仿真驗證邏輯正確后再打板焊接。關鍵詞里提到的“原理圖”“仿真”“代碼”三件套我都會在下面逐一展開。這套東西適合電子類專業(yè)的學生做課程設計或畢業(yè)設計也適合實驗室安全管理人員做低成本改造參考。整套BOM成本控制在80元以內代碼全部開源。注意消防預警系統(tǒng)屬于安全相關設備本文分享的是教學和原型驗證級別的方案不能直接替代經過消防認證的專業(yè)設備。實際部署請務必以專業(yè)消防系統(tǒng)為準。2. 硬件選型為什么是STM32F103C8T6而不是別的2.1 主控芯片的取舍邏輯市面上做這類項目常見的主控選擇有51單片機、Arduino、STM32F103、ESP32這幾種。我選STM32F103C8T6的理由很具體51單片機比如STC89C52RC確實便宜但它的ADC精度只有8位DHT11的時序對晶振頻率敏感51的機器周期導致微秒級延時不好做讀溫濕度經常出錯。而且51的RAM太小想跑一個稍微復雜點的分級預警狀態(tài)機就很吃力。Arduino UnoATmega328P開發(fā)快但同樣存在ADC位數和RAM的限制而且成本比STM32最小系統(tǒng)板還高。最關鍵的是Arduino的生態(tài)偏向快速原型工業(yè)場景下大家更認STM32。ESP32功能強自帶WiFi和藍牙但功耗偏高而且對于這個項目來說WiFi不是必須的——實驗室環(huán)境不一定有可用的無線網絡而且無線模塊會增加調試復雜度。如果后期確實需要遠程通知加一個ESP-01S模塊通過串口通信就行沒必要一開始就上ESP32。STM32F103C8T6的優(yōu)勢在于72MHz主頻、64KB Flash、20KB RAM、12位ADC、多個定時器和USART接口價格只要10塊錢左右。它的定時器可以精確產生微秒級延時讀DHT11毫無壓力12位ADC讀MQ-2的模擬輸出足夠細膩USART可以接藍牙模塊做調試輸出。這個配置做消防預警系統(tǒng)綽綽有余而且資料多、社區(qū)活躍遇到問題容易找到答案。2.2 傳感器選型的實際考量DHT11是入門級溫濕度傳感器精度±2℃、±5%RH采樣周期1秒。有人會問為什么不用DS18B20或者SHT30。DS18B20是單總線數字傳感器精度更高±0.5℃但它只測溫度不測濕度。SHT30精度好、I2C接口但價格是DHT11的5倍以上。對于消防預警來說溫度的絕對精度不是最關鍵的溫度變化的趨勢和速率才是判斷火情的重要依據。DHT11的1℃分辨率足夠捕捉到異常升溫。MQ-2是半導體式可燃氣體傳感器對液化氣、丙烷、氫氣、煙霧都有響應。它的輸出是模擬電壓濃度越高電壓越高。需要注意的是MQ-2需要預熱——冷啟動時讀數不穩(wěn)定通常需要預熱20秒以上才能得到可靠數據。這一點在代碼里必須處理否則上電初期會誤報?;鹧?zhèn)鞲衅魑疫x的是那種帶比較器輸出的模塊檢測到火焰時輸出低電平。它的檢測角度大約60度有效距離1米左右。這個傳感器只能作為輔助確認不能單獨作為報警依據因為它對非火焰的強光源也可能有反應。2.3 繼電器與電源方案繼電器模塊選的是5V驅動的單路繼電器觸點容量10A/250VAC。實驗臺插座的火線串進繼電器的常閉觸點正常情況下繼電器不吸合插座有電報警觸發(fā)后STM32輸出高電平讓繼電器吸合常閉觸點斷開插座斷電。這里有個安全設計細節(jié)我用的是常閉觸點而不是常開觸點。原因是如果系統(tǒng)本身斷電了比如STM32死機或者電源被拔繼電器失電常閉觸點保持閉合插座仍然有電——這看起來好像不安全但實際上如果系統(tǒng)完全斷電說明整個預警系統(tǒng)已經失效此時保持插座供電反而不會造成“系統(tǒng)誤動作導致實驗中斷”的問題。而如果火情確認后需要斷電STM32主動吸合繼電器即可。這個邏輯在代碼里要配合狀態(tài)機來設計。電源方面STM32最小系統(tǒng)板通過USB供電或者外部5V適配器供電傳感器和繼電器都從5V取電。MQ-2的加熱絲電流大約150mA繼電器吸合電流大約70mA加上STM32本身和其他傳感器總電流在300mA左右一個5V/2A的適配器完全夠用。3. 原理圖設計從模塊連接到嘉立創(chuàng)畫圖實操3.1 整體連接框架原理圖設計我是在嘉立創(chuàng)EDA里完成的也可以用Altium Designer或者OrCAD。整體連接關系如下STM32F103C8T6最小系統(tǒng)板作為核心引出5V、3.3V、GND、以及各個GPIO。DHT11數據腳接PA0VCC接3.3VGND接地。數據腳需要接一個4.7kΩ上拉電阻到3.3V。MQ-2模擬輸出接PA1ADC1_IN1VCC接5VGND接地。模塊自帶電位器可以調節(jié)靈敏度?;鹧?zhèn)鞲衅鲾底州敵鼋覲A2VCC接3.3VGND接地。繼電器模塊控制腳接PA3VCC接5VGND接地。OLED屏幕SSD1306I2C接口SCL接PB6SDA接PB7VCC接3.3VGND接地。蜂鳴器接PA4通過一個NPN三極管S8050驅動基極串1kΩ電阻。LED指示燈綠色LED接PA5正常狀態(tài)紅色LED接PA6報警狀態(tài)各串220Ω限流電阻。3.2 畫圖時的幾個關鍵細節(jié)DHT11的上拉電阻不能省。DHT11的數據線是開漏輸出沒有上拉電阻的話總線空閑時無法拉高STM32讀到的全是0。我一開始在面包板上搭電路時忘了接上拉調試了半天以為是時序問題后來用示波器看波形才發(fā)現數據線一直是低電平。這個坑很典型畫原理圖時一定要把上拉電阻畫上。MQ-2的模擬輸出要接在STM32的ADC通道上。STM32F103C8T6的PA0~PA7對應ADC1的通道0~7PA1就是ADC1_IN1。在代碼里配置ADC時要對應好通道號否則讀出來的數據是錯的。繼電器的控制邏輯要確認。市面上很多繼電器模塊是低電平觸發(fā)也就是控制腳給低電平時繼電器吸合。但也有一些是高電平觸發(fā)。畫原理圖時不用管這個但在代碼里要定義好宏方便切換。我用的模塊是高電平觸發(fā)所以代碼里RELAY_ON定義為GPIO_SetBits。OLED的I2C地址。SSD1306的I2C地址通常是0x787位地址或0x3C8位地址左移一位。不同廠家的模塊可能不一樣畫圖時不用管但寫代碼時要確認。我用的模塊地址是0x78。電源去耦。每個芯片的VCC和GND之間都要加0.1μF的陶瓷電容MQ-2和繼電器模塊的電源腳附近再加一個100μF的電解電容防止繼電器吸合時電流突變導致STM32復位。3.3 從原理圖到PCB的注意事項畫完原理圖后生成PCB時要注意幾點MQ-2的加熱絲電流較大走線寬度至少20mil不要用細線。繼電器的強電部分和弱電部分要隔離PCB上強弱電之間保持至少3mm的爬電距離。晶振盡量靠近STM32走線短而直下面不要走其他信號線。ADC輸入線遠離數字信號線避免數字噪聲耦合到模擬信號上。如果只是做課程設計或者驗證可以直接用最小系統(tǒng)板模塊的方式在洞洞板上焊接不一定要打PCB。但如果是畢業(yè)設計需要展示完整的作品建議打一版PCB看起來更專業(yè)。4. 代碼架構分級預警狀態(tài)機是怎么跑起來的4.1 主循環(huán)與定時器節(jié)拍整個代碼基于一個1ms的定時器節(jié)拍來調度。我用的是TIM2配置為1ms中斷一次。在中斷里維護幾個計數器volatile uint32_t tick_1ms 0; volatile uint8_t flag_1s 0; volatile uint8_t flag_5s 0; void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) ! RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); tick_1ms; if (tick_1ms % 1000 0) flag_1s 1; if (tick_1ms % 5000 0) flag_5s 1; } }主循環(huán)里根據這些標志位來調度任務每1秒讀一次DHT11和MQ-2每5秒更新一次OLED顯示每100ms檢查一次火焰?zhèn)鞲衅?。這樣做的目的是避免在主循環(huán)里用delay阻塞讓系統(tǒng)能及時響應火焰?zhèn)鞲衅鞯闹袛嘈盘枴?.2 DHT11的讀取時序與容錯DHT11是單總線協(xié)議時序要求比較嚴格。STM32的主頻是72MHz一個機器周期約13.9ns用delay_us函數可以精確控制微秒級延時。讀取流程是STM32拉低數據線至少18ms然后拉高20-40μs等待DHT11響應。DHT11拉低80μs再拉高80μs表示數據即將開始。之后每一位數據以50μs低電平開始高電平持續(xù)26-28μs表示0持續(xù)70μs表示1。代碼里我用了一個簡單的狀態(tài)機來讀取40位數據8位濕度整數8位濕度小數8位溫度整數8位溫度小數8位校驗和。校驗和等于前四個字節(jié)之和的低8位。容錯處理很關鍵。DHT11偶爾會讀失敗返回全0或者校驗錯誤。我的做法是連續(xù)讀3次如果3次都失敗才認為傳感器故障在OLED上顯示“DHT ERR”。如果只是偶爾一次失敗就沿用上一次的有效數據。這樣避免了因為一次讀取失敗就觸發(fā)誤報。4.3 MQ-2的ADC采樣與滑動濾波MQ-2的輸出電壓隨煙霧濃度升高而升高。STM32的ADC是12位的參考電壓3.3V所以ADC值0~4095對應0~3.3V。我用了滑動平均濾波維護一個長度為10的數組每次采樣后替換最舊的數據然后求平均值。#define FILTER_LEN 10 uint16_t mq2_buf[FILTER_LEN] {0}; uint8_t mq2_idx 0; uint16_t mq2_get_filtered(void) { mq2_buf[mq2_idx] ADC_GetConversionValue(ADC1); mq2_idx (mq2_idx 1) % FILTER_LEN; uint32_t sum 0; for (int i 0; i FILTER_LEN; i) sum mq2_buf[i]; return sum / FILTER_LEN; }濾波之后還要做基線校準。上電后前20秒MQ-2處于預熱階段讀數會從高到低變化。我在代碼里讓前20秒不進行報警判斷同時記錄這20秒內的最低值作為基線。之后的報警閾值設為基線值加上一個偏移量比如300而不是用一個固定的絕對值。這樣能適應不同環(huán)境下的傳感器差異。4.4 分級預警狀態(tài)機的設計這是整個代碼的核心。我把系統(tǒng)狀態(tài)分為四級狀態(tài)觸發(fā)條件動作NORMAL溫度40℃且煙霧閾值綠燈亮蜂鳴器不響WARNING溫度40~60℃或煙霧超閾值黃燈閃爍蜂鳴器間歇響ALARM溫度60℃或煙霧嚴重超標紅燈亮蜂鳴器長響繼電器斷電FIRE火焰?zhèn)鞲衅饔|發(fā)且溫度50℃紅燈快閃蜂鳴器長響繼電器斷電狀態(tài)之間的切換不是瞬時的而是帶有遲滯和確認時間。比如從NORMAL到WARNING需要連續(xù)3次采樣都滿足條件才切換從WARNING回到NORMAL需要連續(xù)5次采樣都正常才恢復。這樣避免了數據抖動導致的頻繁切換。火焰?zhèn)鞲衅鞯捻憫侵袛喾绞揭坏┯|發(fā)立即進入FIRE狀態(tài)但會先確認溫度是否也異常。如果火焰?zhèn)鞲衅饔|發(fā)但溫度正常可能是強光干擾系統(tǒng)會進入WARNING而不是FIRE。4.5 繼電器控制的安全邏輯繼電器控制我加了一個雙重確認機制只有當狀態(tài)機進入ALARM或FIRE并且持續(xù)超過2秒才真正吸合繼電器斷電。這樣做是為了防止瞬時干擾導致誤斷電。畢竟實驗室里跑著實驗突然斷電可能造成數據丟失或者樣品損壞。另外代碼里還加了一個手動復位功能按下連接在PB0的按鍵可以強制將狀態(tài)機復位到NORMAL繼電器恢復常閉。這個功能是給實驗人員確認安全后手動恢復供電用的。5. Proteus仿真在沒有硬件的情況下驗證邏輯5.1 仿真環(huán)境的搭建Proteus 8.9以上版本支持STM32F103C8T6的仿真。搭建步驟在Proteus里新建工程放置STM32F103C8T6芯片。添加DHT11、MQ-2、OLED、繼電器、LED、蜂鳴器等元件。Proteus的元件庫里有DHT11和SSD1306的模型MQ-2可以用一個電位器模擬模擬輸出。連接電路和原理圖一致。加載編譯好的hex文件設置晶振頻率為8MHz外部晶振或72MHz內部PLL。5.2 仿真中的幾個坑DHT11的仿真模型響應速度。Proteus里的DHT11模型不是實時的它的溫濕度值需要在屬性里手動設置或者通過腳本動態(tài)修改。我一開始以為它會像真實傳感器一樣自動變化結果仿真跑起來讀數一直不變。后來在DHT11的屬性里設置了初始值并且用Proteus的腳本功能定時修改才模擬出溫度上升的場景。MQ-2的模擬輸出。Proteus里沒有MQ-2的專用模型我用了一個電位器分壓來模擬。電位器的中間抽頭接STM32的PA1通過調整電位器來改變電壓模擬煙霧濃度的變化。這個方法雖然簡單但足夠驗證ADC采樣和報警邏輯。OLED的顯示。Proteus里的SSD1306模型可以顯示I2C通信的內容但刷新速度比真實硬件慢。仿真時不要頻繁刷新OLED否則會拖慢整個仿真速度。我設置的是每5秒刷新一次仿真跑起來還算流暢。繼電器的仿真。Proteus里的繼電器模型有吸合時間默認是10ms左右。仿真時可以看到繼電器狀態(tài)的變化但聽不到聲音。蜂鳴器可以用一個LED代替來觀察狀態(tài)。5.3 仿真驗證的測試用例我在仿真里設計了幾個測試場景場景一溫度從25℃緩慢上升到70℃觀察狀態(tài)機是否按NORMAL→WARNING→ALARM的順序切換繼電器是否在ALARM狀態(tài)持續(xù)2秒后吸合。場景二溫度正常但MQ-2電壓突然升高到3V觀察是否進入WARNING以及是否在持續(xù)超標后進入ALARM。場景三火焰?zhèn)鞲衅饔|發(fā)但溫度只有30℃觀察是否進入WARNING而不是FIRE。場景四報警后按下復位按鍵觀察狀態(tài)機是否回到NORMAL繼電器是否恢復。這四個場景跑通后基本可以確認邏輯沒有問題。仿真通過后再打板焊接成功率會高很多。6. 調試過程中踩過的坑和解決方法6.1 DHT11讀數一直是0這個問題我遇到過兩次。第一次是因為上拉電阻沒接數據線無法拉高。第二次是因為延時函數不準確——我用的delay_us是基于SysTick的但SysTick的配置被其他庫函數修改了導致延時偏短。后來我改用TIM2做微秒延時問題解決。排查方法用示波器或者邏輯分析儀看數據線的波形。正常情況應該能看到STM32拉低18ms、拉高20μs、DHT11響應拉低80μs的波形。如果波形不對就是時序問題如果數據線一直是低電平就是上拉電阻的問題。6.2 MQ-2上電后一直報警MQ-2冷啟動時加熱絲從室溫升到工作溫度需要時間這期間傳感器的輸出電阻不穩(wěn)定讀數會偏高。我一開始沒做預熱處理上電后MQ-2的ADC值直接超過閾值系統(tǒng)進入ALARM狀態(tài)。解決方法在代碼里加一個20秒的預熱期前20秒不進行報警判斷同時用這段時間采集基線值。預熱期結束后用基線值偏移量作為動態(tài)閾值。6.3 繼電器吸合導致STM32復位這個問題很典型。繼電器吸合瞬間電流突變如果電源濾波不好5V電壓會瞬間跌落導致STM32復位。我一開始以為是代碼問題后來用示波器看5V電源線發(fā)現繼電器吸合時電壓從5V跌到3.8V持續(xù)了大約5ms。解決方法在繼電器模塊的電源腳附近加一個100μF的電解電容和一個0.1μF的陶瓷電容同時在STM32的電源腳也加0.1μF去耦電容。另外繼電器的控制信號線和電源線分開走避免耦合。6.4 OLED顯示亂碼OLED顯示亂碼通常是I2C通信問題。我遇到的原因是I2C的時鐘頻率太高SSD1306跟不上。STM32的I2C默認是100kHz但我配置成了400kHz導致數據傳輸出錯。解決方法把I2C時鐘降到100kHz或者在每次發(fā)送數據后加一個小延時。另外SSD1306的初始化序列要嚴格按照數據手冊來特別是對比度、掃描方向、顯示模式這些寄存器。6.5 火焰?zhèn)鞲衅髡`觸發(fā)火焰?zhèn)鞲衅鲗Υ蚧饳C的火焰很敏感但對日光燈、白熾燈也可能有反應。我在實驗室測試時發(fā)現用手機閃光燈照一下傳感器它也會觸發(fā)。解決方法在代碼里加確認邏輯——火焰?zhèn)鞲衅饔|發(fā)后先檢查溫度是否也異常。如果溫度正常只進入WARNING狀態(tài)不觸發(fā)斷電。同時火焰?zhèn)鞲衅鞯臄底州敵隹梢约右粋€RC濾波減少瞬時干擾。7. 這套系統(tǒng)還能怎么擴展7.1 加藍牙模塊做遠程通知STM32F103C8T6有兩個USART其中一個用來燒錄程序另一個可以接HC-05藍牙模塊。報警時通過藍牙發(fā)送一條消息到手機配合手機端的串口助手APP就能實現遠程通知。代碼里只需要在狀態(tài)機切換時調用USART_SendString發(fā)送預設的消息即可。7.2 加SD卡模塊做數據記錄實驗室事故調查往往需要回溯數據。加一個SD卡模塊通過SPI接口每隔1分鐘把溫度、濕度、煙霧濃度寫入CSV文件。這樣即使系統(tǒng)沒有聯網也能在事后分析數據。SD卡模塊很便宜代碼也不復雜用FatFS文件系統(tǒng)就能實現。7.3 多節(jié)點組網一個實驗室可能有多個實驗臺每個實驗臺放一個預警節(jié)點通過RS485總線或者CAN總線連接到中央監(jiān)控屏。STM32F103C8T6自帶CAN控制器加一個CAN收發(fā)器比如TJA1050就能組網。中央節(jié)點用一個STM32觸摸屏顯示所有節(jié)點的狀態(tài)。7.4 低功耗改造如果實驗室沒有常電供應可以用電池供電。STM32F103C8T6有睡眠模式DHT11和MQ-2也可以間歇供電。不過MQ-2的加熱絲功耗較大低功耗改造需要換用低功耗的煙霧傳感器比如MQ-7或者專用的光電式煙霧傳感器。8. 開源資料說明與復現建議整套資料包括原理圖嘉立創(chuàng)EDA格式包含STM32最小系統(tǒng)、傳感器接口、繼電器驅動、OLED接口。代碼Keil MDK工程基于標準外設庫包含DHT11驅動、MQ-2 ADC采樣、OLED驅動、狀態(tài)機邏輯。仿真Proteus工程文件包含電路和測試腳本。BOM清單所有元器件的型號、數量、參考價格。復現建議先跑仿真確認邏輯正確后再買硬件。硬件焊接時先焊電源部分測好5V和3.3V電壓后再焊STM32和傳感器。調試時先用串口打印數據確認每個傳感器都能正常讀數再調狀態(tài)機邏輯。我在實際調試中最大的體會是不要相信任何一個傳感器的單次讀數。溫度、煙霧、火焰任何一個傳感器都可能因為干擾、老化、環(huán)境變化而出現異常值。只有通過多傳感器交叉驗證、滑動濾波、狀態(tài)機遲滯這些手段才能做出一個誤報率低、可靠性高的預警系統(tǒng)。這套代碼我前后改了三個版本第一版誤報頻繁第二版響應太慢第三版才在靈敏度和可靠性之間找到平衡。如果你也在做類似的項目建議把狀態(tài)機的參數做成可配置的宏定義方便根據實際環(huán)境調整。