本文分享自華為雲社群《基於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 登入官網
https://www.huaweicloud.com/
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 源安裝,免除了使用者需要手動處理依賴關係和更新軟體包等的困擾,具有更加方便、安全和易用等優點。
在命令列終端,複製下面的命令過去,按下Enter鍵。
【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開源框架搭建的伺服器處理資料。使用者可透過微信小程式隨時隨地檢視農田環境的各項實時資料,實現對農作物生長環境的遠端監控,並能便捷地執行手動灌溉、開啟補光燈等遠端控制操作。
本專案的成功實施,不僅有效提升了農業生產過程中的智慧化水平,降低了人工管理成本,而且為實現精準農業和智慧農業提供了有力的技術支援。未來,隨著物聯網技術、雲端計算和人工智慧技術的進一步發展,這套智慧農業管理系統將有望在更多領域推廣使用,助力我國現代農業朝著更高效、智慧、可持續的方向邁進。
點選關注,第一時間瞭解華為雲新鮮技術~