ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32+ESP8266手搓MQTT接入华为云IoTDA的水产养殖系统

STM32+ESP8266手搓MQTT接入华为云IoTDA的水产养殖系统 简介面向嵌入式开发与物联网方向学习者这份PDF围绕基于STM32F103RCT6的智能水产养殖系统展开完整呈现从项目背景、设计需求到硬件选型、程序设计及华为云IoT对接的实现路径。内容涵盖水位监测、水温恒温控制、浊度与PH值水质检测、定时投喂、自动补氧以及手机APP远程监控等功能模块并涉及超声波、温度、浊度、PH传感器、SG90舵机、抽水泵、充氧泵和ESP8266等器件选型。文档还讲解STM32主程序编写、MQTT协议在ESP8266上的实现、华为云物联网平台数据交互以及Qt Android端远程控制思路并结合系统框架图与硬件实物说明传感器采集、继电器控制和步进电机投喂的完整闭环。适合课程设计、毕业设计或嵌入式项目实践参考。资源为1个PDF文件压缩包约37.92MB已有289人学习便于系统查阅方案架构与关键实现细节。1. 从一片鱼塘到一朵云这套STM32水产养殖系统到底解决了什么传统鱼塘最怕两件事一是夜里水温骤降没人管二是水质浑了要等天亮才发现。这套基于STM32F103RCT6的智能水产养殖系统思路很直接——把水位、水温、浊度、PH这几个关键量全部数字化用ESP8266经MQTT协议推到华为云IoTDA再用Qt写的Android端拉取数据做远程控制。说白了它就是一个典型的小型工业物联网闭环终端采集、边缘决策、云端中转、App展示与下发。它适合谁正在做基于STM32的毕业设计、想跑通华为云IoTDA全流程、又希望手里有一份能直接改参数复现的代码的工程师。整套系统最值得拆的地方不在传感器本身而在于ESP8266用的是原厂AT固件、没烧第三方固件意味着MQTT的报文得在STM32里按字节手工拼出来——这才是绝大多数人卡住的地方。下面从硬件选型、协议实现、云平台配置到App联调一层层拆开看。2. 硬件链路与传感器选型为什么是DS18B20加TDS而不是PH模块硬件是整个系统的地基选错了后面代码写得再漂亮也白搭。这一章先把每条采集链路讲清楚再落到具体的接线和初始化代码上。2.1 主控与外设的引脚分配逻辑STM32F103RCT6属于大容量型号256KB Flash、48KB SRAM、5个串口、3个ADC跑这套系统绰绰有余。关键在于把外设分配开外设接口引脚示例说明ESP8266USART2PA2/PA3AT指令通信波特率115200DS18B20单总线PB12需4.7K上拉电阻TDS传感器ADC1_IN0PA0模拟量输出0-2.3V超声波HC-SR04定时器输入捕获PA8/PA9Trig输出、Echo捕获SG90舵机TIM3 PWMPB050Hz0.5-2.5ms脉宽继电器组GPIOPC6/PC7/PC8加热、水泵、增氧泵蜂鸣器GPIOPC9浊度超阈值报警分配原则是模拟量和单总线不抢定时器资源PWM和输入捕获各自独立串口2专门留给WiFi模块方便调试时用串口1打日志。2.2 DS18B20水温采集的单总线时序DS18B20最坑的地方是时序必须精确到微秒级。用HAL库直接跑HAL_Delay的毫秒精度根本不够必须用__NOP()做微秒延时。// DS18B20复位拉低至少480us然后释放等待回应 uint8_t DS18B20_Reset(void) { uint8_t presence; DS18B20_IO_OUT(); DS18B20_DQ_OUT 0; delay_us(750); // 拉低750us超过480us最小值 DS18B20_DQ_OUT 1; delay_us(15); // 释放后15us内采样 DS18B20_IO_IN(); presence DS18B20_DQ_IN; // 0表示存在1表示无器件 delay_us(480); return presence; } // 读取温度整数部分小数部分分辨率0.0625度 float DS18B20_GetTemp(void) { uint8_t tempL, tempH; float temp; DS18B20_Reset(); DS18B20_WriteByte(0xCC); // 跳过ROM DS18B20_WriteByte(0x44); // 启动转换 delay_ms(750); // 12位分辨率最大转换时间 DS18B20_Reset(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0xBE); // 读暂存器 tempL DS18B20_ReadByte(); tempH DS18B20_ReadByte(); temp (tempH 8) | tempL; temp temp * 0.0625; return temp; }delay_us不能直接用HAL库我一般用系统滴答定时器或者简单的循环延时。注意DS18B20转换时间在12位模式下最长750ms这里如果用中断方式读会更容易踩坑简单轮询反而更稳。若温度读出来一直是85.0多半是复位失败或上拉电阻没接。2.3 TDS浊度采集与阈值判断TDS模块输出的是0-2.3V模拟电压对应STM32的ADC通道。用DMA循环采样避免阻塞#define TDS_ADC_CHANNEL ADC_CHANNEL_0 #define TDS_THRESHOLD 800 // 超标阈值单位ppm uint16_t Get_TDS_Value(void) { uint32_t sum 0; for (int i 0; i 10; i) { HAL_ADC_Start(hadc1); HAL_ADC_PollForConversion(hadc1, 10); sum HAL_ADC_GetValue(hadc1); } uint16_t adc sum / 10; // 取10次平均滤波 // 电压换算3.3V/4096 * adc再按模块手册映射到ppm float voltage adc * 3.3f / 4096.0f; return (uint16_t)(voltage * 500); // 具体系数依模块标定 }这里最容易犯的错是拿单次ADC值直接用跳变非常大。取10次平均后基本稳定。当TDS值超过阈值时拉高蜂鸣器并启动水泵继电器等降到阈值的80%再停泵避免临界点频繁启停。3. ESP8266不烧固件STM32里怎么手搓MQTT报文这一章是整套系统最硬核的部分也是很多同学最容易放弃的地方。因为ESP8266用的是官方AT固件不支持MQTT指令所以需要在STM32端自己拼MQTT报文再用AT指令发出去。3.1 为什么不用MQTT AT指令而是用TCP透传市面上的ESP8266AT固件分几类有的支持ATMQTTUSERCFG这类指令有的只能ATCIPSTART建立TCP连接。当前这套方案用的官方固件属于后者因此要建立一条TCP连接指向华为云IoTDA的1883端口然后在其上手工拼MQTT协议。MQTT报文结构分三部分固定报头、可变报头、有效载荷。CONNECT报文的关键字段包括字段字节数值协议名60x00 0x04 M Q T T协议级别10x04MQTT 3.1.1连接标志10xC2用户名密码清理会话保活时间20x00 0x3C60秒客户端ID变长华为云设备ID用户名变长设备ID密码变长HMAC-SHA256加密后的密钥3.2 拼接CONNECT报文的代码实现// 拼装MQTT CONNECT报文华为云要求用户名设备ID密码密钥摘要 uint16_t MQTT_BuildConnect(uint8_t *buf, const char *clientId, const char *username, const char *password) { uint16_t idx 0; uint16_t remaining 10 2 strlen(clientId) 2 strlen(username) 2 strlen(password); buf[idx] 0x10; // CONNECT报文类型 buf[idx] remaining 0xFF; // 剩余长度小长度单字节 buf[idx] 0x00; buf[idx] 0x04; buf[idx] M; buf[idx] Q; buf[idx] T; buf[idx] T; buf[idx] 0x04; // 协议级别3.1.1 buf[idx] 0xC2; // 用户名密码清会话 buf[idx] 0x00; buf[idx] 0x3C; // 保活60s // 客户端ID、用户名、密码各带2字节长度前缀 buf[idx] strlen(clientId) 8; buf[idx] strlen(clientId) 0xFF; memcpy(buf[idx], clientId, strlen(clientId)); idx strlen(clientId); buf[idx] strlen(username) 8; buf[idx] strlen(username) 0xFF; memcpy(buf[idx], username, strlen(username)); idx strlen(username); buf[idx] strlen(password) 8; buf[idx] strlen(password) 0xFF; memcpy(buf[idx], password, strlen(password)); idx strlen(password); return idx; }剩余长度字段如果超过127字节需要多字节变长编码这里的报文通常短一字节够用。密码字段华为云要求用设备密钥做HMAC-SHA256计算后转十六进制字符串格式是时间戳;算法;签名这部分必须在STM32端做一次摘要运算。如果不想在MCU里跑HMAC常见做法是预先生成好签名再下发但那样不是动态密钥安全性低不适合正式产品。3.3 用AT指令把报文发出去拼好报文后通过AT指令发送// 连接WiFi和TCP发送MQTT报文 void ESP8266_SendMQTT(uint8_t *mqttBuf, uint16_t len) { ESP8266_SendAT(ATCWMODE1\r\n, OK); ESP8266_SendAT(ATCWJAP\SSID\,\PASSWORD\\r\n, OK); ESP8266_SendAT(ATCIPSTART\TCP\,\7445xxxx.st1.iotda-device.cn-north-4.myhuaweicloud.com\,1883\r\n, CONNECT); char cmd[32]; sprintf(cmd, ATCIPSEND%d\r\n, len); ESP8266_SendAT(cmd, ); // 等待提示符 HAL_UART_Transmit(huart2, mqttBuf, len, 1000); ESP8266_SendAT(, OK); }ATCIPSEND必须等模块回再发数据否则会被当成AT指令截断。AT指令之间要留足够延时发送过快会导致丢包。如果一直卡在ATCIPSTART上先ping一下接入地址的IP确认网络是否通。4. 华为云IoTDA的产品建模与Topic订阅报文能发出去还不算完云平台这头如果模型建错数据照样上不来。这一章把云侧配置和Topic定义讲透。4.1 产品创建与自定义模型的意义登录华为云控制台搜索IoTDA进入设备接入服务。开通后总览页能看到MQTT接入地址和端口1883是明文端口单片机加载不了证书就走1883。创建产品时设备类型选自定义类型然后在产品详情的模型定义里添加属性和命令。属性是设备上报的数据比如温度、TDS、水位命令是下发给设备的控制指令比如开关水泵、设置投喂间隔。服务ID属性名数据类型范围WaterQualitytemperatureint0-50WaterQualitytdsint0-3000WaterQualitywaterLevelint0-100FeedControlfeedIntervalint1-1440FeedControlmanualFeedstringON/OFF属性名必须和STM32上报JSON里的key完全一致大小写敏感这是最常见的数据上不去的原因。4.2 上下行Topic的拼接规则华为云IoTDA的Topic格式是固定的上报属性$oc/devices/{device_id}/sys/properties/report订阅命令$oc/devices/{device_id}/sys/commands/#上报数据用的JSON结构{ services: [ { service_id: WaterQuality, properties: { temperature: 26, tds: 720, waterLevel: 85 } } ] }SUBSCRIBE报文同样要手工拼只是把报文类型改成0x82载荷里放Topic和QoS等级。订阅成功后才能收到云端下发的命令比如App点了手动投喂设备侧会在commands主题收到manualFeed:ON。4.3 定时投喂与定时增氧的定时器实现定时投喂用TIM4产生1分钟基准中断累计到用户设定的间隔就驱动SG90转动一次// TIM4 1分钟中断累计到feedInterval触发投喂 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim htim4) { feedCounter; oxygenCounter; if (feedCounter feedInterval) { SG90_SetAngle(90); // 推饲料 delay_ms(500); SG90_SetAngle(0); // 复位 feedCounter 0; feedCount; // 投喂次数累计 } if (oxygenCounter oxygenInterval) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_SET); delay_ms(30000); // 增氧30秒 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_RESET); oxygenCounter 0; } } }定时器中断里不要放HAL_Delay以外的耗时操作更不要把MQTT发送放进来否则会阻塞整个中断链。投喂次数要存到Flash或者上报云端避免掉电丢失。5. 联调排错与Qt端远程控制的几个技巧前面能跑通这里再说几个实际调起来最卡的点。5.1 常见故障定位表现象可能原因排查方向MQTT连接被拒绝密码签名错误或时间戳过期检查HMAC计算和UTC时间戳数据上报后云平台无记录属性名与模型不一致对比JSON key和模型定义ESP8266频繁掉线保活时间过长或供电不足保活改30秒模块单独供电ADC读数跳变未滤波或参考电压不稳多次平均检查VDDA滤波电容舵机抖动PWM周期不稳确认TIM预分频和ARR正确5.2 Qt端拉数据的接口调用Qt端用华为云提供的应用侧API而不是直接订阅设备消息// Qt中调用IoTDA应用侧API查询设备属性 QNetworkAccessManager *manager new QNetworkAccessManager(this); QNetworkRequest req(QUrl(https://iotda.cn-north-4.myhuaweicloud.com/v5/iot/{project_id}/devices/{device_id}/properties)); req.setRawHeader(X-Auth-Token, token.toUtf8()); req.setHeader(QNetworkRequest::ContentTypeHeader, application/json); QNetworkReply *reply manager-get(req); connect(reply, QNetworkReply::finished, [](){ QByteArray data reply-readAll(); QJsonDocument doc QJsonDocument::fromJson(data); // 解析properties更新UI上的温度、TDS、水位显示 });X-Auth-Token需要通过IAM认证接口获取Token默认24小时过期App里要做自动刷新。手动喂食的下发走POST /v5/iot/{project_id}/devices/{device_id}/commands命令名要和在云平台定义的一致否则设备侧收不到。5.3 上报频率与功耗的取舍传感器数据不是上报越勤越好。水温变化慢30秒一次足够TDS和PH可以1分钟一次水位只在换水时快速上报。上报太频繁既费流量又容易触发云平台流控还可能因为ESP8266长时间工作发热掉线。用属性上报和命令响应分开处理命令响应走即时通道属性走周期通道整个系统的稳定性会明显上一个台阶。最后提醒一句所有阈值参数最好做成云端可下发别硬编码在STM32里否则每次调整都要重新烧录。本文还有配套的精品资源点击获取
返回列表