
简介本资源是一套面向嵌入式开发初学者与农业物联网实践者的完整鸡舍环境监测小程序源码基于STM32微控制器与ONENET云平台实现温湿度、光照等关键参数的采集、上传与远程调控解决传统养殖中人工巡检效率低、响应滞后等问题。压缩包共37个文件93KB含12张PNG界面图、8个JSON配置文件如app.json、project.config.json、7个JS逻辑文件含util.js、页面交互与ONENET通信模块、5个WXSS样式文件及4个WXML模板文件结构清晰覆盖小程序前端STM32端协同开发全链路。已有526人学习下载资源包含可直接运行的小程序工程目录pages/index/mine/logs等标准结构、设备对接说明readme.txt及典型UI组件如shebei.png、guangzhaoqiangdu.png等可视化元素便于快速理解物联网数据流设计与微信小程序工程组织方式。1. 鸡舍环境监测不是“把传感器连上云”就完事——STM32ONENET小程序的闭环必须解决三个硬骨头养鸡场老板老张去年装了套“智能鸡舍”温湿度数据能传到手机但一到夏天中午小程序页面卡住、曲线断点、报警不响最后发现是STM32采集间隔设成1秒ONENET API限流触发小程序前端又没做离线缓存。这暴露了一个典型误区环境监测类物联网项目不是硬件上传、云平台收数、小程序展示的简单拼接而是三端协同的时序系统。本方案聚焦真实鸡舍场景——空间密闭、粉尘多、供电不稳定、网络常波动、需长期无人值守因此必须让STM32端抗干扰采样、ONENET端稳态接入、小程序端带本地兜底逻辑。它适合嵌入式初学者做毕设有完整源码可调试也适合农业IoT工程师复用通信模块和小程序数据绑定模式。核心不在炫技而在让温度超35℃时哪怕WiFi断了2分钟恢复后仍能补传、报警不漏、历史曲线不断档。2. STM32端用HAL库中断环形缓冲区实现抗干扰连续采集鸡舍环境存在强电磁干扰风机启停、照明镇流器、粉尘覆盖传感器探头、供电电压波动尤其使用12V铅酸电池时。若直接用HAL_Delay阻塞式读取DHT22或SHT30极易因总线抖动导致CRC校验失败若用轮询方式查ADC又会错过关键温升拐点。必须采用“硬件触发软件滤波异步上报”三层设计。2.1 硬件选型与引脚规划以STM32F103C8T6为例传感器类型型号接口方式STM32引脚关键约束说明温湿度SHT30I²CPB6/PB7需外接4.7kΩ上拉电阻避免鸡舍高湿导致I²C总线漏电氨气浓度ZE08-CH2OUARTPA9/PA10使用硬件流控RTS/CTS防止风机启动时串口丢帧光照强度BH1750I²CPB6/PB7与SHT30共用I²C总线需在初始化中配置不同地址本地存储AT24C02I²CPB6/PB7存储最近200条异常数据断网时启用提示PB6/PB7复用I²C总线时必须在MX_I2C1_Init()中显式调用HAL_I2C_DeInit(hi2c1)再重初始化否则SHT30与BH1750地址冲突会导致HAL_I2C_Master_Transmit返回HAL_BUSY。2.2 中断驱动的环形缓冲区实现C语言核心代码// 定义环形缓冲区结构体大小为64适配鸡舍10秒级上报周期 #define SENSOR_BUF_SIZE 64 typedef struct { float temp; float humi; uint16_t nh3_ppm; uint16_t lux; uint32_t timestamp; // ms级时间戳用于ONENET时间对齐 } sensor_data_t; sensor_data_t sensor_ring_buf[SENSOR_BUF_SIZE]; volatile uint16_t ring_head 0; volatile uint16_t ring_tail 0; // 定时器中断服务函数TIM31s触发 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM3) { // 1. 读取SHT30非阻塞I²C超时设为10ms if (HAL_I2C_Master_Transmit(hi2c1, SHT30_ADDR1, cmd_measure, 2, 10) HAL_OK) { HAL_Delay(15); // SHT30测量需15ms if (HAL_I2C_Master_Receive(hi2c1, SHT30_ADDR1, rx_buffer, 6, 10) HAL_OK) { sensor_data_t new_data { .temp sht30_parse_temp(rx_buffer), .humi sht30_parse_humi(rx_buffer3), .nh3_ppm read_nh3_uart(), // 从ZE08-CH2O串口缓存读取 .lux bh1750_read_lux(), .timestamp HAL_GetTick() }; // 2. 写入环形缓冲区原子操作 uint16_t next_head (ring_head 1) % SENSOR_BUF_SIZE; if (next_head ! ring_tail) { // 缓冲区未满 sensor_ring_buf[ring_head] new_data; __DMB(); // 内存屏障确保写顺序 ring_head next_head; } } } } }代码逻辑说明HAL_TIM_PeriodElapsedCallback是1秒定时中断不执行耗时操作如网络发送只做数据采集与入队sht30_parse_temp()对原始字节做CRC校验与温度补偿鸡舍高温下SHT30存在±0.3℃偏移需查表修正环形缓冲区使用volatile修饰指针防止编译器优化导致读写错位__DMB()内存屏障指令确保sensor_ring_buf[ring_head] new_data在ring_head next_head之前完成避免多线程中断主循环访问冲突。2.3 ONENET接入前的数据预处理与心跳保活鸡舍WiFi模块ESP8266-01S常因信号弱掉线。STM32不直接处理TCP连接而是通过AT指令交由ESP管理。关键策略是数据不上报则不发心跳上报成功才更新心跳计时器。// 主循环中检查并上报伪代码 while (1) { if (ring_head ! ring_tail) { // 缓冲区有数据 sensor_data_t data sensor_ring_buf[ring_tail]; if (onenet_send_json(data)) { // 封装为JSON{temp:28.5,humi:65.2,time:1712345678} ring_tail (ring_tail 1) % SENSOR_BUF_SIZE; onenet_last_success_tick HAL_GetTick(); // 更新心跳基准 } else if (HAL_GetTick() - onenet_last_success_tick 60000) { // 超过60秒无成功上报强制发心跳包空JSON {} onenet_send_heartbeat(); } } HAL_Delay(100); }参数说明onenet_send_json()内部调用ATCIPSEND...发送必须设置ATCIPMODE0非透传模式否则ESP在WiFi断开时无法返回ERROR心跳包超时阈值设为60秒源于ONENET平台默认设备离线判定时间为90秒留30秒冗余所有AT指令交互需加HAL_Delay(20)防ESP响应延迟实测鸡舍环境下ESP8266-01S冷启动后首条AT响应平均耗时18ms。3. ONENET云平台用APIKey设备影子数据流规则构建稳定数据管道ONENET作为国内成熟物联网平台其优势在于免费设备数、低延时MQTT接入、内置可视化。但新手常踩坑直接用网页版生成的APIKey权限过大、未启用设备影子导致断网数据丢失、数据流未配置单位转换。本方案采用“最小权限APIKey设备影子缓存数据流自动清洗”三重保障。3.1 生成专用APIKey并绑定设备onenet生成apikey实操登录ONENET控制台 → 选择对应产品 → 【开发者中心】→ 【APIKey管理】→ 【创建APIKey】名称chicken_house_stm32_uploader语义化命名便于审计权限仅勾选device_data_write写设备数据、device_status_read读设备在线状态有效期设为365天避免毕业设计期间过期绑定设备选择已创建的设备设备ID格式如678901234不选“所有设备”注意该APIKey仅用于STM32端HTTP POST上传绝不可嵌入小程序前端代码。小程序通过自建后端中转防止APIKey泄露。3.2 设备影子Device Shadow启用与数据同步逻辑ONENET设备影子本质是云端JSON缓存当设备离线时仍可接收写请求上线后自动同步。启用步骤设备详情页 → 【基础信息】→ 【编辑】→ 勾选“启用设备影子”在【数据流管理】中为每个字段temp/humi/nh3/lux设置影子属性数据流名称temperature数据类型float单位℃影子更新策略last_wins最后写入者胜出STM32端HTTP POST示例curl命令验证用curl -X POST \ https://api.heclouds.com/devices/678901234/datapoints \ -H api-key: your_apikey_here \ -H Content-Type: application/json \ -d { datastreams: [ { id: temperature, datapoints: [{value: 28.5, at: 2024-04-05T10:20:30Z}] }, { id: humidity, datapoints: [{value: 65.2, at: 2024-04-05T10:20:30Z}] } ] }关键参数说明at字段必须为ISO8601 UTC时间2024-04-05T10:20:30Z不能用本地时间或毫秒时间戳否则ONENET折线图时序错乱value为浮点数鸡舍温度建议保留1位小数28.5而非28.500000减少JSON体积单次POST最多提交10个datastream本方案将4个传感器分2次提交温湿度一组、氨气光照一组降低单次失败影响面。3.3 数据流规则自动清洗异常值并触发告警鸡舍传感器易受粉尘、冷凝水影响产生毛刺数据如温度突变至80℃。ONENET【数据流规则】可实时过滤规则名称触发条件执行动作应用场景temp_outliertemperature 45temperature 5nh3_high_alertnh3_ppm 20向微信服务号推送模板消息需提前配置氨气超标及时通风data_smoothmoving_average(temperature, 5)生成新数据流temperature_smooth折线图显示更平滑趋势提示moving_average函数需在ONENET规则引擎中启用“高级计算”输入为最近5个点的temperature值输出为均值。该功能免去STM32端计算资源消耗且云端计算结果可直接用于小程序图表。4. 微信小程序端用WXML数据绑定本地缓存离线告警实现可靠交互小程序不是ONENET控制台的皮肤而是面向养殖户的操作终端。需解决三个实际问题1WiFi断开时页面不白屏2历史数据加载慢3报警消息不被微信折叠。本方案采用“双数据源骨架屏服务通知”组合。4.1 WXML数据绑定与骨架屏skeleton loadingindex.wxml中定义动态绑定区域!-- 骨架屏网络加载中显示 -- view wx:if{{loading}} classskeleton view classskeleton-item temp/view view classskeleton-item humi/view view classskeleton-item nh3/view /view !-- 实际数据网络加载完成显示 -- view wx:else view classdata-card text classlabel当前温度/text text classvalue{{currentData.temp}}℃/text text classstatus wx:if{{currentData.temp 32}}⚠️ 高温预警/text /view !-- 其他字段... -- /viewindex.js中数据获取逻辑Page({ data: { loading: true, currentData: { temp: 0, humi: 0, nh3: 0 }, historyChart: null }, onLoad() { this.loadRealTimeData(); this.loadHistoryChart(); }, loadRealTimeData() { // 1. 优先读取本地缓存断网可用 const cache wx.getStorageSync(chicken_cache) || {}; this.setData({ currentData: cache, loading: false }); // 2. 发起网络请求超时10秒 wx.request({ url: https://your-server.com/api/chicken/realtime, timeout: 10000, success: (res) { if (res.data.code 0) { this.setData({ currentData: res.data.data }); wx.setStorageSync(chicken_cache, res.data.data); // 更新缓存 } }, fail: () { // 网络失败保持缓存数据不提示错误养殖户不关心技术细节 } }); } });参数说明wx.setStorageSync存储周期为永久但鸡舍数据时效性要求高缓存有效期设为5分钟在onLoad中增加时间戳判断const cache wx.getStorageSync(chicken_cache); if (cache Date.now() - cache.timestamp 5 * 60 * 1000) { this.setData({ currentData: cache.data }); }timeout: 10000避免小程序白屏等待实测鸡舍WiFi弱信号下HTTP请求常卡在DNS解析阶段。4.2 历史数据图表用ONENET API直连Canvas绘制折线图小程序不依赖第三方图表库如echarts-for-weixin体积大改用原生Canvas绘制轻量折线图。数据源直接调用ONENET REST API需后端代理跨域// 获取ONENET历史数据后端代理接口 wx.request({ url: https://your-server.com/api/onenet/history?dstemperaturestart2024-04-04T00:00:00Zend2024-04-05T00:00:00Z, success: (res) { const points res.data.datapoints.map(p ({ x: new Date(p.at).getTime(), // 转为毫秒时间戳 y: p.value })); this.drawChart(points); // 调用Canvas绘图函数 } });drawChart()核心逻辑简化版drawChart(points) { const query wx.createSelectorQuery(); query.select(#chartCanvas).fields({ node: true, size: true }).exec((res) { const canvas res[0].node; const ctx canvas.getContext(2d); const dpr wx.getSystemInfoSync().pixelRatio; canvas.width res[0].width * dpr; canvas.height res[0].height * dpr; ctx.scale(dpr, dpr); // 绘制坐标轴 ctx.beginPath(); ctx.moveTo(20, 20); ctx.lineTo(20, res[0].height - 20); ctx.lineTo(res[0].width - 20, res[0].height - 20); ctx.stroke(); // 绘制折线points已按时间排序 if (points.length 1) { ctx.beginPath(); ctx.moveTo(20, this.yScale(points[0].y, res[0].height)); points.forEach(p { const x this.xScale(p.x, res[0].width, points[0].x, points[points.length-1].x); const y this.yScale(p.y, res[0].height); ctx.lineTo(x, y); }); ctx.strokeStyle #1890ff; ctx.lineWidth 2; ctx.stroke(); } }); }, xScale(value, width, min, max) { return 20 (value - min) / (max - min) * (width - 40); }, yScale(value, height) { // 温度范围映射5~45℃ → 画面高度20~height-20 return height - 20 - (value - 5) / 40 * (height - 40); }关键技巧dpr设备像素比必须显式处理否则iPhone上图表模糊xScale和yScale函数将原始数据映射到Canvas坐标系温度Y轴固定范围5~45℃避免因数据波动导致图表缩放失真折线图不画网格线减少视觉干扰符合养殖户快速扫视需求。5. 跨端联调与鸡舍现场排错用串口日志ONENET设备日志小程序调试器定位真问题联调不是“各端跑通就行”而是模拟鸡舍真实环境粉尘覆盖传感器、WiFi信号-75dBm、电源电压11.2V。以下为高频问题及验证方法。5.1 STM32端日志输出规范避免printf拖慢系统禁用标准printf改用精简串口日志宏#define LOG_LEVEL_DEBUG 0 #define LOG_LEVEL_INFO 1 #define LOG_LEVEL_WARN 2 #define LOG_LEVEL_ERROR 3 #define LOG_LEVEL LOG_LEVEL_INFO #define LOG(level, fmt, ...) do { \ if (level LOG_LEVEL) { \ char buf[128]; \ int len snprintf(buf, sizeof(buf), [%.3f][%s:%d] fmt \r\n, \ HAL_GetTick()/1000.0f, __FILE__, __LINE__, ##__VA_ARGS__); \ HAL_UART_Transmit(huart1, (uint8_t*)buf, len, 10); \ } \ } while(0) // 使用示例 LOG(LOG_LEVEL_INFO, SHT30 read OK: %.1f℃, data.temp); LOG(LOG_LEVEL_WARN, NH3 sensor timeout, using last value %d, last_nh3);日志分析要点时间戳用HAL_GetTick()/1000.0f秒级避免毫秒数过长占满串口每行日志≤128字节防止UART FIFO溢出鸡舍中ESP8266常抢串口资源LOG_LEVEL在main.h中统一定义发布版本设为LOG_LEVEL_WARN减小代码体积。5.2 ONENET设备日志解读识别网络层问题在ONENET设备详情页 → 【设备日志】重点关注三类记录日志类型正常示例异常表现排查方向HTTP上报POST /devices/678901234/datapoints 200POST ... 429限流或000超时检查APIKey是否被其他程序滥用调整上报间隔≥5秒MQTT连接MQTT connect successMQTT connect failed: -4网络不可达检查ESP8266 AT指令ATCWMODE?是否为1Station模式设备影子Shadow updated: temperature28.5无影子更新日志但HTTP上报成功确认设备影子功能已启用且数据流ID拼写正确区分大小写提示429 Too Many Requests错误出现时立即在STM32端增加上报间隔至10秒并检查是否有其他调试程序如串口助手在循环发送测试数据。5.3 小程序真机调试技巧绕过微信开发工具局限微信开发工具模拟器无法复现鸡舍弱网必须真机调试开启调试手机微信 → 发送#debug到任意聊天 → 扫码进入调试面板网络监控在调试面板【Network】中筛选/api/chicken/查看请求耗时。若TTFB首字节时间3000ms说明后端或ONENET响应慢本地缓存验证飞行模式下打开小程序确认currentData仍显示最新值来自wx.getStorageSync告警消息测试在ONENET【数据流规则】中手动触发nh3_high_alert观察微信服务号是否收到模板消息需提前在小程序后台配置服务号关联。关键验证表鸡舍部署前必测测试项通过标准失败应对措施断网续传WiFi关闭5分钟后恢复小程序历史图表补全断档数据检查STM32环形缓冲区大小是否≥300支持5分钟×60秒高温报警手动修改ONENET中temperature为36℃5秒内小程序顶部弹出红色警告条确认小程序onShow周期性调用loadRealTimeData粉尘干扰用湿布覆盖SHT30探头10秒温度值缓慢下降而非突变至0或NaN检查sht30_parse_temp()中是否加入滑动窗口滤波最后一行技术内容在STM32的main.c中将HAL_TIM_Base_Start_IT(htim3)置于MX_GPIO_Init()之后、MX_I2C1_Init()之前确保定时器中断在I²C初始化完成前就绪避免首次采集丢失。本文还有配套的精品资源点击获取