1. 项目整体思路:为什么用STM32做猪场环境监控
1.1 智慧养猪到底解决了什么问题
先说结论:这套基于STM32的智慧养猪系统,本质上是一个典型的环境监测与自动控制终端。很多人一听到"智慧养猪"就以为是多大的平台工程,其实落到硬件上,核心就三件事:把猪舍里的温湿度、氨气浓度这些关键参数测准,根据阈值自动开关风机和加热设备,再把这些数据推到云端让养殖户用手机随时看。
做这个项目之前,我认真算过一笔账。传统猪舍管理最痛的不是设备贵,而是人不可能24小时守在猪舍里。夜间气温陡降没人调整风机,氨气超标没人发现,等到第二天早上进圈,猪已经开始打喷嚏、食欲下降。养猪这行有个说法:环境控制好了,饲料转化率能差出10%以上。这10%摊到一批猪身上,就是实打实的利润。所以智慧养猪系统不是花架子,它解决的是"人不在现场但环境不能失控"这个刚需。
这个项目非常适合两类人参考。一类是物联网方向的学生,拿它做嵌入式毕业设计,硬件和软件闭环完整,既有STM32底层开发又有云平台对接,评委想看的技术点基本全覆盖。另一类是小型养殖场业主或者农创团队,想低成本搭一套环境监控系统,这个方案零基础也能跟着复现。
1.2 系统架构与数据流向设计
整个系统的数据流很简单,但设计时我特意坚持了一个原则:本地优先,云端其次。也就是说,即使断网、云平台挂了,现场设备也必须按照预设逻辑自动运行。这一点在养殖场景里是生死线,猪舍环境控制不能依赖网络,网络断了风机照常开,加热器照常工作,这才是合格的系统架构。
系统分成三层来设计:
- 感知层:温湿度传感器采集舍内温湿度,氨气传感器采集有害气体浓度,光照传感器判断是否需要补光,这些都是模拟量或数字量直接进STM32的ADC或I2C接口。
- 决策控制层:STM32F103C8T6作为主控,运行采集、滤波、阈值判断、控制输出逻辑。本地OLED屏幕实时显示数据,按键可以修改报警阈值。
- 执行与远程层:继电器模块驱动风机、加热器、水泵和照明;ESP8266模块通过串口与STM32通信,把数据通过MQTT协议上报到云平台,同时接收手机端下发的远程控制指令。
选STM32而不用Arduino或者树莓派,理由其实很现实:Arduino开发快但工业稳定性和接口资源都差点意思,尤其是多路ADC采集加继电器控制,跑起来心里没底;树莓派跑Linux确实强大,但成本高、功耗高,猪舍这种高湿粉尘环境金贵的板子不敢往里放。STM32F103C8T6这颗芯片零售价几块钱,64KB Flash够写完整套逻辑,20KB RAM跑传感器采集和协议栈绰绰有余,而且资料满天飞,遇到问题随便一搜就有答案。更重要的是,做这个项目能真正练到定时器、ADC、I2C、USART、中断这些嵌入式基本功,做完一个项目,这些外设基本都摸透了。
2. 硬件选型与关键电路:避开那些上电就烧板的坑
2.1 主控选型与传感器搭配方案
主控我选用STM32F103C8T6最小系统板,这是国内用得最泛滥也最稳的一颗料。它的资源分配我列一下,方便你对照自己手头的板子规划引脚:
- 12位ADC:采集氨气传感器输出、光照传感器模拟输出,还可以预留一路采集电源电压做监测。
- USART1:预留调试串口,接CH340模块打印日志。
- USART2:接ESP8266通信模块。
- I2C1:接OLED屏幕和数字温湿度传感器。
- I2C2或者软件模拟I2C:接BH1750光照传感器,避免和OLED抢总线。
- 定时器TIM2:做ADC采样定时触发,保证采集频率稳定。
传感器选型这块我踩过不少坑,直接说结论。温度湿度测量建议用SHT30或者DHT22,SHT30是I2C接口、精度高(温度±0.3℃,湿度±2%RH)、长期稳定性好,价格也就几块钱,既然做系统不差这几块,我最后选的是SHT30。DHT11虽然便宜到一两块,但温湿度精度都很差,湿度误差±5%,环境波动大一点数据就跳来跳去,做自动控制很容易误动作,不建议用在正经项目里。
氨气传感器我用的MQ137模块。这类半导体气体传感器输出的是模拟电压,直接接STM32 ADC采集就行,电路最简单。但你要有心理准备,MQ系列有几个脾气:初次上电需要预热24小时以上才稳定,长期使用会有基线漂移,而且对温湿度敏感。所以我的做法是软件里做了基线校准,同时把温湿度数据参与氨气读数的温度补偿,这个后面细说。
光照传感器选BH1750,I2C接口,量程0~65535 lux,直接输出数字光照强度,不用自己算曲线。用于猪舍照明控制很合适,判断白天黑夜自动开关补光灯。
各传感器参数对比和选型理由,我整理了一张表:
| 传感器 | 测量对象 | 接口类型 | 精度/量程 | 选型理由 |
|---|---|---|---|---|
| SHT30 | 温湿度 | I2C | 温度±0.3℃ 湿度±2%RH | 精度高,长期漂移小,自带防凝露能力 |
| DHT22 | 温湿度 | 单总线 | 温度±0.5℃ 湿度±2%RH | 低成本替代方案,单总线协议简单 |
| DHT11 | 温湿度 | 单总线 | 温度±2℃ 湿度±5%RH | 仅适合学习上手验证流程 |
| MQ137 | 氨气浓度 | 模拟量ADC | 量程参考模块手册 | 测量硫化氢/氨气,响应快 |
| BH1750 | 光照强度 | I2C | 0~65535 lux | 数字输出,不用标定 |
2.2 继电器驱动与电源抗干扰设计
继电器控制是这套系统最容易出问题的环节,我建议直接照抄成熟方案:STM32的GPIO输出接光耦隔离器PC817,光耦输出驱动ULN2003或者三极管,再去控制5V继电器线圈。继电器线圈两端必须并联一个1N4007续流二极管,方向反着接,否则继电器断电瞬间的感应电动势会把驱动管击穿,这是新手最容易忽略的细节。
很多人贪省事直接用现成的低电平触发继电器模块,板载三极管都帮你焊好了,光耦也有,插上就能用。但要注意,市面上的继电器模块触发逻辑是分高电平和低电平两种的,买的时候一定看清楚模块丝印。我调试的时候就是因为默认低电平触发,上电瞬间STM32的引脚还没初始化完毕,IO口处于浮空状态,继电器直接咔哒一声吸合了,风机在系统启动的瞬间就转了。这种问题在现场很吓人,解决方法是:程序里第一步就把所有继电器控制引脚初始化为确定的关闭电平,并且在硬件上给控制引脚接一个10K下拉或上拉电阻,保证上电默认态是安全的。
电源部分是整个系统稳定运行的地基。我的方案是:220V交流电进开关电源转24V,24V给风机、加热器等执行器供电。24V再接一个隔离型DC-DC模块降到5V,5V给继电器模块和传感器供电。5V再经AMS1117-3.3V降到3.3V给STM32和ESP8266供电。注意这里有两个关键点:一是5V和3.3V之间最好用LDO,纹波小,ADC采集的数据才稳定;二是传感器供电和MCU供电不要共用一组LDO,MQ137这种传感器工作时电流变化明显,会拉低电压导致单片机复位。
我第一版图省事,直接用USB线5V供电给整个板子,结果继电器一吸合STM32就复位,查了半天发现是供电被拉垮。后来换成独立供电方案,继电器和MCU供电彻底分开,问题才根治。电源这块表我改了三版,最终稳定运行的架构是:220V交流→24V开关电源→隔离DC-DC 5V→AMS1117 3.3V,模数地单点连接。
3. 软件框架与核心控制逻辑:从裸机调度到自动控制策略
3.1 裸机状态机还是RTOS
这套系统的软件规模,说实话不上RTOS完全够用。系统任务也没几个:传感器采集、数据显示、自动控制逻辑、串口通信、看门狗喂狗。用FreeRTOS当然也能跑,但F103C8T6只有20KB RAM,跑一个精简版RTOS后留给用户代码的空间就紧张了,而且任务间同步还要考虑信号量、队列,调试复杂度直接翻倍。我的建议是裸机主循环加定时器时间片调度,逻辑清楚又省资源。
具体做法是这样:SysTick配置成1ms中断,维护一个系统Tick计数器。主循环里根据Tick值轮询各个任务的时间标志位,例如每100ms采集一次传感器,每500ms刷新一次OLED,每200ms执行一次控制逻辑,每50ms处理一次串口接收缓冲区,每1000ms检查一次ESP8266连接状态。这样做的好处是任务之间不会互相阻塞,任何一个任务卡了,其他任务还在跑,现场稳定性比大循环里顺序执行好得多。
我第一版就是典型的大循环:采集传感器—显示—判断继电器—串口收发,所有事排队做。结果串口数据量大一点,采集周期就变长,控制响应变慢,调试时很被动。改成时间片调度之后,整个系统节奏稳定了,问题也变少。如果你后续要加4G模块、加称重传感器、加多个控制策略,再考虑上FreeRTOS也不迟。
3.2 传感器采集与滤波算法
传感器数据最怕的就是毛刺。尤其是猪舍里风机一启动,电磁干扰叠加到模拟量上,ADC读出来的氨气浓度数值跳来跳去,直接用这个数去控制设备,就会造成风机频繁启停。所以软件滤波必须做,我用的是滑动窗口中值滤波加算术平均的复合方案:连续采样10次,排序后去掉最大值和最小值,剩下8个数求平均。这个组合既滤掉了随机尖峰,又保留了真实变化趋势,实测效果非常好。
ADC采集的代码很简单,但有几个细节值得注意。STM32的ADC是12位分辨率,参考电压3.3V,所以电压值和ADC值的换算关系是:电压 = ADC值 × 3.3 / 4095。如果你的VREF引脚没有接高精度基准源,直接使用板载3.3V供电,那么3.3V这一项会有几十毫伏的偏差,对氨气这种人眼看不出来的模拟量影响不大,但对精密测量就不够用了。我实际使用中用万用表实测了板子3.3V的实际值,把这个实际值写进代码里做换算,精度提升很明显。
SHT30的读取用的是I2C接口,调用HAL库的I2C函数,每次读6个字节,前2字节是温度,中间2字节是湿度,最后2字节是CRC校验。一定记得做CRC校验,环境潮湿时I2C总线偶尔会有错码,没有校验的话会出现偶尔一次温度跳到80多度的鬼数据,触发加热器误关闭的严重问题。
BH1750同样走I2C,注意上电后要先发送一次连续高分辨率模式指令,然后等120ms再读数据,别读太快,否则返回的全是0xFF。
MQ137氨气传感器的处理麻烦一点。它输出的是模拟电压,电压越高表示气体浓度越高。但这个传感器有个特点,零点和灵敏度都会随着使用时间漂移。我的处理方法是:系统首次校准在洁净空气环境中,记录此时ADC采到的基线电压;之后定时每天早上4点,当判断氨气浓度稳定且风机未启动时,平滑更新这个基线值。这样能有效补偿长期漂移。至于电压怎么换算成ppm浓度,每个厂家的模块灵敏度不同,最好用标准气体标定,没有条件的话可以参考手册里的灵敏度曲线做近似线性换算,作为控制阈值够用了。
说一下定时器输入捕获。我额外加了一路风速传感器,用的是霍尔风速计,输出频率和风速成正比,用STM32的定时器输入捕获功能测脉冲频率,再换算成风速。这里正好用到了定时器捕获测频率的经典玩法,代码里把TIM2配置成输入捕获模式,上升沿触发,计算两次捕获的时间差取倒数就是频率。做这个功能的时候顺便把定时器中断、捕获比较这些知识点巩固了一遍。
3.3 自动化控制策略:滞回控制、联动与告警
自动控制策略是整个智慧养猪系统的大脑,但别把它想得太复杂,核心就一个词:滞回控制。所谓滞回,就是给开关动作设定一个差值区间,避免设备在阈值附近来回翻转。
举个例子,假设猪舍温度超过28℃需要开风机降温,低于25℃需要关风机。如果不做滞回,温度到27.99℃风机启动,吹一会儿降到28.01℃,因为仍然超过28℃,风机不会停,那还好;但如果策略写的是超过28℃开、低于28℃关,那么温度在28℃上下波动时,风机会疯狂地开了关关了开,继电器触点寿命急剧下降,电机也受不了。所以我的策略是:温度大于28.5℃开风机,降到25.5℃以下才会关风机,中间这3℃就是滞回区间。
各环境参数的联动策略我整理成了表格,直接可以在代码里实现:
| 控制对象 | 开启条件 | 关闭条件 | 优先级 |
|---|---|---|---|
| 降温风机 | 温度>28.5℃ 或 氨气>15ppm | 温度<25.5℃ 且 氨气<8ppm | 高 |
| 加热器 | 温度<18℃ 且 湿度非极端高 | 温度>21℃ | 中 |
| 除湿风机 | 湿度>75%RH 且 温度>15℃ | 湿度<65%RH | 中 |
| 补光灯 | 光照<500 lux 且 时段为白天 | 光照>2000 lux 或 夜间定时关闭 | 低 |
| 报警器 | 温度>32℃ 持续30秒 或 氨气>25ppm 持续10秒 | 条件解除并持续60秒 | 最高 |
注意我把所有报警条件都加了持续时间判断,这是我从实际教训里总结的。最开始报警逻辑是温度一超32℃就响,结果中午太阳照到传感器上,温度瞬间冲到33℃,蜂鸣器猛烈报警,其实是误报。加了持续30秒才算数的逻辑之后,误报率基本归零。这种防抖思想在处理所有现场信号时都通用,不是只有按键消抖才用。
定时饲喂功能我用了两个方案。简单方案是直接定时控制下料电机,比如早上7点、中午12点、下午6点各转10秒,靠时间来控制。进阶方案是加HX711称重传感器,每次下料前称一下料斗的重量,下料后称一下重量差,把实际下料量反馈到云端,做精准饲喂。HX711是24位ADC芯片,通过串口协议和STM32通信,网上资料很多,硬件连接就四根线,做起来不难。
4. 远程监控与数据上云:把猪舍环境送到手机里
4.1 通信模块选型与MQTT基础
远程通信这层,我选的是ESP8266模块。这颗Wi-Fi芯片被国内开发者玩出了花,资料多到看不完,十几块钱一片,作为STM32的通信搭档再合适不过。我手头有ESP-01S和ESP-12F两种模块,最终用的是ESP-12F,因为它的引脚更多、天线性能更好、Flash更大,跑AT固件更稳。
STM32和ESP8266的连接很简单:STM32的USART2 TX接ESP8266的RXD,STM32的USART2 RX接ESP8266的TXD,共地,波特率我设115200。注意ESP8266是3.3V电平,绝对不能直接接5V的TXD,否则模块必烧。如果你的STM32板子是5V供电,串口TX需要加一个分压电阻或者用电平转换芯片。
MQTT协议是现在的物联网标配,我拿微信群聊做个类比。服务器就是一个聊天群,设备往群里发消息叫发布,设备订阅某个话题后,群里一旦有相关消息就能收到,这叫订阅。每个消息都有主题,比如"猪舍1号/环境数据",设备端发布到这个主题,手机端订阅这个主题,数据自然就通了。MQTT最厉害的是消息质量等级和遗嘱机制,设备断线时服务器能感知到,这对远程监控来说太重要了。
4.2 STM32端AT指令连接与数据上报实操
我用的ESP8266固件是支持MQTT透传的AT固件,连接云平台的流程大致是:先设Wi-Fi模式、连接路由器、然后配置MQTT参数、建立连接、订阅主题、循环发布消息。AT指令序列我贴出来,你照着串口调试助手验证一遍再往STM32里写:
AT // 测试模块是否响应 AT+CWMODE=1 // 设置为Station模式 AT+CWJAP="你的WiFi名","你的WiFi密码" // 连接路由器 AT+MQTTUSERCFG=0,1,"ClientID","用户名","密码",0,0,"" // 配置MQTT参数 AT+MQTTCONN=0,"bemfa.com",9501,1 // 连接巴法云MQTT服务器 AT+MQTTSUB=0,"设备主题",1 // 订阅控制主题 AT+MQTTPUB=0,"设备主题","{\"temp\":\"25.6\"}",1,0 // 发布环境数据不同厂家固件的AT指令格式有差异,上面是我手头这个固件的写法,你拿到模块后先用串口助手一条条试,确认每条指令都返回OK了再写进STM32程序。
数据上报的格式我用的是最轻量的JSON字符串。STM32端没有用cJSON库,因为场景简单,几个字段手拼字符串就够了,嵌入式环境能少一个依赖就少一个。举个例子,上报的环境数据长这样:
{"temp":"25.6","hum":"68.5","nh3":"8.2","lux":"1200","fan":"ON","heat":"OFF","feed":"0"}手机端云平台App显示的就是这个JSON解析后的字段。提醒一句,字符串拼接时别忘在数据前后加转义字符,我一开始调试时JSON格式不对,云端一直显示离线,后来串口打了原始数据才发现是引号没处理好。
断线重连机制是整个远程模块的重点。猪舍环境Wi-Fi信号不稳定是常态,路由器和ESP8266偶发断连太正常了。我的处理逻辑是:STM32每10秒查询一次ESP8266的MQTT连接状态,如果断开就重新走一遍连接流程,先连Wi-Fi再连MQTT。同时开启STM32独立看门狗IWDG,主循环里喂狗,一旦程序卡死在某个阻塞环节,看门狗强制复位整个系统,复位后再自动重连。这套组合拳做完,系统在无人干预的情况下运行几个月不重启完全没问题。
5. 现场调试与常见问题排查实录
5.1 传感器数据异常排查
现场调试遇到最多的问题是传感器数据不对。我把几个典型的坑整理成表,基本覆盖了大多数情况:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| SHT30偶尔读出0xFF | I2C总线被干扰或上拉电阻不足 | 检查I2C上拉电阻是否4.7K,读数据加CRC校验 |
| 温度正常湿度一直99% | 传感器敏感头被水汽凝结或损坏 | 用热风枪吹干传感器,检查外壳防水透气膜 |
| 氨气数值开机就满量程 | 传感器未预热完成或已到期老化 | 初次使用预热24小时,记录基线电压判断 |
| ADC数值跳变剧烈 | 电源纹波大或地线回路不合理 | 改用LDO供电,模拟地与数字地单点连接 |
| BH1750读光照一直是0 | 地址错误或模式未设置 | 确认ADDR引脚电平对应地址,连续高分辨率模式必须等待120ms |
最让我头疼的是氨气传感器稳定性的问题。刚开始系统跑着跑着,氨气数值会缓慢爬升,过几天就到了报警值。我以为是传感器坏了,后来才意识到是传感器自身的基线漂移。环境湿度变大、温度升高都会影响半导体传感器的输出。解决的办法就是我前面说的每日基线自校准,系统每天凌晨自动记录一个基线值,把这个基线作为当天的零点参考,漂移问题大大缓解。
另外提醒一句,任何传感器在猪舍这种高粉尘环境下都要加防护。我给传感器套了透气防尘帽,定期拆下来用酒精清洁,数据稳定性和传感器寿命提升明显。传感器是易耗品,现场多备两套总能用到。
5.2 通信掉线、设备重启与大坑实录
远程通信掉线的问题排查起来其实有套路。如果ESP8266一直连不上路由器,先用手机开热点对比测试,能连上说明是现场路由器问题,路由器换个位置或者加个信号中继就能解决。如果模块能连上Wi-Fi但连不上MQTT服务器,大概率是服务器参数配错了,用串口助手打印模块返回的ERROR信息,对照AT指令手册逐条排查。
我在现场调试时遇到过两次比较隐蔽的问题。第一次是继电器动作的瞬间,整个系统直接重启。当时抓破脑袋想了很久,最后用示波器量电源轨才发现,继电器吸合瞬间在5V电源上产生了一个很大的电压跌落波形,持续时间虽然只有几十毫秒,但足以让单片机掉电复位。解决方案是:把继电器模块供电改为独立的5V隔离电源,同时STM32的3.3V电源输入端并一个470uF电解电容和100nF瓷片电容做储能缓冲。改完后再也没出现过继电器动作导致重启的问题。
第二次是ESP8266运行一两天之后莫名卡死,Wi-Fi断开后重连不上。排查到最后是ESP8266模块自身供电不足。ESP8266在发送数据瞬间电流峰值高达300mA,如果供电电流不够,模块会随机死机。我开始用AMS1117-3.3V给ESP8266供电根本扛不住,后来改成专门的3.3V大电流稳压模块,问题彻底解决。所有ESP8266项目都建议直接上5V→3.3V的DC-DC降压模块,至少输出1A。
5.3 猪舍环境给嵌入式开发者的施工建议
最后聊几个现场施工的经验,这部分在实验室里永远学不到。
防水防潮是第一优先级。猪舍内湿度常年70%以上,冲洗猪圈时水枪直接喷到控制箱附近。我的做法是:所有电路板正反面涂三防漆,传感器接线端子打热熔胶固定,控制箱用带密封条的防水箱,进出线用防水接头加线槽。涂三防漆之前先确认所有调试工作结束,涂完就不能再焊线路了。
防鼠防猪拱是第二优先级。猪对新奇的东西充满好奇,拱一下控制箱、咬一下线缆都是常事。所有外露线缆必须穿PVC线槽或者金属软管,控制箱固定在猪够不着、水溅不到的墙壁高位。继电器线束和传感器线束分开走,避免强电电磁干扰耦合到信号线上。
电子元器件的温度等级也要注意。我批量采购时就犯过只买民用级元器件的错误。猪舍夏天的温度加上设备自身发热,控制箱内部温度能到50℃以上,民用级芯片的标称工作温度上限一般是70℃,看着够用,但长期在温度临界边缘运行,寿命衰减很严重。所以控制箱内部加了一个风扇,温度超过45℃自动启动排风,元器件尽量选工业级,贵不了多少,稳定性完全不是一个档次。
调试工具方面,强烈建议在STM32程序里预留一套串口命令调试接口,平时用USART1输出分级日志:LOG_INFO打印常规状态,LOG_DEBUG打印传感器原始ADC值,LOG_ERROR打印异常事件。现场排查问题的时候,一根串口线和手机串口助手App就能看到系统内部状态,比带着笔记本到处跑方便太多。
资料这块,我把全套工程文件整理好了:完整原理图、PCB文件、STM32源码工程(标准库版)、ESP8266 AT指令配置文档、巴法云平台接入配置说明、外壳3D打印图纸都在里面。需要参考的读者在评论区给我留言或者私信我,我看到都会发。做这个项目的过程中我自己也踩了不少坑,我把这些经验全部写进了资料里的调试笔记,希望能帮你少走弯路。