本文分享自華爲雲社區《基於STM32的智慧農業管理系統設計與實現》,作者: DS小龍哥。
一、前言
1.1 項目介紹
【1】項目功能
隨着全球農業現代化進程的加快,以及物聯網、人工智能等先進技術的發展與應用,智慧農業已經成爲現代農業發展的新趨勢。基於精準感知、智能控制和遠程管理的智慧農業系統能夠顯著提升農作物生產效率,降低資源消耗,實現環境友好型可持續農業生產。
在當前背景下,我國正大力推進數字鄉村建設,智慧農業管理系統作爲其中的重要組成部分,對於提高農業生產精細化管理水平,解決傳統農業中信息獲取不及時、人工管理成本高、決策缺乏科學依據等問題具有重要作用。
本設計開發一套基於STM32F103RCT6主控芯片的智慧農業管理系統,通過集成DHT11溫溼度傳感器、BH1750光照強度傳感器以及土壤溼度檢測傳感器,實時監測農田環境和作物生長狀態,並在超出閾值時通過蜂鳴器報警,提醒管理人員進行灌溉、施肥等操作。同時,採用NBIoT通信技術(BC26模塊)將採集到的數據上傳至雲端,利用EMQX開源MQTT服務器框架部署於華爲雲ECS服務器上的MQTT服務器,實現數據的遠程展示與處理。
系統支持微信小程序遠程控制功能,使得農戶或管理者可以隨時隨地查看農田環境參數、接收預警信息,並能遠程手動控制灌溉設備、補光燈等,大大提高了農業生產的智能化和便捷性。此項目的實施不僅有助於推動我國農業信息化水平的提升,也有利於農業資源的高效利用,對保障國家糧食安全、促進農業增效、農民增收具有重要意義。
【2】設計實現的功能
(1)實時環境監測:系統通過集成的DHT11溫溼度傳感器、BH1750光照強度傳感器以及土壤溼度檢測傳感器,實時監測農田環境中的溫度、溼度、光照強度和土壤含水量等關鍵參數。當這些參數超過或低於預設閾值時,系統將自動觸發蜂鳴器報警,提醒管理人員關注並採取相應措施。
(2)自動化管理與預警:根據土壤溼度傳感器檢測的數據,如果土壤溼度低於設定的適宜作物生長的含水量閥值,則系統會自動提醒管理者進行灌溉操作。同時,可以按照預設週期發送施肥提醒,以確保農作物在最佳時期得到充足的水分和養分供應。
(3)遠程控制功能:利用NBIoT通信技術(BC26模塊)將現場採集到的各項數據上傳至雲端MQTT服務器,並通過微信小程序實現遠程訪問和展示。用戶可以通過微信小程序查看實時監測數據,以及對農田設備進行遠程手動控制,如啓動或關閉5V抽水泵進行灌溉,開啓或關閉白色LED補光燈調節光照條件。
(4)數據上雲與分析:基於EMQX開源MQTT服務器框架搭建的MQTT服務器,能夠接收並處理STM32主控板傳輸的農業環境數據,並對接微信小程序平臺,爲用戶提供直觀易懂的數據圖表和分析結果,便於農戶或農業技術人員進行科學決策和精準管理。
【3】項目硬件模塊組成
(1)主控模塊: 採用STM32F103RCT6微控制器作爲核心控制單元,負責整個系統的運行和管理。STM32F103RCT6具有豐富的外設接口、強大的處理能力和低功耗特性,能夠實時處理傳感器數據、執行邏輯判斷,並通過無線通信模塊發送和接收指令。
(2)環境監測模塊:
-
溫溼度監測:使用DHT11溫溼度傳感器採集農田環境的溫度和溼度信息。
-
光照強度監測:採用BH1750光照強度傳感器測量農田的光照強度。
-
土壤溼度檢測:使用土壤溼度檢測傳感器獲取作物生長區域的土壤含水量數據。
(3)控制輸出模塊:
-
補光燈控制:配置白色LED燈作爲補光光源,根據光照強度監測結果,通過STM32主控板進行智能調節或遠程手動控制。
-
灌溉系統控制:採用5V抽水泵配合繼電器實現灌溉功能,當土壤溼度低於預設閾值時,STM32主控板將控制繼電器閉合,啓動抽水泵進行灌溉;反之則停止灌溉。
(4)無線通信模塊: 集成NBIoT-BC26模塊,實現與雲端服務器的數據交互。該模塊具備廣覆蓋、低功耗、大連接的特點,可確保在各種複雜農業環境中穩定地傳輸數據至MQTT服務器。
(5)報警模塊: 系統配備蜂鳴器用於異常情況報警,當環境參數超出設定範圍時,主控板會驅動蜂鳴器發出聲音警報。
1.2 設計思路
(1)系統需求分析:根據智慧農業管理的實際需求,確定需要監測的關鍵環境參數(溫度、溼度、光照強度和土壤溼度),以及必要的控制功能(灌溉、補光燈控制等)。同時考慮遠程監控與預警的需求,規劃通過NBIoT通信技術實現數據上傳及遠程操控。
(2)硬件選型與設計:
-
主控芯片選擇STM32F103RCT6,因其具有豐富的外設接口、強大的處理能力和低功耗特性,能夠滿足系統實時數據採集與控制的要求。
-
選用DHT11作爲溫溼度傳感器,BH1750作爲光照強度傳感器,以及土壤溼度檢測傳感器,分別獲取農田環境的基本信息。
-
設計灌溉系統,使用5V抽水泵配合繼電器控制灌溉,以響應土壤溼度的監測結果。
-
採用白色LED燈作爲補光光源,並接入主控板進行智能調節或遠程控制。
-
配備蜂鳴器用於異常情況報警。
-
選用NBIoT-BC26模塊確保無線通信穩定可靠,實現數據上雲。
(3)軟件架構設計:
-
開發STM32的嵌入式軟件程序,負責讀取各傳感器數據,執行邏輯判斷,如環境參數超限時觸發報警、根據土壤溼度自動或手動控制灌溉、週期性提醒施肥等操作。
-
實現NBIoT通信協議棧,將現場採集的數據通過BC26模塊發送至雲端MQTT服務器。
-
在雲端部署EMQX開源MQTT服務器框架,接收並存儲前端設備發送的數據。
-
開發微信小程序客戶端,對接MQTT服務器,展示農田環境的各項實時監測數據,提供遠程手動控制界面。
1.3 傳感器功能介紹
(1)DHT11溫溼度傳感器:
-
功能:用於實時監測農田環境中的溫度和相對溼度。
-
特點:DHT11是一種低成本、低功耗的數字式溫溼度複合傳感器,提供了一體化的解決方案。它能夠直接輸出經過校準的數字信號,便於微處理器直接讀取,無需複雜的信號處理電路。
(2)BH1750光照強度傳感器:
-
功能:測量農田或溫室內的光照強度(照度),以判斷當前光照條件是否滿足作物生長需求。
-
特點:BH1750是一款I²C接口的數字光照強度傳感器,具有高精度和寬量程的特點,可精確檢測光照強度,並支持多種分辨率模式切換以適應不同的應用場景。
(3)土壤溼度檢測傳感器:
-
功能:用於監測種植區域土壤的水分含量,作爲決定灌溉與否的重要依據。
-
特點:這類傳感器通常採用電容式、電阻式或者頻域反射(FDR)等原理來檢測土壤溼度,通過轉換爲電信號變化,從而實現對土壤含水量的非破壞性測定。其特點是能反映土壤實際溼潤狀況,幫助實現精準灌溉。
(4)蜂鳴器報警模塊:
-
功能:雖然不是傳統意義上的傳感器,但在本系統中作爲報警裝置使用,當環境參數超出預設閾值時,由主控芯片STM32控制蜂鳴器發出聲音警報,提醒管理人員及時處理異常情況。
(5)5V抽水泵與繼電器組合:
-
功能:抽水泵與繼電器配合實現灌溉功能,繼電器根據土壤溼度傳感器的數據反饋控制抽水泵的開關狀態,達到智能灌溉的目的。
-
特點:繼電器作爲電子開關,可以遠程控制大電流設備如抽水泵的通斷,實現小電流控制大電流,同時隔離了主控制器與負載之間的電氣連接,提高了系統的安全性。
(6)NBIoT-BC26模塊:
-
功能:作爲物聯網通信組件,負責將採集到的各種數據無線傳輸至雲端服務器,同時也接收來自雲端的控制指令,實現遠程數據交互和控制。
-
特點:NBIoT(窄帶物聯網)技術具有低功耗、廣覆蓋、大連接的優點,特別適合於智慧農業這種需要大面積部署且網絡連接要求穩定的場景。BC26模塊是基於NBIoT標準的通信模塊,具備良好的網絡兼容性和穩定性。
1.4 開發工具的選擇
STM32的編程語言選擇C語言,C語言執行效率高,大學裏主學的C語言,C語言編譯出來的可執行文件最接近於機器碼,彙編語言執行效率最高,但是彙編的移植性比較差,目前在一些操作系統內核裏還有一些低配的單片機使用的較多,平常的單片機編程還是以C語言爲主。C語言的執行效率僅次於彙編,語法理解簡單、代碼通用性強,也支持跨平臺,在嵌入式底層、單片機編程裏用的非常多,當前的設計就是採用C語言開發。
開發工具選擇Keil,keil是一家世界領先的嵌入式微控制器軟件開發商,在2015年,keil被ARM公司收購。因爲當前芯片選擇的是STM32F103系列,STMF103是屬於ARM公司的芯片構架、Cortex-M3內核系列的芯片,所以使用Kile來開發STM32是有先天優勢的,而keil在各大高校使用的也非常多,很多教科書裏都是以keil來教學,開發51單片機、STM32單片機等等。目前作爲MCU芯片開發的軟件也不只是keil一家獨大,IAR在MCU微處理器開發領域裏也使用的非常多,IAR擴展性更強,也支持STM32開發,也支持其他芯片,比如:CC2530,51單片機的開發。從軟件的使用上來講,IAR比keil更加簡潔,功能相對少一些。如果之前使用過keil,而且使用頻率較多,已經習慣再使用IAR是有點不適應界面的。
二、EMQX開源MQTT服務器框架
EMQX是一款開源的、雲原生的分佈式物聯網MQTT消息服務器,設計目標是實現高可靠性,並支持承載海量物聯網終端的MQTT連接,以及在海量物聯網設備間實現低延時消息路由。基於Erlang/OTP平臺開發,充分利用了Erlang/OTP的軟實時、低延時和分佈式特性。
以下是EMQX服務器框架的詳細介紹:
(1)可擴展性:EMQX支持億級的MQTT服務訂閱,單節點能夠支持500萬MQTT設備連接,集羣可擴展至1億併發MQTT連接。這種強大的擴展能力使其能夠適應不同規模的物聯網應用。
(2)安全性:EMQX提供了多種安全機制,包括SSL/TLS、密碼認證、增強認證和ACL(訪問控制列表)等,以保障數據傳輸和訪問的安全性。
(3)規則引擎:EMQX內置了基於SQL的規則引擎,能夠實時過濾、轉換和處理消息,提供靈活的消息處理機制。這使得應用程序能夠根據業務需求對消息進行靈活處理。
(4)數據存儲:EMQX企業版還提供了數據存儲功能,將客戶端上下線狀態、訂閱關係、離線消息、消息內容以及消息回執等操作記錄到各種數據庫中。這一功能在服務崩潰或客戶端異常離線後,能夠保留數據,確保數據的完整性和可靠性。
(5)集羣設計:EMQX採用Masterless的大規模分佈式集羣架構,實現了系統的高可用性和水平擴展。集羣設計包括維護訂閱表、路由表和主題樹等數據結構,以實現消息轉發和投遞給各節點上的訂閱者。
(6)協議支持:EMQX完全支持MQTT 5.0和3.x協議標準,提供了更好的伸縮性、安全性和可靠性。同時,它還提供了對多種其他協議的支持,如WebSocket、TCP、SSL/TLS等。
(7)易用性:EMQX提供了豐富的API和插件管理功能,使得用戶可以方便地查看在線客戶端信息、踢出客戶端、管理插件狀態等。它還提供了可視化的管理界面和調試工具,方便用戶進行監控和管理。
三、購買ECS雲服務器
3.1 登錄官網
3.2 購買ECS服務器
【1】選擇ECS彈性服務器
【2】選擇ECS服務器的區域、配置信息、操作系統(我選擇的Ubuntu18.04 64位)。
【3】購買彈性公網IP,配置帶寬。
【4】配置密碼
【5】選擇購買時長,我這裏選擇了1個月時長
【6】確認付費付款
收到郵件提醒,服務器創建成功。 (爲了寫教程,花費320元,買了一個月服務器)
【7】返回彈性服務器的控制檯
【8】點擊服務器名字,可以進入到詳情頁面。
3.3 配置安全組
要確保MQTT服務器常用的幾個端口已經開放出出來。
3.4 安裝FinalShell
Windows下安裝 FinalShell 終端,方便使用SSH協議遠程登錄到雲服務器。 (當然,使用其他方式登錄也是一樣的)
3.5 遠程登錄到雲服務器終端
【1】新建連接,選擇SSH連接。
【2】填入IP地址、用戶名、密碼
這裏的主機就是填服務器的公網IP地址,密碼就是創建服務器輸入的密碼,用戶名直接用root。
【3】點擊連接服務器
【4】第一次登錄會彈出提示框,選擇接受並保存
【5】接下來可以看到服務器已經登錄成功了。
四、Linux下安裝EMQX
本章節將介紹如何在 Ubuntu 系統中下載安裝並啓動 EMQX。
支持的 Ubuntu 版本:
-
Ubuntu 22.04
-
Ubuntu 20.04
-
Ubuntu 18.04
4.1 官網地址
鏈接:https://www.emqx.io/docs/zh/v5.2/deploy/install-ubuntu.html
4.2 通過Apt源安裝
EMQX 支持通過 Apt 源安裝,免除了用戶需要手動處理依賴關係和更新軟件包等的困擾,具有更加方便、安全和易用等優點。
在命令行終端,複製下面的命令過去,按下回車鍵。
【1】通過以下命令配置 EMQX Apt 源:
curl -s https://assets.emqx.com/scripts/install-emqx-deb.sh | sudo bash
【2】運行以下命令安裝 EMQX:
sudo apt-get install emqx
【3】運行以下命令啓動 EMQX:
sudo systemctl start emqx
過程如下:
4.3 EMQX常用的命令
sudo systemctl emqx start 啓動 sudo systemctl emqx stop 停止 sudo systemctl emqx restart 重啓
五、配置EMQX服務器
5.1 登錄EMQX內置管理控制檯
EMQX 提供了一個內置的管理控制檯,即 EMQX Dashboard。方便用戶通過 Web 頁面就能輕鬆管理和監控 EMQX 集羣,並配置和使用所需的各項功能。
在瀏覽器裏輸入: http://122.112.225.194:18083
就可以訪問EMQX的後臺管理頁面。可以管理以連接的客戶端或檢查運行狀態。
這裏面的IP地址,就是自己ECS雲服務器的公網IP地址。
打開瀏覽器後,輸入地址後打開的效果:
默認用戶名和密碼:
用戶名:admin 密碼:public
第一次登錄會提示你修改新密碼,如果不想設置,也可以選擇跳過(公網服務器部署,還是要修改密碼安全些)。
下面修改新密碼:
登錄成功的頁面顯示如下:
5.2 MQTT配置
這裏可以配置MQTT的一些參數,根據自己的需求進行配置。
5.3 測試MQTT通信
新建一個客戶端,點擊連接。
連接之後,然後點擊訂閱,和發佈,如果下面消息能正常的接收。說明MQTT服務器通信是已經正常,沒問題了。
並且在這個頁面也可以看到主題發佈
和主題訂閱
的格式。
5.4 MQTT客戶端登錄服務器測試
接下來就打開我們自己的MQTT客戶端登錄MQTT服務器進行測試數據的通信。
端口選擇: 1883
根據軟件參數填入參數,登錄,進行主題的發佈和訂閱。
說明: 目前還沒有配置客戶端認證,現在只要IP和端口輸入正確,MQTT三元組可以隨便輸入,都可以登錄上服務器的,服務器沒有對三元組做校驗。
EMQ X 默認配置中啓用了匿名認證,任何客戶端都能接入 EMQX。沒有啓用認證插件或認證插件沒有顯式允許/拒絕(ignore)連接請求時,EMQX 將根據匿名認證啓用情況決定是否允許客戶端連接。
然後打開EMQX
的管理後臺,可以看到我們的設備已經登錄服務器了,名字爲test1
。
在訂閱主題的頁面也可以看到我們客戶端設備訂閱的主題。
5.5 客戶端認證配置
EMQX 默認配置中啓用了匿名認證,任何客戶端都能接入 EMQX。沒有啓用認證插件或認證插件沒有顯式允許/拒絕(ignore)連接請求時,EMQX 將根據匿名認證啓用情況決定是否允許客戶端連接。
在正式產品裏肯定是要啓用認證的,不然任何設備都能接入。
下面就介紹如何配置 客戶端認證。
【1】打開客戶端認證頁面
【2】選擇密碼認證
【3】選擇內置數據庫
【4】設置認證方式(都可以默認,不用改),直接點擊創建。
【5】創建成功後,點擊用戶管理
【6】添加用戶
【7】添加成功
【8】添加完畢之後,打開MQTT客戶端可以進行測試。
登錄的時候,MQTT用戶名和密碼必須輸入正確,按照上一步添加的信息進行如實填寫,否則是無法登錄服務器的。
5.6 客戶端授權配置
客戶端授權頁面可以配置每個客戶端(設備)的主題發佈,訂閱權限。限制它是否可以發佈主題,訂閱主題。 如果有需要就可以進行配置。
http://127.0.0.1:18083/#/authorization/detail/built_in_database?tab=users
【1】創建數據源
【2】選擇內置數據庫
【3】完成創建
【4】點擊權限管理
【5】選擇客戶端ID,點擊添加
【6】配置權限
5.7 數據轉發(集成)
在集成選項裏,可以對設備數據處理。 比如:轉發到自己的HTTP服務器,轉發到自己其他的MQTT服務器,創建規則,某些事件觸發某些動作等等。
選擇數據橋接。
可以把數據發送端自己的HTTP服務器,或者發送到其他的MQTT服務器。
選擇HTTP服務 (如果自己有HTTP服務器,可以將數據轉發給自己的HTTP服務器)。
七、MQTT客戶端消息互發測試
7.1 添加2個設備
爲了方便測試設備間互相訂閱主題,數據收發,在客戶端認證頁面至少添加2個設備。我這裏分別添加了test1
和test2
。
7.2 設備間測試
設備A訂閱設備B的主題,設備B訂閱設備A的主題,實現數據互發。
設備A的MQTT信息:
MQTT服務器地址:122.112.225.194 MQTT服務器端口號:1883 MQTT客戶端ID:AAA MQTT用戶名:test1 MQTT登錄密碼:12345678 訂閱主題:BBB/# 發佈主題:AAA/1 發佈的消息:{ "msg": "我是AAA設備" }
設備B的MQTT信息:
MQTT服務器地址:122.112.225.194 MQTT服務器端口號:1883 MQTT客戶端ID:BBB MQTT用戶名:test2 MQTT登錄密碼:12345678 訂閱主題:AAA/# 發佈主題:BBB/1 發佈的消息:{ "msg": "我是BBB設備" }
八、STM32硬件端開發
8.1 BC26模塊的AT指令調試過程
BC20/BC26 開啓GPS、連接MQTT服務器的AT指令發送流程。
(1)查詢模塊是否正常
AT OK
(2)獲取卡號,查詢卡是否插好
AT+CIMI 460041052911195 OK
(3)激活網絡
AT+CGATT=1 OK
(4)獲取網絡激活狀態
AT+CGATT? +CGATT: 1 OK
(5)查詢網絡質量
AT+CSQ +CSQ: 26,0 OK
(6)檢查網絡狀態
AT+CEREG=? //檢查網絡狀態 +CEREG: 0,1 //找網成功 OK
(7)激活GPS
激活GPS,要等一段時間 AT+QGNSSC=1 OK
(8)查詢GPS激活狀態
查詢激活狀態,1表示成功激活 AT+QGNSSC? +QGNSSC: 1 OK
(9)獲取一次GPS定位語句
AT+QGNSSRD="NMEA/RMC" +QGNSSRD: $GNRMC,120715.00,A,3150.78179,N,11711.93433,E,0.000,,310818,,,A,V*19 OK
(10)連接MQTT服務器
AT+QMTOPEN=0,"a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com",1883 OK +QMTOPEN: 0,0
(11)登錄MQTT服務器
命令格式: AT+QMTCONN=<tcpconnectID>,<clientID>,<username>,<password> AT+QMTCONN=0,"6210e8acde9933029be8facf_dev1_0_0_2022021913","6210e8acde9933029be8facf_dev1","6cea55404b463e666cd7a6060daba745bbaa17fe7078dfef45f8151cdf19673d" OK +QMTCONN: 0,0,0
(12)訂閱主題
命令格式: AT+QMTSUB=<tcpconnectID>,<msgID>,"<topic1>”,<qos1>[,"<topic2>”,<qos2>…] AT+QMTSUB=0,1,"$oc/devices/6210e8acde9933029be8facf_dev1/sys/messages/down",2 OK +QMTSUB: 0,1,0,2
(13)發佈主題
命令格式:AT+QMTPUB=<tcpconnectID>,<msgID>,<qos>,<retain>,"<topic>","<msg>" 先發送指令: AT+QMTPUB=0,0,0,0,"$oc/devices/6210e8acde9933029be8facf_dev1/sys/properties/repor" 等待返回 ">" 接着發送數據.不需要加回車。 "{"services": [{"service_id": "gps","properties":{"longitude":12.345,"latitude":33.345}}]}" 數據發送完畢,再發送結束符。 十六進制的值--0x1a 。某些串口調試助手可以適應ctrl+z 快捷鍵輸入0xA 等待模塊返回"OK",到此數據發送完成。 OK +QMTPUB: 0,0,0
8.2 BH1750傳感器
下面貼出的是 通過 I2C 接口與 BH1750 光照傳感器通信,讀取光敏值並通過串口打印的代碼:
#include "stm32f1xx_hal.h" #include "stdio.h" I2C_HandleTypeDef hi2c1; UART_HandleTypeDef huart1; #define BH1750_ADDRESS (0x23 << 1) // BH1750地址 void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_USART1_UART_Init(void); static void MX_I2C1_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); MX_I2C1_Init(); uint8_t data[2]; uint16_t lux; while (1) { HAL_I2C_Master_Transmit(&hi2c1, BH1750_ADDRESS, (uint8_t[]){0x10}, 1, HAL_MAX_DELAY); // 設置單次高分辨率模式 HAL_Delay(180); // 等待傳感器測量完成 HAL_I2C_Master_Receive(&hi2c1, BH1750_ADDRESS, data, 2, HAL_MAX_DELAY); // 讀取光照值 lux = (data[0] << 8) | data[1]; char buffer[20]; sprintf(buffer, "Lux: %d\r\n", lux); HAL_UART_Transmit(&huart1, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY); // 通過串口打印光照值 HAL_Delay(1000); // 延時1秒 } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } static void MX_I2C1_Init(void) { hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 400000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } } static void MX_USART1_UART_Init(void) { huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } } void Error_Handler(void) { while (1) { } } #ifdef USE_FULL_ASSERT void assert_failed(uint8_t *file, uint32_t line) { } #endif
8.3 DHT11溫溼度模塊
下面貼出的是 DHT11 溫溼度傳感器讀取環境溫溼度數據並通過串口打印的代碼:
#include "stm32f1xx_hal.h" #include "stdio.h" TIM_HandleTypeDef htim2; UART_HandleTypeDef huart1; #define DHT11_GPIO_PORT GPIOA #define DHT11_GPIO_PIN GPIO_PIN_0 void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_USART1_UART_Init(void); static void MX_TIM2_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); MX_TIM2_Init(); HAL_TIM_Base_Start(&htim2); while (1) { HAL_TIM_Base_Start(&htim2); HAL_Delay(2000); // 等待2秒 // 發送開始信號 HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_RESET); HAL_Delay(18); HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_SET); HAL_Delay(20); // 設置引腳爲輸入 GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = DHT11_GPIO_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct); // 等待 DHT11 響應 while (!HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN)) ; while (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN)) ; // 讀取數據 uint8_t data[5] = {0}; for (int i = 0; i < 5; i++) { for (int j = 0; j < 8; j++) { while (!HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN)) ; HAL_TIM_Base_Start(&htim2); while (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN)) ; if (HAL_TIM_Base_GetCounter(&htim2) > 40) data[i] |= (1 << (7 - j)); } } // 計算溫度和溼度 uint8_t humidity = data[0]; uint8_t temperature = data[2]; char buffer[50]; sprintf(buffer, "Temperature: %d°C, Humidity: %d%%\r\n", temperature, humidity); HAL_UART_Transmit(&huart1, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY); // 通過串口打印溫溼度數據 } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } static void MX_TIM2_Init(void) { htim2.Instance = TIM2; htim2.Init.Prescaler = 72 - 1; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 65535; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(&htim2); } static void MX_USART1_UART_Init(void) { huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } } void Error_Handler(void) { while (1) { } } #ifdef USE_FULL_ASSERT void assert_failed(uint8_t *file, uint32_t line) { } #endif
8.4 土壤溼度傳感器
通過 ADC 模塊讀取土壤溼度並通過串口打印的代碼:
#include "stm32f1xx_hal.h" #include "stdio.h" ADC_HandleTypeDef hadc1; UART_HandleTypeDef huart1; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_USART1_UART_Init(void); static void MX_ADC1_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); MX_ADC1_Init(); uint16_t adc_value; while (1) { HAL_ADC_Start(&hadc1); // 啓動 ADC 轉換 if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK) { adc_value = HAL_ADC_GetValue(&hadc1); // 讀取 ADC 值 char buffer[50]; sprintf(buffer, "Soil Moisture: %d\r\n", adc_value); HAL_UART_Transmit(&huart1, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY); // 通過串口打印土壤溼度數據 } HAL_Delay(1000); // 延時1秒 } } void SystemClock_Config(void) { // 略,根據實際情況配置系統時鐘 } static void MX_ADC1_Init(void) { ADC_ChannelConfTypeDef sConfig = {0}; hadc1.Instance = ADC1; hadc1.Init.ScanConvMode = DISABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 1; if (HAL_ADC_Init(&hadc1) != HAL_OK) { Error_Handler(); } sConfig.Channel = ADC_CHANNEL_0; // 修改爲實際連接的通道 sConfig.Rank = 1; sConfig.SamplingTime = ADC_SAMPLETIME_13CYCLES_5; // 根據實際情況調整採樣時間 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } } static void MX_USART1_UART_Init(void) { huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } } void Error_Handler(void) { while (1) { } } #ifdef USE_FULL_ASSERT void assert_failed(uint8_t *file, uint32_t line) { } #endif
九、總結
本智慧農業管理系統設計與實現項目基於STM32F103RCT6微控制器爲核心,通過集成DHT11溫溼度傳感器、BH1750光照強度傳感器和土壤溼度檢測傳感器等設備,構建了一套全面的農田環境監測系統。當環境參數超出預設閾值時,系統能夠實時報警並自動或提醒進行灌溉、施肥等操作,同時利用蜂鳴器發出聲音警報。
在遠程控制方面,系統採用NBIoT-BC26模塊實現了無線通信功能,將採集到的數據傳輸至雲端MQTT服務器,並通過EMQX開源框架搭建的服務器處理數據。用戶可通過微信小程序隨時隨地查看農田環境的各項實時數據,實現對農作物生長環境的遠程監控,並能便捷地執行手動灌溉、開啓補光燈等遠程控制操作。
本項目的成功實施,不僅有效提升了農業生產過程中的智能化水平,降低了人工管理成本,而且爲實現精準農業和智慧農業提供了有力的技術支持。未來,隨着物聯網技術、雲計算和人工智能技術的進一步發展,這套智慧農業管理系統將有望在更多領域推廣使用,助力我國現代農業朝着更高效、智能、可持續的方向邁進。