軟硬件開源項(xiàng)目-智能農(nóng)業(yè)監(jiān)控系統(tǒng):MQTT阿里云平臺(tái)監(jiān)測(cè)+內(nèi)置Web網(wǎng)頁(yè)控制+代碼解析
智能農(nóng)業(yè)監(jiān)控系統(tǒng):開源項(xiàng)目推薦
【下載地址】智能農(nóng)業(yè)監(jiān)控系統(tǒng)源碼 本倉(cāng)庫(kù)提供了一套完整的智能農(nóng)業(yè)監(jiān)控系統(tǒng)源碼,基于 W55MH32 以太網(wǎng)單片機(jī)實(shí)現(xiàn)三大核心功能:通過(guò) ADC 采集土壤濕度與光照數(shù)據(jù),基于閾值自動(dòng)控制水泵灌溉;經(jīng) MQTT 協(xié)議對(duì)接阿里云,實(shí)現(xiàn)數(shù)據(jù)遠(yuǎn)程查看與設(shè)備控制;提供本地 Web 接口,支持實(shí)時(shí)監(jiān)測(cè)與閾值調(diào)整。 項(xiàng)目倉(cāng)庫(kù)地址:https://gitee.com/shenzhen-weishi_3_0/W55MH32
完整顯示視頻bilibili|點(diǎn)擊跳轉(zhuǎn)
1 項(xiàng)目構(gòu)思與核心目標(biāo)
老家親戚種大棚,灌溉總讓人頭疼:每天來(lái)回查濕度,憑經(jīng)驗(yàn)澆水,忙時(shí)作物缺水蔫苗,雨天積水爛根,既費(fèi)人力又浪費(fèi)水,環(huán)境調(diào)節(jié)總跟不上作物需求。我便琢磨著做套簡(jiǎn)易系統(tǒng)解決這些問(wèn)題。?
之前用過(guò) WIZnet 的 W5500 芯片做以太網(wǎng)項(xiàng)目,對(duì)他們的芯片挺熟悉。聽(tīng)說(shuō)新出了帶 MCU 的 W55MH32 以太網(wǎng)芯片,就申請(qǐng)了開發(fā)板試試。拿到板子后,先連接傳感器在自家盆栽做測(cè)試,確認(rèn)硬件兼容和數(shù)據(jù)穩(wěn)定后,正式搭建智能農(nóng)業(yè)監(jiān)管系統(tǒng)。這板子自帶硬件 TCP/IP 引擎,外設(shè)接口豐富,剛好滿足傳感器連接和數(shù)據(jù)傳輸需求,官方還有阿里云連接例程,省了不少事。有之前的經(jīng)驗(yàn)打底,用它連阿里云、搭局域網(wǎng)監(jiān)控網(wǎng)頁(yè),心里挺有底。?我計(jì)劃搭傳感器與水泵聯(lián)動(dòng)模型:傳感器采集土壤濕度、光照等數(shù)據(jù),傳云端后自動(dòng)控制水泵啟停。說(shuō)干就干,畫接線圖、連傳感器和繼電器,優(yōu)化 MQTT 邏輯接阿里云,還做了簡(jiǎn)易網(wǎng)頁(yè)方便遠(yuǎn)程查看操作。調(diào)試后,數(shù)據(jù)采集和水泵自動(dòng)啟停功能都穩(wěn)定實(shí)現(xiàn)了。?這個(gè)模型還在完善中,后續(xù)會(huì)優(yōu)化硬件集成和代碼邏輯,讓運(yùn)行更穩(wěn)定耐用?,F(xiàn)在整理開發(fā)細(xì)節(jié)記錄下來(lái),之后會(huì)開源代碼和 PCB 文件,希望給想在田間用物聯(lián)網(wǎng)技術(shù)的朋友做個(gè)參考,提供思路,讓技術(shù)真正為農(nóng)活添力。?
核心目標(biāo):
硬件層面:實(shí)現(xiàn)傳感器數(shù)據(jù)采集與執(zhí)行器(水泵)控制的穩(wěn)定聯(lián)動(dòng),確保環(huán)境數(shù)據(jù)實(shí)時(shí)性與設(shè)備響應(yīng)可靠性。
軟件層面:完成 MQTT 協(xié)議對(duì)接阿里云,實(shí)現(xiàn)設(shè)備與云端的雙向數(shù)據(jù)傳輸,同時(shí)支持本地網(wǎng)頁(yè)控制和環(huán)境數(shù)據(jù)查看。
應(yīng)用層面:默認(rèn)土壤濕度低于 30% 自動(dòng)啟動(dòng)灌溉,高于 50% 自動(dòng)停止,支持遠(yuǎn)程動(dòng)態(tài)調(diào)整閾值,兼顧自動(dòng)化與靈活性。
后續(xù)計(jì)劃進(jìn)一步將W55MH32以太網(wǎng)單片機(jī)芯片與光照傳感器、土壤濕度傳感器深度集成,同時(shí)匹配適配田間環(huán)境的防護(hù)外殼——既通過(guò)硬件整合提升系統(tǒng)穩(wěn)定性,又借助外殼抵御大棚內(nèi)的溫濕度波動(dòng)、粉塵等干擾,讓設(shè)備在實(shí)際農(nóng)業(yè)場(chǎng)景中更耐用、易部署。此外,還會(huì)公開完整的硬件原理圖和PCB設(shè)計(jì)文件,方便有需要的朋友參考復(fù)用,降低技術(shù)落地的門檻,讓這套方案能更便捷地應(yīng)用到田間地頭。
1.1 方案圖示

2 硬件選型與搭建
2.1 核心組件清單
主控:W55MH32L-EVB(216MHz主頻、自帶硬件TCP/IP引擎)。
傳感器:土壤濕度傳感器(模擬輸出)、光照傳感器(模擬輸出)。
執(zhí)行器:5V繼電器模塊、小型水泵。
輔助設(shè)備:外部 5V 電源、網(wǎng)線、路由器、杜邦線若干。
2.2 電路連接技巧
開發(fā)板引腳定義復(fù)雜,我采用"功能分組"法簡(jiǎn)化連接:
模擬量輸入組:PA0接土壤濕度傳感器,PA3接光照傳感器(利用單片機(jī)ADC功能)。
數(shù)字輸出組:PB10 接繼電器IN引腳(控制信號(hào))。
電源組:開發(fā)板5V輸出給繼電器供電,傳感器獨(dú)立接3.3V(避免干擾),繼電器COM端接外接電源正極。
水泵:正極接繼電器常開端,負(fù)極接外接電源負(fù)極。
特別注意繼電器的"低電平有效"特性——初始化時(shí)需將PB10置高,通過(guò)拉低電平觸發(fā)動(dòng)作,這一點(diǎn)在后續(xù)軟件設(shè)計(jì)中需重點(diǎn)匹配。
3 開發(fā)環(huán)境搭建
3.1 軟件工具鏈
編譯環(huán)境:Keil uVision5,版本大于V5.3(需安裝W55MH32 系列芯片包)。
調(diào)試工具:WIZ UartTool串口助手,其他串口助手也可。
瀏覽器:用于打開網(wǎng)頁(yè)查看。
云平臺(tái):阿里云物聯(lián)網(wǎng)平臺(tái)(需完成實(shí)名認(rèn)證)。
源碼:gitee倉(cāng)庫(kù)項(xiàng)目倉(cāng)庫(kù)地址|點(diǎn)擊跳轉(zhuǎn)
4 連接阿里云物聯(lián)網(wǎng)平臺(tái)
4.1 MQTT連接阿里云收發(fā)數(shù)據(jù)流程
4.1.1 準(zhǔn)備階段
注冊(cè)與實(shí)名認(rèn)證:用戶需要在阿里云平臺(tái)注冊(cè)賬號(hào),并完成實(shí)名認(rèn)證。
創(chuàng)建產(chǎn)品和添加物模型:登錄阿里云物聯(lián)網(wǎng)平臺(tái),創(chuàng)建產(chǎn)品并在產(chǎn)品下添加以下物模型功能。

創(chuàng)建設(shè)備:在剛剛創(chuàng)建的產(chǎn)品下創(chuàng)建一個(gè)設(shè)備。

4.1.2 記錄參數(shù)
連接參數(shù):在剛剛創(chuàng)建的設(shè)備詳情頁(yè)中找到MQTT連接參數(shù)。

訂閱主題:/sys/k1zh33h3hte/${deviceName}/thing/service/property/set(屬性設(shè)置主題)
發(fā)布主題:/sys/k1zh33h3hte/${deviceName}/thing/event/property/post(上報(bào)消息主題)
注意:上面兩個(gè)主題中的${deviceName}需要替換成設(shè)備名。

4.1.3 連接、訂閱和發(fā)布消息
接著我們可以使用上面記錄的連接參數(shù)進(jìn)行連接,當(dāng)連接成功后,訂閱上面的訂閱主題。并通過(guò)發(fā)布主題上報(bào)物模型數(shù)據(jù)。
在阿里云平臺(tái),如果產(chǎn)品創(chuàng)建階段選擇的數(shù)據(jù)格式為Alink JSON格式時(shí),接收和發(fā)送數(shù)據(jù)格式都會(huì)遵守下面這個(gè)格式:
{
"method": "thing.event.property.post",
"id": "2241348",
"params": {
"prop_float": 1.25,
"prop_int16": 4658,
"prop_bool": 1
},
"version": "1.0"
}
5 主要程序解析
5.1 main.c分析
1.系統(tǒng)初始化與硬件配置
完成基礎(chǔ)硬件初始化(時(shí)鐘、延時(shí)、串口、定時(shí)器等),配置ADC(模數(shù)轉(zhuǎn)換)用于傳感器數(shù)據(jù)采集,初始化繼電器控制模塊,并設(shè)置WIZnet以太網(wǎng)芯片的網(wǎng)絡(luò)參數(shù)(MAC、IP、網(wǎng)關(guān)等)。
2.網(wǎng)絡(luò)通信功能 實(shí)現(xiàn)雙重網(wǎng)絡(luò)通信能力:
MQTT協(xié)議:通過(guò)MQTT客戶端(do_mqtt()和mqtt_post_properties())實(shí)現(xiàn)傳感器數(shù)據(jù)的遠(yuǎn)程發(fā)布。
獲取網(wǎng)頁(yè):通過(guò)loopback_tcps()提供TCP服務(wù)器功能,進(jìn)行HTTP請(qǐng)求和響應(yīng)處理。
3.傳感器數(shù)據(jù)處理與控制
周期性(5秒間隔)通過(guò)process_sensors_and_control()讀取傳感器數(shù)據(jù)(濕度、光照強(qiáng)度)。
基于濕度閾值(高低閾值)實(shí)現(xiàn)繼電器自動(dòng)控制邏輯。
將實(shí)時(shí)數(shù)據(jù)(濕度、光照、繼電器狀態(tài))通過(guò)MQTT發(fā)布到阿里平臺(tái)同時(shí)刷新網(wǎng)頁(yè)環(huán)境數(shù)據(jù)。
#include "bsp_adc.h" #include "bsp_rcc.h" #include "bsp_tim.h" #include "bsp_uart.h" #include "delay.h" #include "do_mqtt.h" #include "do_sensor.h" #include "loopback.h" #include "sv.h" #include "wiz_interface.h" #include "wizchip_conf.h" #include #include #include /* 全局實(shí)時(shí)傳感器數(shù)據(jù)(在do_mqtt.c中定義) */ extern float g_humidity_value; // 當(dāng)前濕度讀數(shù) extern float g_light_intensity; // 當(dāng)前光照強(qiáng)度讀數(shù) extern uint8_t g_solenoid_valve_state; // 電磁閥狀態(tài)(1:開啟, 0:關(guān)閉) /* 濕度閾值(與阿里云IoT模型對(duì)齊,范圍0~100) */ int g_humidity_low_threshold = 30; // 澆水的下限閾值 int g_humidity_high_threshold = 50; // 停止?jié)菜纳舷揲撝? /* 函數(shù)原型 */ extern void mqtt_post_properties(void); /* 套接字和緩沖區(qū)配置常量 */ #define SOCKET_TCP_ID 0 #define SOCKET_MQTT_ID 1 #define ETHERNET_BUF_MAX_SIZE (1024 * 2) // 以太網(wǎng)緩沖區(qū)最大大小 #define SENSOR_READ_INTERVAL 5000 // 傳感器采樣間隔(毫秒) uint16_t g_tcp_listen_port = 8080; // TCP服務(wù)器監(jiān)聽(tīng)端口 wiz_NetInfo g_default_network_info = { // 默認(rèn)網(wǎng)絡(luò)配置 .mac = {0x00, 0x08, 0xdc, 0x12, 0x22, 0x12}, .ip = {192, 168, 1, 30}, .gw = {192, 168, 1, 1}, .sn = {255, 255, 255, 0}, .dns = {8, 8, 8, 8}, .dhcp = NETINFO_DHCP}; uint8_t g_ethernet_data_buf[ETHERNET_BUF_MAX_SIZE] = { 0}; // 以太網(wǎng)數(shù)據(jù)緩沖區(qū) static uint8_t s_mqtt_send_buf[ETHERNET_BUF_MAX_SIZE] = { 0}; // MQTT發(fā)送緩沖區(qū)(靜態(tài)) static uint8_t s_mqtt_recv_buf[ETHERNET_BUF_MAX_SIZE] = { 0}; // MQTT接收緩沖區(qū)(靜態(tài)) /* 聲明systick_count,名稱與bsp_tim.o中的引用匹配 */ volatile uint32_t systick_count = 0; // 系統(tǒng)滴答計(jì)數(shù)器(毫秒)- bsp_tim使用的名稱 /** * @brief 主程序循環(huán) * @details 處理MQTT通信、TCP回環(huán)和傳感器處理 */ int main(void) { rcc_clk_config(); // RCC時(shí)鐘配置 delay_init(); // 延時(shí)初始化 console_usart_init(115200); // 控制臺(tái)串口初始化,波特率115200 tim3_init(); // TIM3定時(shí)器初始化 printf("%s 傳感器監(jiān)控系統(tǒng)rn", _WIZCHIP_ID_); adc_dma_init(); // ADC DMA初始化 sv_init(); // 電磁閥初始化 wiz_toe_init(); // WIZ芯片TOE初始化 wiz_phy_link_check(); // WIZ物理層連接檢查 network_init(g_ethernet_data_buf, &g_default_network_info); // 網(wǎng)絡(luò)初始化 mqtt_init(SOCKET_MQTT_ID, s_mqtt_send_buf, s_mqtt_recv_buf); // MQTT初始化 printf("系統(tǒng)初始化完成。rn"); while (1) { do_mqtt(); // 處理MQTT通信 // 處理TCP回環(huán) loopback_tcps(SOCKET_TCP_ID, g_ethernet_data_buf, g_tcp_listen_port); // 按間隔讀取傳感器并發(fā)布數(shù)據(jù) if (systick_count >= SENSOR_READ_INTERVAL) { systick_count = 0; process_sensors_and_control(); // 處理傳感器數(shù)據(jù)并控制設(shè)備 mqtt_post_properties(); // 通過(guò)MQTT發(fā)布傳感器數(shù)據(jù) } } }
5.2 ADC采集
1.關(guān)鍵配置:
初始化ADC為連續(xù)掃描模式,同時(shí)啟用DMA傳輸。
配置GPIO為模擬輸入模式,對(duì)應(yīng)傳感器連接的引腳。
設(shè)置DMA以循環(huán)方式將 ADC 數(shù)據(jù)傳輸?shù)骄彌_區(qū),無(wú)需CPU干預(yù)。
2.數(shù)據(jù)處理:
采樣數(shù)據(jù)交替存儲(chǔ)在s_adc_dma_buffer緩沖區(qū)中。
提供adc_get_average_value()函數(shù),可獲取指定通道的平均采樣值,減少噪聲影響。
#include "bsp_adc.h"
#include "w55mh32_adc.h"
#include "w55mh32_dma.h"
#include "w55mh32_gpio.h"
#include "w55mh32_rcc.h"
/* ADC DMA數(shù)據(jù)靜態(tài)緩沖區(qū)(存儲(chǔ)交替的通道讀數(shù)) */
static uint16_t s_adc_dma_buffer[ADC_BUFFER_SIZE];
/**
* @brief 初始化帶DMA功能的ADC
* @details 配置ADC通道(濕度和光照)、GPIO和DMA以實(shí)現(xiàn)連續(xù)采樣
*/
void adc_dma_init(void) {
ADC_InitTypeDef adc_init_struct; // ADC初始化結(jié)構(gòu)體
GPIO_InitTypeDef gpio_init_struct; // GPIO初始化結(jié)構(gòu)體
DMA_InitTypeDef dma_init_struct; // DMA初始化結(jié)構(gòu)體
// 使能ADC、GPIO和DMA的時(shí)鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// 配置GPIO引腳為模擬輸入(PA0: 濕度, PA3: 光照)
gpio_init_struct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_3;
gpio_init_struct.GPIO_Mode = GPIO_Mode_AIN; // 模擬輸入模式
GPIO_Init(GPIOA, &gpio_init_struct);
// 配置ADC為連續(xù)掃描模式
ADC_StructInit(&adc_init_struct); // 初始化ADC結(jié)構(gòu)體為默認(rèn)值
adc_init_struct.ADC_Mode = ADC_Mode_Independent; // 獨(dú)立模式
adc_init_struct.ADC_ScanConvMode = ENABLE; // 使能掃描模式
adc_init_struct.ADC_ContinuousConvMode = ENABLE; // 使能連續(xù)轉(zhuǎn)換模式
adc_init_struct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 無(wú)外部觸發(fā)
adc_init_struct.ADC_DataAlign = ADC_DataAlign_Right; // 數(shù)據(jù)右對(duì)齊
adc_init_struct.ADC_NbrOfChannel = 2; // 2個(gè)通道(濕度+光照)
ADC_Init(ADC1, &adc_init_struct);
// 配置ADC通道和采樣時(shí)間
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1,
ADC_SampleTime_55Cycles5); // 濕度通道(PA0)
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 2,
ADC_SampleTime_55Cycles5); // 光照通道(PA3)
// 配置DMA用于ADC數(shù)據(jù)傳輸
DMA_DeInit(DMA1_Channel1); // 重置DMA通道1配置
dma_init_struct.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // 外設(shè)基地址(ADC數(shù)據(jù)寄存器)
dma_init_struct.DMA_MemoryBaseAddr = (uint32_t)s_adc_dma_buffer; // 內(nèi)存基地址(DMA緩沖區(qū))
dma_init_struct.DMA_DIR = DMA_DIR_PeripheralSRC; // 傳輸方向:外設(shè)到內(nèi)存
dma_init_struct.DMA_BufferSize = ADC_BUFFER_SIZE; // 緩沖區(qū)大小
dma_init_struct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 禁止外設(shè)地址遞增
dma_init_struct.DMA_MemoryInc = DMA_MemoryInc_Enable; // 使能內(nèi)存地址遞增
dma_init_struct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 外設(shè)數(shù)據(jù)大?。喊胱郑?6位)
dma_init_struct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 內(nèi)存數(shù)據(jù)大小:半字(16位)
dma_init_struct.DMA_Mode = DMA_Mode_Circular; // 循環(huán)模式
dma_init_struct.DMA_Priority = DMA_Priority_High; // 高優(yōu)先級(jí)
dma_init_struct.DMA_M2M = DMA_M2M_Disable; // 禁止內(nèi)存到內(nèi)存?zhèn)鬏? DMA_Init(DMA1_Channel1, &dma_init_struct);
// 使能DMA和ADC
DMA_Cmd(DMA1_Channel1, ENABLE); // 使能DMA通道1
ADC_DMACmd(ADC1, ENABLE); // 使能ADC的DMA請(qǐng)求
// 校準(zhǔn)并啟動(dòng)ADC
ADC_Cmd(ADC1, ENABLE); // 使能ADC1
ADC_ResetCalibration(ADC1); // 重置校準(zhǔn)寄存器
while (ADC_GetResetCalibrationStatus(ADC1)) // 等待重置校準(zhǔn)完成
;
ADC_StartCalibration(ADC1); // 開始校準(zhǔn)
while (ADC_GetCalibrationStatus(ADC1)) // 等待校準(zhǔn)完成
;
ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 軟件觸發(fā)ADC轉(zhuǎn)換
}
/**
* @brief 獲取特定通道的ADC平均值
* @param channel_index ADC通道索引(0: 濕度, 1: 光照)
* @param sample_count 用于平均的樣本數(shù)量
* @return 平均ADC值(12位)
*/
uint16_t adc_get_average_value(uint8_t channel_index, uint8_t sample_count) {
uint32_t sum = 0; // 樣本總和
uint8_t valid_samples = 0; // 有效樣本數(shù)
// 從DMA緩沖區(qū)讀取交替的樣本(通道0在偶數(shù)索引,通道1在奇數(shù)索引)
for (uint8_t i = 0; i < sample_count && i < ADC_BUFFER_SIZE / 2; i++) {
sum += s_adc_dma_buffer[i * 2 + channel_index]; // 累加對(duì)應(yīng)通道的樣本
valid_samples++; // 計(jì)數(shù)有效樣本
}
return (valid_samples > 0) ? (sum / valid_samples) : 0; // 返回平均值(避免除零)
}
5.3 傳感器數(shù)據(jù)讀取
1.濕度傳感器讀取(humidity_read):
從ADC獲取濕度通道的平均原始值。
將ADC值轉(zhuǎn)換為電壓(基于3.3V參考電壓和12位ADC)。
通過(guò)傳感器特定公式將電壓轉(zhuǎn)換為濕度值(0-100%)。
對(duì)結(jié)果進(jìn)行范圍限制,確保在有效區(qū)間內(nèi)。
2.光照強(qiáng)度讀?。╨ight_read_intensity):
從 ADC 獲取光照通道的平均原始值。
處理零值情況避免除零錯(cuò)誤。
通過(guò)傳感器公式將ADC值轉(zhuǎn)換為光照強(qiáng)度(自定義單位)。
3.傳感器處理與控制(process_sensors_and_control):
讀取濕度和光照值并更新全局變量。
根據(jù)濕度閾值控制繼電器:低于低閾值打開(澆水),高于高閾值關(guān)閉(停止?jié)菜?/p>
打印當(dāng)前狀態(tài)信息(濕度、光照、閥門狀態(tài)及閾值)。
#include "bsp_adc.h"
#include "do_sensor.h"
#include "sv.h"
#include
extern float g_humidity_value;
extern float g_light_intensity;
extern uint8_t g_solenoid_valve_state;
extern int g_humidity_low_threshold;
extern int g_humidity_high_threshold;
/**
* @brief 從傳感器讀取濕度值
* @details 將ADC原始值轉(zhuǎn)換為相對(duì)濕度(0~100%)
* @return 濕度值(0.0~100.0)
*/
float humidity_read(void) {
// 從濕度通道(索引0)讀取ADC平均值
uint16_t adc_raw_value = adc_get_average_value(0, ADC_BUFFER_SIZE / 2);
// 將ADC值轉(zhuǎn)換為電壓(參考電壓3.3V,12位ADC)
float voltage = adc_raw_value * 3.3f / 4096.0f;
// 將電壓轉(zhuǎn)換為濕度(傳感器特定公式)
float humidity = (3.3f - voltage) * 30.3f;
// 將值限制在有效范圍內(nèi)(0~100%)
if (humidity < 0.0f) {
humidity = 0.0f;
}
if (humidity > 100.0f) {
humidity = 100.0f;
}
return humidity;
}
/**
* @brief 從傳感器讀取光照強(qiáng)度
* @details 將ADC原始值轉(zhuǎn)換為光照強(qiáng)度(任意單位)
* @return 光照強(qiáng)度值
*/
float light_read_intensity(void) {
// 從光照通道(索引1)讀取ADC平均值
uint16_t adc_raw_value = adc_get_average_value(1, ADC_BUFFER_SIZE / 2);
// 避免除零錯(cuò)誤
if (adc_raw_value == 0) {
adc_raw_value = 1;
}
// 將ADC值轉(zhuǎn)換為光照強(qiáng)度(傳感器特定公式)
return 500000.0f / (float)adc_raw_value;
}
/**
* @brief 讀取傳感器并控制電磁閥
* @details 讀取濕度和光照傳感器,基于閾值控制閥門
*/
void process_sensors_and_control(void) {
g_humidity_value = humidity_read();
g_light_intensity = light_read_intensity();
// 基于濕度閾值控制電磁閥
if (g_humidity_value < g_humidity_low_threshold) {
g_solenoid_valve_state = 1;
sv_open(); // 打開閥門(開始澆水)
} else if (g_humidity_value > g_humidity_high_threshold) {
g_solenoid_valve_state = 0;
sv_close(); // 關(guān)閉閥門(停止?jié)菜?
}
// 打印當(dāng)前狀態(tài)
printf("Humidity:%.1f Light:%.0f Valve:%s Low:%d High:%drn",
g_humidity_value, g_light_intensity,
g_solenoid_valve_state ? "ON" : "OFF", g_humidity_low_threshold,
g_humidity_high_threshold);
}
5.4 繼電器控制澆灌
1.硬件定義與初始化:
定義繼電器控制引腳為 GPIOB的Pin10。
sv_init() 函數(shù)負(fù)責(zé)初始化GPIO:
使能GPIOB時(shí)鐘。
配置引腳為開漏輸出模式(GPIO_Mode_Out_OD),速度50MHz。
初始狀態(tài)設(shè)置為高電平(關(guān)閉繼電器,利用開漏模式的內(nèi)部上拉)。
2.核心控制函數(shù):
sv_close():通過(guò)設(shè)置引腳為高電平關(guān)閉繼電器,停止?jié)菜?,并打印狀態(tài)信息。
sv_open():通過(guò)清除引腳為低電平打開繼電器,開始澆水,并打印狀態(tài)信息。
#include "bsp_uart.h"
#include "sv.h"
#include "w55mh32_rcc.h"
#include
/* 電磁閥控制引腳 */
#define SOLENOID_VALVE_PIN GPIO_Pin_10
#define SOLENOID_VALVE_PORT GPIOB
/**
* @brief 初始化電磁閥(繼電器)控制
* @details 配置GPIO引腳為開漏模式用于繼電器控制
*/
void sv_init(void) {
GPIO_InitTypeDef gpio_init_struct;
// 使能GPIOB時(shí)鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 配置引腳為開漏輸出(繼電器控制)
gpio_init_struct.GPIO_Mode = GPIO_Mode_Out_OD;
gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz;
gpio_init_struct.GPIO_Pin = SOLENOID_VALVE_PIN;
GPIO_Init(SOLENOID_VALVE_PORT, &gpio_init_struct);
// 初始狀態(tài):閥門關(guān)閉(引腳高電平,開漏模式配合內(nèi)部上拉)
GPIO_SetBits(SOLENOID_VALVE_PORT, SOLENOID_VALVE_PIN);
}
/**
* @brief 關(guān)閉繼電器(停止?jié)菜? * @details 設(shè)置繼電器控制引腳為高電平,使電磁閥失活
*/
void sv_close(void) {
GPIO_SetBits(SOLENOID_VALVE_PORT, SOLENOID_VALVE_PIN);
printf("Solenoid valve closed (high humidity).rn");
}
/**
* @brief 打開繼電器(開始澆水)
* @details 清除繼電器控制引腳為低電平,使電磁閥激活
*/
void sv_open(void) {
GPIO_ResetBits(SOLENOID_VALVE_PORT, SOLENOID_VALVE_PIN);
printf("Solenoid valve opened (low humidity).rn");
}
5.5 連接阿里云
1.功能定位:通過(guò) MQTT 協(xié)議實(shí)現(xiàn)設(shè)備與阿里云 IoT 平臺(tái)的雙向通信,完成傳感器數(shù)據(jù)上報(bào)與云端控制指令接收。
2.核心配置:包含阿里云MQTT 服務(wù)器地址、端口、認(rèn)證信息(客戶端ID、用戶名、密碼)及收發(fā)主題,采用QoS0等級(jí)通信。
3.關(guān)鍵流程:
初始化:解析域名、建立網(wǎng)絡(luò)連接、配置MQTT客戶端參數(shù)。
通信機(jī)制:通過(guò)狀態(tài)機(jī)管理連接、訂閱、消息收發(fā)及錯(cuò)誤重連。
數(shù)據(jù)交互:將傳感器數(shù)據(jù)(濕度、光照、閥門狀態(tài))打包為JSON上報(bào);解析云端上下發(fā)的JSON指令,控制閥門門開關(guān)及更新濕度閾值。
4.與系統(tǒng)集成:關(guān)聯(lián)傳感器數(shù)據(jù)全局變量,調(diào)用繼電器控制函數(shù),實(shí)現(xiàn)本地設(shè)備狀態(tài)與云端的同步。
需要注意:mqttconn s_mqtt_connection_params函數(shù)中參數(shù)要修改為4.1.2中創(chuàng)建設(shè)備的MQTT參數(shù)和訂閱發(fā)布主題。
#include "MQTTClient.h"
#include "cJSON.h"
#include "delay.h"
#include "do_dns.h"
#include "do_mqtt.h"
#include "mqtt_interface.h"
#include "sv.h"
#include "wiz_interface.h"
#include "wizchip_conf.h"
#include
#define MQTT_ETHERNET_MAX_SIZE (1024 * 2) // MQTT緩沖區(qū)最大大小
/* MQTT控制句柄 */
MQTTClient g_mqtt_client = {0};
Network g_mqtt_network = {0};
int g_mqtt_conn_status;
static uint8_t s_mqtt_run_status = CONN; // MQTT狀態(tài)機(jī)狀態(tài)
/* MQTT連接參數(shù)(替換為你的設(shè)備憑證) */
static mqttconn s_mqtt_connection_params = {
.mqttHostUrl = "iot-06z009vm5y6jfwj.mqtt.iothub.aliyuncs.com",
.server_ip =
{
0,
},
.port = 1883,
.clientid = "k1zh33h3hte.IGAT|securemode=2,signmethod=hmacsha256,timestamp="
"1752635274022|",
.username = "IGAT&k1zh33h3hte",
.passwd =
"f04b7d14d10a981e0eb6248da38b52060ff443c3f4b825d01594dfaa7e5720c1",
.pubtopic = "/sys/k1zh33h3hte/IGAT/thing/event/property/post",
.subtopic = "/sys/k1zh33h3hte/IGAT/thing/service/property/set",
.pubQoS = QOS0,
};
/* MQTT接收緩沖區(qū)(靜態(tài)) */
static char s_mqtt_received_msg[512] = {0};
static uint8_t s_mqtt_receive_flag = 0; // 新MQTT消息標(biāo)志
/* MQTT消息/配置結(jié)構(gòu) */
MQTTMessage g_mqtt_pub_msg = {.qos = QOS0, .retained = 0, .dup = 0, .id = 0};
MQTTPacket_willOptions g_mqtt_will = MQTTPacket_willOptions_initializer;
MQTTPacket_connectData g_mqtt_conn_data = MQTTPacket_connectData_initializer;
/* 全局傳感器數(shù)據(jù)(與main.c共享) */
float g_humidity_value = 0.0f; // 當(dāng)前濕度
float g_light_intensity = 0.0f; // 當(dāng)前光照強(qiáng)度
uint8_t g_solenoid_valve_state = 0; // 電磁閥狀態(tài)(1:開啟, 0:關(guān)閉)
/* 濕度閾值(與main.c共享) */
extern int g_humidity_low_threshold;
extern int g_humidity_high_threshold;
/**
* @brief 初始化MQTT客戶端
* @param sn 套接字號(hào)
* @param send_buf MQTT發(fā)送數(shù)據(jù)緩沖區(qū)
* @param recv_buf MQTT接收數(shù)據(jù)緩沖區(qū)
*/
void mqtt_init(uint8_t sn, uint8_t *send_buf, uint8_t *recv_buf) {
wiz_NetInfo network_info = {0};
wizchip_getnetinfo(&network_info);
// 將MQTT服務(wù)器域名解析為IP地址
if (do_dns(send_buf, (uint8_t *)s_mqtt_connection_params.mqttHostUrl,
s_mqtt_connection_params.server_ip)) {
while (1)
; // DNS解析失敗時(shí)停機(jī)
}
// 初始化網(wǎng)絡(luò)和MQTT客戶端
NewNetwork(&g_mqtt_network, sn);
ConnectNetwork(&g_mqtt_network, s_mqtt_connection_params.server_ip,
s_mqtt_connection_params.port);
MQTTClientInit(&g_mqtt_client, &g_mqtt_network, 1000, send_buf,
MQTT_ETHERNET_MAX_SIZE, recv_buf, MQTT_ETHERNET_MAX_SIZE);
// 配置MQTT連接參數(shù)
g_mqtt_conn_data.willFlag = 0;
g_mqtt_conn_data.MQTTVersion = 4;
g_mqtt_conn_data.clientID.cstring = s_mqtt_connection_params.clientid;
g_mqtt_conn_data.username.cstring = s_mqtt_connection_params.username;
g_mqtt_conn_data.password.cstring = s_mqtt_connection_params.passwd;
g_mqtt_conn_data.keepAliveInterval = 30;
g_mqtt_conn_data.cleansession = 1;
}
/**
* @brief 解碼MQTT JSON消息(設(shè)置閾值/閥門狀態(tài))
* @param msg JSON消息負(fù)載
*/
void mqtt_json_decode(char *msg) {
cJSON *root_json = cJSON_Parse(msg);
if (!root_json) {
printf("MQTT JSON parse failedrn");
return;
}
// 從JSON中提取"params"對(duì)象
cJSON *params_json = cJSON_GetObjectItem(root_json, "params");
if (!params_json) {
cJSON_Delete(root_json);
return;
}
// 如果"Elect"字段存在,更新電磁閥狀態(tài)
cJSON *valve_json = cJSON_GetObjectItem(params_json, "Elect");
if (valve_json) {
g_solenoid_valve_state = valve_json->valueint;
if (g_solenoid_valve_state) {
sv_open();
} else {
sv_close();
}
}
// 如果"Low"/"High"字段存在,更新濕度閾值
cJSON *low_threshold_json = cJSON_GetObjectItem(params_json, "Low");
cJSON *high_threshold_json = cJSON_GetObjectItem(params_json, "High");
if (low_threshold_json) {
g_humidity_low_threshold = low_threshold_json->valueint;
}
if (high_threshold_json) {
g_humidity_high_threshold = high_threshold_json->valueint;
}
// 確保閾值范圍有效(下限 <= 上限)
if (g_humidity_low_threshold > g_humidity_high_threshold) {
g_humidity_low_threshold = g_humidity_high_threshold - 1;
}
cJSON_Delete(root_json); // 釋放JSON對(duì)象
}
/**
* @brief 接收MQTT消息的回調(diào)函數(shù)
* @param md 消息數(shù)據(jù)(主題和負(fù)載)
*/
void mqtt_message_arrived(MessageData *md) {
char topic_name[64] = {0};
char msg_payload[512] = {0};
// 提取主題和負(fù)載
sprintf(topic_name, "%.*s", (int)md->topicName->lenstring.len,
md->topicName->lenstring.data);
sprintf(msg_payload, "%.*s", (int)md->message->payloadlen,
(char *)md->message->payload);
printf("MQTT recv: %s, %srnrn", topic_name, msg_payload);
// 存儲(chǔ)消息并設(shè)置標(biāo)志
s_mqtt_receive_flag = 1;
memset(s_mqtt_received_msg, 0, sizeof(s_mqtt_received_msg));
memcpy(s_mqtt_received_msg, msg_payload, strlen(msg_payload));
}
/**
* @brief 通過(guò)MQTT發(fā)布傳感器數(shù)據(jù)
* @details 以JSON格式發(fā)送濕度、光照、閥門狀態(tài)和閾值
*/
void mqtt_post_properties(void) {
char json_payload[256] = {0};
// 將傳感器數(shù)據(jù)格式化為JSON負(fù)載
int payload_len =
snprintf(json_payload, sizeof(json_payload),
"{"id":"123","version":"1.0","params":{"
""Elect":%d,"
""Humidity":%.1f,"
""light":%.0f,"
""Low":%d,"
""High":%d"
"},"method":"thing.event.property.post"}",
g_solenoid_valve_state, g_humidity_value, g_light_intensity,
g_humidity_low_threshold, g_humidity_high_threshold);
// 發(fā)布消息
g_mqtt_pub_msg.payload = json_payload;
g_mqtt_pub_msg.payloadlen = payload_len;
MQTTPublish(&g_mqtt_client, s_mqtt_connection_params.pubtopic,
&g_mqtt_pub_msg);
printf("MQTT published: %s, %srnrn", s_mqtt_connection_params.pubtopic,
json_payload);
}
/**
* @brief MQTT狀態(tài)機(jī)(處理連接、訂閱和消息)
*/
void do_mqtt(void) {
uint8_t ret;
switch (s_mqtt_run_status) {
case CONN: // 連接到MQTT服務(wù)器
ret = MQTTConnect(&g_mqtt_client, &g_mqtt_conn_data);
printf("Connecting to MQTT server: %d.%d.%d.%d:%drn",
s_mqtt_connection_params.server_ip[0],
s_mqtt_connection_params.server_ip[1],
s_mqtt_connection_params.server_ip[2],
s_mqtt_connection_params.server_ip[3],
s_mqtt_connection_params.port);
printf("Connection %srnrn", ret == SUCCESSS ? "success" : "failed");
s_mqtt_run_status = (ret == SUCCESSS) ? SUB : ERR;
break;
case SUB: // 訂閱主題
ret = MQTTSubscribe(&g_mqtt_client, s_mqtt_connection_params.subtopic,
s_mqtt_connection_params.pubQoS, mqtt_message_arrived);
printf("Subscribing to %srn", s_mqtt_connection_params.subtopic);
printf("Subscription %srnrn", ret == SUCCESSS ? "success" : "failed");
s_mqtt_run_status = (ret == SUCCESSS) ? KEEPALIVE : ERR;
break;
case KEEPALIVE: // 維持連接并檢查消息
if (MQTTYield(&g_mqtt_client, 30) != SUCCESSS) {
s_mqtt_run_status = ERR;
break;
}
// 繼續(xù)處理接收的消息
case RECV: // 處理接收的消息
if (s_mqtt_receive_flag) {
s_mqtt_receive_flag = 0;
mqtt_json_decode(s_mqtt_received_msg); // 解碼并處理消息
}
break;
case ERR: // 處理錯(cuò)誤(重試)
printf("MQTT error! Reconnecting...rn");
delay_ms(1000);
s_mqtt_run_status = CONN; // 重試連接
break;
default:
break;
}
}
5.6 網(wǎng)頁(yè)控制
1.功能定位:提供Web服務(wù)接口,支持通過(guò)HTTP協(xié)議獲取設(shè)備狀態(tài)和控制參數(shù)。
2.核心功能:
解析HTTP 請(qǐng)求(支持 GET、POST方法)。
提供多個(gè)API端點(diǎn):
根路徑/:返回網(wǎng)頁(yè)內(nèi)容。
/api/sensor:以JSON格式返回傳感器數(shù)據(jù)(濕度、光照、閥門狀態(tài)、閾值)。
/api/threshold:接收POST請(qǐng)求更新濕度閾值。
3.數(shù)據(jù)交互:
讀取全局變量獲取傳感器狀態(tài)和閾值。
通過(guò)HTTP響應(yīng)返回JSON格式數(shù)據(jù)。
解析POST請(qǐng)求中的JSON數(shù)據(jù)更新系統(tǒng)參數(shù)。
4.通信管理:
基于TCP狀態(tài)機(jī)管理連接生命周期(建立、數(shù)據(jù)傳輸、關(guān)閉)。
處理不同HTTP狀態(tài)碼(200、204、400、404)。
#include "cJSON.h"
#include "loopback.h"
#include "socket.h"
#include "web_page.h"
#include "wizchip_conf.h"
#include
#include
#include
/* 使用web_page.h中定義的HTTP_RESPONSE_404 */
#define DATA_BUF_SIZE 1024
#define STR(x) #x // 用于行號(hào)的字符串化宏
/* 全局傳感器數(shù)據(jù)(與main.c共享) */
extern float g_humidity_value;
extern float g_light_intensity;
extern uint8_t g_solenoid_valve_state;
extern int g_humidity_low_threshold;
extern int g_humidity_high_threshold;
/**
* @brief HTTP請(qǐng)求行結(jié)構(gòu)(方法、URI、版本)
*/
typedef struct {
char method[16]; // 例如:"GET"
char uri[256]; // 例如:"/api/sensor"
char version[16]; // 例如:"HTTP/1.1"
} HttpReqLine;
/**
* @brief 從原始數(shù)據(jù)解析HTTP請(qǐng)求行
* @param request 原始HTTP請(qǐng)求數(shù)據(jù)
* @param req_line 存儲(chǔ)解析結(jié)果的輸出結(jié)構(gòu)
* @return 成功返回0,失敗返回-1
*/
static int http_parse_request_line(const char *request, HttpReqLine *req_line) {
char buffer[1024];
strncpy(buffer, request, sizeof(buffer));
buffer[sizeof(buffer) - 1] = ''; // 確保字符串以空字符結(jié)尾
// 查找第一行的結(jié)束符("rn"或"n")
char *line_end = strstr(buffer, "rn");
if (!line_end) {
line_end = strstr(buffer, "n"); // 兼容Unix換行符
}
if (!line_end) {
return -1; // 格式無(wú)效
}
*line_end = ''; // 截?cái)嗟降谝恍?
// 使用空格分割為方法、URI和版本
char *method = strtok(buffer, " ");
char *uri = strtok(NULL, " ");
char *version = strtok(NULL, " ");
if (!method || !uri || !version) {
return -1; // 請(qǐng)求行不完整
}
// 將解析的值復(fù)制到結(jié)構(gòu)中
strncpy(req_line->method, method, sizeof(req_line->method) - 1);
strncpy(req_line->uri, uri, sizeof(req_line->uri) - 1);
strncpy(req_line->version, version, sizeof(req_line->version) - 1);
return 0;
}
/**
* @brief 發(fā)送帶有CORS頭的HTTP響應(yīng)
* @param sn 套接字號(hào)
* @param status HTTP狀態(tài)碼(例如:"200 OK")
* @param content_type MIME類型(例如:"application/json")
* @param body 響應(yīng)體內(nèi)容
*/
static void send_http_response(uint8_t sn, const char *status,
const char *content_type, const char *body) {
char response[512];
int body_len = strlen(body);
// 構(gòu)建帶有CORS頭的響應(yīng)
sprintf(response,
"HTTP/1.1 %srn"
"Content-Type: %srn"
"Access-Control-Allow-Origin: *rn"
"Access-Control-Allow-Methods: GET, POST, OPTIONSrn"
"Access-Control-Allow-Headers: Content-Typern"
"Connection: closern"
"Content-Length: %drn"
"rn"
"%s",
status, content_type, body_len, body);
// 發(fā)送完整響應(yīng)
send(sn, (uint8_t *)response, strlen(response));
}
/**
* @brief 帶HTTP支持的TCP服務(wù)器回環(huán)測(cè)試
* @param sn 套接字號(hào)
* @param network_buf 網(wǎng)絡(luò)數(shù)據(jù)緩沖區(qū)
* @param port 監(jiān)聽(tīng)端口
* @return 成功返回1,失敗返回錯(cuò)誤碼
*/
int32_t loopback_tcps(uint8_t sn, uint8_t *network_buf, uint16_t port) {
int32_t ret;
uint16_t recv_size = 0;
uint16_t send_size = 0;
switch (getSn_SR(sn)) {
case SOCK_ESTABLISHED: // 連接已建立
if (getSn_IR(sn) & Sn_IR_CON) {
setSn_IR(sn, Sn_IR_CON); // 清除連接中斷標(biāo)志
}
// 檢查接收數(shù)據(jù)
if ((recv_size = getSn_RX_RSR(sn)) > 0) {
if (recv_size > DATA_BUF_SIZE) {
recv_size = DATA_BUF_SIZE; // 限制為緩沖區(qū)大小
}
// 讀取接收的數(shù)據(jù)
ret = recv(sn, network_buf, recv_size);
if (ret <= 0) {
return ret; // 處理錯(cuò)誤
}
recv_size = (uint16_t)ret;
network_buf[recv_size] = ''; // 添加空終止符
// 解析HTTP請(qǐng)求行
HttpReqLine req_line;
if (http_parse_request_line((char *)network_buf, &req_line) == 0) {
printf("HTTP Method: %s, URI: %sn", req_line.method, req_line.uri);
// 處理OPTIONS請(qǐng)求(CORS預(yù)檢)
if (strcmp(req_line.method, "OPTIONS") == 0) {
send_http_response(sn, "204 No Content", "text/plain", "");
disconnect(sn);
close(sn);
}
// 處理GET /(提供網(wǎng)頁(yè))
else if (strcmp(req_line.method, "GET") == 0 &&
strcmp(req_line.uri, "/") == 0) {
uint16_t content_len = strlen(index_page);
send_size = 0;
while (strlen(index_page) != send_size) {
ret = send(sn, (uint8_t *)index_page + send_size,
strlen(index_page) - send_size);
if (ret < 0) {
close(sn);
return ret;
}
send_size += ret;
}
disconnect(sn);
close(sn);
}
// 處理GET /api/sensor(返回傳感器數(shù)據(jù))
else if (strcmp(req_line.method, "GET") == 0 &&
strcmp(req_line.uri, "/api/sensor") == 0) {
char sensor_json[128];
sprintf(sensor_json,
"{"humi":%.1f,"light":%.0f,"sv":"%s","low":%d,"
""high":%d}",
g_humidity_value, g_light_intensity,
g_solenoid_valve_state ? "ON" : "OFF",
g_humidity_low_threshold, g_humidity_high_threshold);
send_http_response(sn, "200 OK", "application/json", sensor_json);
disconnect(sn);
close(sn);
}
// 處理POST /api/threshold(更新閾值)
else if (strcmp(req_line.method, "POST") == 0 &&
strcmp(req_line.uri, "/api/threshold") == 0) {
// 查找請(qǐng)求體(健壯解析)
char *body_start = NULL;
char *header_end = strstr((char *)network_buf, "rnrn");
if (header_end) {
body_start = header_end + 4; // 跳過(guò)"rnrn"
} else {
// 兼容Unix換行符
header_end = strstr((char *)network_buf, "nn");
if (header_end) {
body_start = header_end + 2; // 跳過(guò)"nn"
}
}
// 驗(yàn)證請(qǐng)求體存在
if (!body_start || body_start >= (char *)network_buf + recv_size) {
send_http_response(
sn, "400 Bad Request", "application/json",
"{"success":false, "error":"Missing request body"}");
disconnect(sn);
close(sn);
return 1;
}
// 解析JSON請(qǐng)求體
cJSON *root_json = cJSON_Parse(body_start);
if (!root_json) {
send_http_response(
sn, "400 Bad Request", "application/json",
"{"success":false, "error":"Invalid JSON format"}");
disconnect(sn);
close(sn);
return 1;
}
// 提取并驗(yàn)證閾值
cJSON *low_json = cJSON_GetObjectItem(root_json, "low");
cJSON *high_json = cJSON_GetObjectItem(root_json, "high");
if (cJSON_IsNumber(low_json) && cJSON_IsNumber(high_json)) {
int new_low = low_json->valueint;
int new_high = high_json->valueint;
// 驗(yàn)證范圍
if (new_low < 0 || new_high > 100 || new_low >= new_high) {
send_http_response(sn, "400 Bad Request", "application/json",
"{"success":false, "error":"Invalid "
"range (0-100, low < high)"}");
} else {
// 更新閾值
g_humidity_low_threshold = new_low;
g_humidity_high_threshold = new_high;
printf("Thresholds updated: Low=%d, High=%dn", new_low,
new_high);
send_http_response(sn, "200 OK", "application/json",
"{"success":true}");
}
} else {
send_http_response(sn, "400 Bad Request", "application/json",
"{"success":false, "error":"Missing 'low' "
"or 'high' parameters"}");
}
cJSON_Delete(root_json); // 釋放JSON對(duì)象
disconnect(sn);
close(sn);
}
// 處理未知請(qǐng)求
else {
send_http_response(sn, "404 Not Found", "text/html",
"
Page Not Found/body?>/html?>"); disconnect(sn); close(sn); } } else { printf("Failed to parse HTTP requestn"); send_http_response( sn, "400 Bad Request", "application/json", "{"success":false, "error":"Invalid request format"}"); disconnect(sn); close(sn); } } break; case SOCK_CLOSE_WAIT: // 關(guān)閉等待狀態(tài) if ((ret = disconnect(sn)) != SOCK_OK) { return ret; } break; case SOCK_INIT: // 套接字已初始化,開始監(jiān)聽(tīng) if ((ret = listen(sn)) != SOCK_OK) { return ret; } break; case SOCK_CLOSED: // 套接字已關(guān)閉,重新初始化 if ((ret = socket(sn, Sn_MR_TCP, port, 0x00)) != sn) { return ret; } break; default: break; } return 1; }
6 功能驗(yàn)證
程序燒錄完畢,硬件連接完成如下圖所示:

硬件連接完畢,上電通過(guò)串口助手打印如下信息:

6.1 同步阿里云
通過(guò)串口每5S采集一次數(shù)據(jù)發(fā)送到云平臺(tái),當(dāng)我們把光照傳感器逐漸靠近光源,光照值越來(lái)越大。此時(shí)土壤濕度傳感器未插入土壤,在空氣中檢測(cè)濕度小于30%RH,繼電器打開,開始澆灌。


當(dāng)我們把土壤濕度傳感器插入盆栽中,可以看到當(dāng)濕度小于30%RH時(shí)自動(dòng)開始灌溉,當(dāng)濕度大于50%RH時(shí)停止灌溉。


當(dāng)濕度小于30%RH時(shí)繼電器打開,開始澆灌,當(dāng)濕度大于50%RH時(shí)繼電器關(guān)閉停止灌溉。

服務(wù)器下發(fā)指令控制繼電器開關(guān),進(jìn)而實(shí)現(xiàn)對(duì)澆灌啟停的控制。

6.2 網(wǎng)頁(yè)控制
通過(guò)網(wǎng)頁(yè)平臺(tái)不僅能實(shí)時(shí)監(jiān)測(cè)農(nóng)田環(huán)境數(shù)據(jù),更可遠(yuǎn)程靈活設(shè)定調(diào)控閾值,讓田間管理實(shí)現(xiàn)精準(zhǔn)化、智能化調(diào)控。

7總結(jié)
該系統(tǒng)以W55MH32L-EVB為核心,通過(guò)MQTT連阿里云,采集土壤濕度、光照數(shù)據(jù),實(shí)現(xiàn)水泵智能控制。支持自動(dòng)與遠(yuǎn)程調(diào)控,5秒采集一次,同時(shí)支持網(wǎng)頁(yè)修改濕度閾值,功能穩(wěn)定,為智能灌溉提供有效方案。感謝大家的耐心閱讀!如果您在搭建過(guò)程中遇到硬件接線、阿里云配置或代碼調(diào)試問(wèn)題,歡迎在評(píng)論區(qū)留言交流~ 覺(jué)得有幫助的話也請(qǐng)點(diǎn)贊收藏,您的支持是我分享的動(dòng)力!
審核編輯 黃宇
-
單片機(jī)
+關(guān)注
關(guān)注
6074文章
45457瀏覽量
667100 -
阿里云
+關(guān)注
關(guān)注
3文章
1033瀏覽量
45594 -
MQTT協(xié)議
+關(guān)注
關(guān)注
0文章
104瀏覽量
6472 -
智能農(nóng)業(yè)
+關(guān)注
關(guān)注
0文章
136瀏覽量
8812
發(fā)布評(píng)論請(qǐng)先 登錄
基于阿里云MQTT物聯(lián)網(wǎng)平臺(tái)視頻監(jiān)控(下)
esp8266連接阿里云平臺(tái)時(shí)mqtt連接超時(shí)
【NXP LPC54110試用申請(qǐng)】基于云計(jì)算的農(nóng)業(yè)智能檢測(cè)及農(nóng)產(chǎn)品溯源系統(tǒng)
基于阿里云HiTSDB搭建工業(yè)物聯(lián)網(wǎng)平臺(tái)實(shí)踐
基于onenet云平臺(tái)MQTT協(xié)議數(shù)據(jù)采集以及遠(yuǎn)程控制的個(gè)人總結(jié)資料
如何通過(guò)MQTT協(xié)議連接物聯(lián)網(wǎng)阿里云實(shí)現(xiàn)設(shè)備的遠(yuǎn)程IO監(jiān)控開關(guān)量數(shù)字量模擬量狀態(tài)的讀取及控制
基于鴻蒙Hi3861V100 MQTT協(xié)議 對(duì)接阿里云物聯(lián)網(wǎng)平臺(tái)
stm32+W5500 與 阿里云微消息隊(duì)列 MQTT版本
如何用阿里云的Iot Studio制作web網(wǎng)頁(yè)呢
基于OpenHarmony的阿里云IoT服務(wù)實(shí)現(xiàn)
【OpenHarmony開源開發(fā)者成長(zhǎng)計(jì)劃解決方案學(xué)生挑戰(zhàn)賽】--基于OpenHarmony的智慧農(nóng)業(yè)環(huán)境監(jiān)控系統(tǒng)設(shè)計(jì)
微信小程序使用MQTT遠(yuǎn)程控制單片機(jī)——阿里云物聯(lián)網(wǎng)平臺(tái)
基于阿里云MQTT物聯(lián)網(wǎng)平臺(tái)視頻監(jiān)控(上)
智能農(nóng)業(yè)監(jiān)控系統(tǒng):MQTT阿里云平臺(tái)監(jiān)測(cè)+內(nèi)置Web網(wǎng)頁(yè)控制+代碼解析
評(píng)論