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

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

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

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

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

4.1.3 連接、訂閱和發(fā)布消息
接著我們可以使用上面記錄的連接參數(shù)進行連接,當連接成功后,訂閱上面的訂閱主題。并通過發(fā)布主題上報物模型數(shù)據(jù)。
在阿里云平臺,如果產(chǎn)品創(chuàng)建階段選擇的數(shù)據(jù)格式為Alink JSON格式時,接收和發(fā)送數(shù)據(jù)格式都會遵守下面這個格式:
{
"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ǔ)硬件初始化(時鐘、延時、串口、定時器等),配置ADC(模數(shù)轉(zhuǎn)換)用于傳感器數(shù)據(jù)采集,初始化繼電器控制模塊,并設(shè)置WIZnet以太網(wǎng)芯片的網(wǎng)絡(luò)參數(shù)(MAC、IP、網(wǎng)關(guān)等)。
2.網(wǎng)絡(luò)通信功能 實現(xiàn)雙重網(wǎng)絡(luò)通信能力:
MQTT協(xié)議:通過MQTT客戶端(do_mqtt()和mqtt_post_properties())實現(xiàn)傳感器數(shù)據(jù)的遠程發(fā)布。
獲取網(wǎng)頁:通過loopback_tcps()提供TCP服務(wù)器功能,進行HTTP請求和響應(yīng)處理。
3.傳感器數(shù)據(jù)處理與控制
周期性(5秒間隔)通過process_sensors_and_control()讀取傳感器數(shù)據(jù)(濕度、光照強度)。
基于濕度閾值(高低閾值)實現(xiàn)繼電器自動控制邏輯。
將實時數(shù)據(jù)(濕度、光照、繼電器狀態(tài))通過MQTT發(fā)布到阿里平臺同時刷新網(wǎng)頁環(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ù)據(jù)(在do_mqtt.c中定義) */ extern float g_humidity_value; // 當前濕度讀數(shù) extern float g_light_intensity; // 當前光照強度讀數(shù) extern uint8_t g_solenoid_valve_state; // 電磁閥狀態(tài)(1:開啟, 0:關(guān)閉) /* 濕度閾值(與阿里云IoT模型對齊,范圍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)聽端口 wiz_NetInfo g_default_network_info = { // 默認網(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)滴答計數(shù)器(毫秒)- bsp_tim使用的名稱 /** * @brief 主程序循環(huán) * @details 處理MQTT通信、TCP回環(huán)和傳感器處理 */ int main(void) { rcc_clk_config(); // RCC時鐘配置 delay_init(); // 延時初始化 console_usart_init(115200); // 控制臺串口初始化,波特率115200 tim3_init(); // TIM3定時器初始化 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(); // 通過MQTT發(fā)布傳感器數(shù)據(jù) } } }
5.2 ADC采集
1.關(guān)鍵配置:
初始化ADC為連續(xù)掃描模式,同時啟用DMA傳輸。
配置GPIO為模擬輸入模式,對應(yīng)傳感器連接的引腳。
設(shè)置DMA以循環(huán)方式將 ADC 數(shù)據(jù)傳輸?shù)骄彌_區(qū),無需CPU干預(yù)。
2.數(shù)據(jù)處理:
采樣數(shù)據(jù)交替存儲在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ū)(存儲交替的通道讀數(shù)) */
static uint16_t s_adc_dma_buffer[ADC_BUFFER_SIZE];
/**
* @brief 初始化帶DMA功能的ADC
* @details 配置ADC通道(濕度和光照)、GPIO和DMA以實現(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的時鐘
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)體為默認值
adc_init_struct.ADC_Mode = ADC_Mode_Independent; // 獨立模式
adc_init_struct.ADC_ScanConvMode = ENABLE; // 使能掃描模式
adc_init_struct.ADC_ContinuousConvMode = ENABLE; // 使能連續(xù)轉(zhuǎn)換模式
adc_init_struct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 無外部觸發(fā)
adc_init_struct.ADC_DataAlign = ADC_DataAlign_Right; // 數(shù)據(jù)右對齊
adc_init_struct.ADC_NbrOfChannel = 2; // 2個通道(濕度+光照)
ADC_Init(ADC1, &adc_init_struct);
// 配置ADC通道和采樣時間
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)先級
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請求
// 校準并啟動ADC
ADC_Cmd(ADC1, ENABLE); // 使能ADC1
ADC_ResetCalibration(ADC1); // 重置校準寄存器
while (ADC_GetResetCalibrationStatus(ADC1)) // 等待重置校準完成
;
ADC_StartCalibration(ADC1); // 開始校準
while (ADC_GetCalibrationStatus(ADC1)) // 等待校準完成
;
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]; // 累加對應(yīng)通道的樣本
valid_samples++; // 計數(shù)有效樣本
}
return (valid_samples > 0) ? (sum / valid_samples) : 0; // 返回平均值(避免除零)
}
5.3 傳感器數(shù)據(jù)讀取
1.濕度傳感器讀?。╤umidity_read):
從ADC獲取濕度通道的平均原始值。
將ADC值轉(zhuǎn)換為電壓(基于3.3V參考電壓和12位ADC)。
通過傳感器特定公式將電壓轉(zhuǎn)換為濕度值(0-100%)。
對結(jié)果進行范圍限制,確保在有效區(qū)間內(nèi)。
2.光照強度讀?。╨ight_read_intensity):
從 ADC 獲取光照通道的平均原始值。
處理零值情況避免除零錯誤。
通過傳感器公式將ADC值轉(zhuǎn)換為光照強度(自定義單位)。
3.傳感器處理與控制(process_sensors_and_control):
讀取濕度和光照值并更新全局變量。
根據(jù)濕度閾值控制繼電器:低于低閾值打開(澆水),高于高閾值關(guān)閉(停止?jié)菜?/p>
打印當前狀態(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)換為相對濕度(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 從傳感器讀取光照強度
* @details 將ADC原始值轉(zhuǎn)換為光照強度(任意單位)
* @return 光照強度值
*/
float light_read_intensity(void) {
// 從光照通道(索引1)讀取ADC平均值
uint16_t adc_raw_value = adc_get_average_value(1, ADC_BUFFER_SIZE / 2);
// 避免除零錯誤
if (adc_raw_value == 0) {
adc_raw_value = 1;
}
// 將ADC值轉(zhuǎn)換為光照強度(傳感器特定公式)
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é)菜?
}
// 打印當前狀態(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ù)負責初始化GPIO:
使能GPIOB時鐘。
配置引腳為開漏輸出模式(GPIO_Mode_Out_OD),速度50MHz。
初始狀態(tài)設(shè)置為高電平(關(guān)閉繼電器,利用開漏模式的內(nèi)部上拉)。
2.核心控制函數(shù):
sv_close():通過設(shè)置引腳為高電平關(guān)閉繼電器,停止?jié)菜?,并打印狀態(tài)信息。
sv_open():通過清除引腳為低電平打開繼電器,開始澆水,并打印狀態(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時鐘
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.功能定位:通過 MQTT 協(xié)議實現(xiàn)設(shè)備與阿里云 IoT 平臺的雙向通信,完成傳感器數(shù)據(jù)上報與云端控制指令接收。
2.核心配置:包含阿里云MQTT 服務(wù)器地址、端口、認證信息(客戶端ID、用戶名、密碼)及收發(fā)主題,采用QoS0等級通信。
3.關(guān)鍵流程:
初始化:解析域名、建立網(wǎng)絡(luò)連接、配置MQTT客戶端參數(shù)。
通信機制:通過狀態(tài)機管理連接、訂閱、消息收發(fā)及錯誤重連。
數(shù)據(jù)交互:將傳感器數(shù)據(jù)(濕度、光照、閥門狀態(tài))打包為JSON上報;解析云端上下發(fā)的JSON指令,控制閥門門開關(guān)及更新濕度閾值。
4.與系統(tǒng)集成:關(guān)聯(lián)傳感器數(shù)據(jù)全局變量,調(diào)用繼電器控制函數(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)機狀態(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消息標志
/* 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; // 當前濕度
float g_light_intensity = 0.0f; // 當前光照強度
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 套接字號
* @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解析失敗時停機
}
// 初始化網(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消息負載
*/
void mqtt_json_decode(char *msg) {
cJSON *root_json = cJSON_Parse(msg);
if (!root_json) {
printf("MQTT JSON parse failedrn");
return;
}
// 從JSON中提取"params"對象
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對象
}
/**
* @brief 接收MQTT消息的回調(diào)函數(shù)
* @param md 消息數(shù)據(jù)(主題和負載)
*/
void mqtt_message_arrived(MessageData *md) {
char topic_name[64] = {0};
char msg_payload[512] = {0};
// 提取主題和負載
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);
// 存儲消息并設(shè)置標志
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 通過MQTT發(fā)布傳感器數(shù)據(jù)
* @details 以JSON格式發(fā)送濕度、光照、閥門狀態(tài)和閾值
*/
void mqtt_post_properties(void) {
char json_payload[256] = {0};
// 將傳感器數(shù)據(jù)格式化為JSON負載
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)機(處理連接、訂閱和消息)
*/
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: // 處理錯誤(重試)
printf("MQTT error! Reconnecting...rn");
delay_ms(1000);
s_mqtt_run_status = CONN; // 重試連接
break;
default:
break;
}
}
5.6 網(wǎng)頁控制
1.功能定位:提供Web服務(wù)接口,支持通過HTTP協(xié)議獲取設(shè)備狀態(tài)和控制參數(shù)。
2.核心功能:
解析HTTP 請求(支持 GET、POST方法)。
提供多個API端點:
根路徑/:返回網(wǎng)頁內(nèi)容。
/api/sensor:以JSON格式返回傳感器數(shù)據(jù)(濕度、光照、閥門狀態(tài)、閾值)。
/api/threshold:接收POST請求更新濕度閾值。
3.數(shù)據(jù)交互:
讀取全局變量獲取傳感器狀態(tài)和閾值。
通過HTTP響應(yīng)返回JSON格式數(shù)據(jù)。
解析POST請求中的JSON數(shù)據(jù)更新系統(tǒng)參數(shù)。
4.通信管理:
基于TCP狀態(tài)機管理連接生命周期(建立、數(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 // 用于行號的字符串化宏
/* 全局傳感器數(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請求行結(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請求行
* @param request 原始HTTP請求數(shù)據(jù)
* @param req_line 存儲解析結(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; // 格式無效
}
*line_end = ''; // 截斷到第一行
// 使用空格分割為方法、URI和版本
char *method = strtok(buffer, " ");
char *uri = strtok(NULL, " ");
char *version = strtok(NULL, " ");
if (!method || !uri || !version) {
return -1; // 請求行不完整
}
// 將解析的值復(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 套接字號
* @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)測試
* @param sn 套接字號
* @param network_buf 網(wǎng)絡(luò)數(shù)據(jù)緩沖區(qū)
* @param port 監(jiān)聽端口
* @return 成功返回1,失敗返回錯誤碼
*/
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); // 清除連接中斷標志
}
// 檢查接收數(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; // 處理錯誤
}
recv_size = (uint16_t)ret;
network_buf[recv_size] = ''; // 添加空終止符
// 解析HTTP請求行
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請求(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)頁)
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) {
// 查找請求體(健壯解析)
char *body_start = NULL;
char *header_end = strstr((char *)network_buf, "rnrn");
if (header_end) {
body_start = header_end + 4; // 跳過"rnrn"
} else {
// 兼容Unix換行符
header_end = strstr((char *)network_buf, "nn");
if (header_end) {
body_start = header_end + 2; // 跳過"nn"
}
}
// 驗證請求體存在
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請求體
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;
}
// 提取并驗證閾值
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;
// 驗證范圍
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對象
disconnect(sn);
close(sn);
}
// 處理未知請求
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)聽 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 功能驗證
程序燒錄完畢,硬件連接完成如下圖所示:

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

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


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


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

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

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

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

智能農(nóng)業(yè)監(jiān)控系統(tǒng):MQTT阿里云平臺監(jiān)測+內(nèi)置Web網(wǎng)頁控制+代碼解析
評論