做嵌入式这些年,陆陆续续攒了不少小项目,但真正让我觉得“拿出来分享也不丢人”的,还是这套智能宠物喂食系统。当时做它的初衷很朴素:我经常出差,家里那只猫跟着我挨饿,市面上能定时出粮的喂食器要么贵得离谱,要么逻辑死板——出粮量固定、没有余量提示、远程功能还要订阅云端服务。所以我就想,干脆用STM32自己造一个,顺便把完整的代码、原理图和仿真工程都整理出来,放到开源社区里。如果你正在找入门STM32的实战项目,或者家里也有毛孩子需要照顾伙食,这套资料可以直接拿去做复刻,也能当课程设计、毕业设计的底子。文章里我会把原理图怎么画、代码怎么组织、仿真怎么跑通,以及我踩过的坑,一次性讲清楚。
1. 功能定位与整体方案选择
1.1 我需要一台怎样的喂食器
动手之前,我先把自己对“智能喂食”的真实需求列成了清单,只有需求明确,后面选型才不会跑偏。我的预期功能是:第一,能设定每天多个喂食时段,每顿的粮食重量可以单独调整;第二,出粮之后能知道储粮桶还剩多少,粮快没了要能提示我;第三,最好有一块屏幕能直接看到下次喂食倒计时和设备状态;第四,能手动补粮,按一下按钮就出一份粮,方便加餐。至于手机App远程控制,我把它列为“可以有但不是必须”,为了控制复杂度,第一版用WiFi模块上报状态即可,不强求云端双向控制。
这样梳理下来,系统的核心模块就很清晰了:主控、出粮机构、称重模块、人机交互、时钟和通信。出粮机构最常用的是螺旋推进式或者转盘式,我选了步进电机驱动的螺旋出粮结构,因为它出粮量可控、不容易卡粮,而且零件在市面上很常见。称重放在储粮桶底部,用应变片式压力传感器加HX711采集,精度能做到克级,完全够用。人机交互用OLED屏幕加三个按键,不用触摸屏,因为触摸屏在嵌入式小项目里成本和可靠性都不划算。
1.2 为什么选STM32F103C8T6而不是更便宜的国产替代
主控选型上,我一开始也犹豫过要不要用APM32或者GD32这类可以直接兼容STM32引脚的国产芯片,毕竟价格确实有优势。但考虑到这是一个要开源出去、希望别人也能顺利复刻的项目,我最终选择了STM32F103C8T6。原因很实在:这颗芯片的参考资料最多,遇到问题随便一搜就有解决方案;Keil工程模板和标准外设库、HAL库的例程铺天盖地;烧录用ST-Link或者串口ISP都方便,板子也便宜。等你自己玩熟了,想换GD32还是APM32,基本就是改一下宏定义和启动文件的事,但这颗芯片能帮你把项目的“入门门槛”降到最低。
性能上,72MHz主频、64KB Flash、20KB RAM,跑这个项目绰绰有余。我用的是HAL库开发,虽然没有标准外设库那么“贴近寄存器”,但胜在可读性好,而且HAL库在定时器、I2C、ADC这些外设的初始化上封装得很统一,新手对着代码和参考手册能少走很多弯路。时钟树配置成72MHz主频,外部8MHz晶振,这些在代码里都有注释,后面我会详细讲。
1.3 系统架构图景与数据流
整套系统的数据流可以从两个方向来理解。向下的方向是“执行”:STM32根据内部RTC时间判断是否到达喂食时刻,到达后通过定时器输出PWM脉冲驱动步进电机,电机每转一圈,螺旋机构就推出一定量的粮食;同时HX711不断采样储粮桶重量,用于闭环校验本次出粮量是否达标。向上的方向是“状态回传”:OLED实时显示重量、时间、喂食倒计时,ESP8266模块通过串口把状态数据发送到局域网,我用手机上的串口调试助手或者简单的上位机就能看到。
为了让逻辑清晰,我在代码里用了状态机来管理整个设备的工作流程。状态包括:待机、手动出粮、定时出粮、称重校准、参数设置。每个状态之间的转换都由按键事件、定时器中断或者RTC闹钟事件触发。这种设计的好处是,你不会在中断里写一堆乱七八糟的业务逻辑,主循环只管按当前状态去执行对应动作,排查问题的时候思路非常清楚。
2. 硬件设计与原理图核心细节
2.1 最小系统板的电路设计
很多新手喜欢直接买一块现成的“最小系统板”来插杜邦线搭电路,这当然可以,但作为开源项目,我还是把完整的原理图设计了出来,这样大家可以自己画PCB或者按图飞线。原理图的核心首先是电源部分:系统输入是5V USB或者DC头,经过AMS1117-3.3稳压给STM32和传感器供电。这里有个细节,HX711和步进电机驱动(我用了ULN2003)对电源纹波比较敏感,所以模拟部分和数字部分我做了单点接地,并且在每个芯片的电源引脚旁边都放了0.1uF去耦电容。原理图里这些电容不是随便画的,布局时要尽量靠近电源引脚,否则高频噪声会直接从PCB走线串进芯片。
晶振电路是另一个容易踩坑的地方。STM32F103C8T6的主晶振我用8MHz,两个20pF负载电容。电容值不是随便选的,它要根据晶振的负载电容参数计算。我用的这颗晶振标称负载电容是18pF,理论上C = 2 × CL - Cstray,如果Cstray大概按4pF估算,那么C = 2×18 - 4 = 32pF。不过实际板上走线短、寄生电容小,我实测用20pF起振非常稳定,如果你按公式算出来偏大导致不起振,可以适当减小。还有复位电路,10k上拉电阻加100nF电容到地,这是官方推荐的经典接法,NRST引脚低电平复位。
2.2 出粮机构与步进电机驱动
步进电机我选的是28BYJ-48,配ULN2003驱动板。这颗电机是5V的,四相五线,减速比1/64,步距角5.625/64度。也就是说,要让输出轴转一圈,需要给驱动板发 64 × 64 = 4096 个步进脉冲。我一开始想用“转多少圈”来控制出粮重量,后来发现粮食的密度和颗粒大小会影响每次出粮量,单纯靠圈数不准。所以我改成了“定时喂食”:到点后电机启动,每转一圈就暂停200毫秒,让粮食从出料口稳定落下,同时HX711实时读取重量变化,达到目标重量就立即停止。这种闭环方式比开环转圈可靠得多。
ULN2003驱动板的输入引脚直接接STM32的四个GPIO,控制时序就是标准的四相八拍:A、AB、B、BC、C、CD、D、DA。代码里我用一个查表数组来输出这八个状态,每次状态切换间隔1.8毫秒左右,这样电机转动比较平滑。要注意的是,28BYJ-48虽然标称5V,但堵转时电流不小,如果直接由STM32的3.3V引脚供电会带不动,必须从5V电源取电,而且驱动板的GND要和STM32的GND共地。
2.3 称重模块与HX711接线
称重部分的核心是HX711,这是一颗24位高精度A/D转换芯片,专门用于桥式传感器。它和STM32的通信只需要两根线:PD_SCK和DOUT。HX711有两个输入通道,A通道增益128,B通道增益64,我接的是A通道。传感器是铝制悬臂梁式压力传感器,量程5kg,红黑绿白四根线分别接HX711的E+、E-、A+、A-。接线顺序不能错,错了读数就是负的或者乱跳。
硬件上需要注意给HX711的AVDD供电,我用了5V,这样参考电压是5V,传感器激励电压也是5V。HX711的速率引脚RATE我直接接GND,选择默认的10Hz采样率,对喂食这种慢变化量足够用了。芯片的DOUT在数据准备好时会拉低,MCU检测到这个下降沿后,再给PD_SCK提供25个时钟脉冲,前24个是数据位,第25个把通道和增益切回A通道128。代码里我封装了一个HX711_Read函数,用GPIO模拟时序,不依赖硬件SPI,这样改到其他板子上也能直接用。
2.4 OLED、按键与ESP8266通信接口
显示部分我用的是0.96寸I2C接口OLED,SSD1306驱动。I2C的SDA和SCL分别接PB7和PB6(这是I2C1的默认引脚),记得要接两个4.7k上拉电阻,虽然OLED模块上通常自带了,但如果你自己画原理图,千万别漏。OLED的I2C地址一般是0x3C,代码里可以扫描确认,后面我会提这个坑。
三个按键我采用“一个ADC引脚+三个不同分压电阻”的方式,这样能省IO。原理图上,三个按键分别把不同的电阻接到地,MCU通过读取ADC值判断是哪个按键被按下。比如按键1对应1k电阻,按键2对应2.2k,按键3对应4.7k,ADC读到的电压就各不相同。这种方式比直接占用三个GPIO要省引脚,但ADC参考电压要稳定,所以我用了3.3V作为参考。如果你觉得ADC按键在工程上不够稳妥,改成三个独立GPIO加内部上拉也一样,代码里我都做了兼容。
ESP8266模块通过USART1连接,PA9(TX)接模块的RX,PA10(RX)接模块的TX,波特率115200。模块供电需要稳定3.3V,但ESP8266在WiFi发射瞬间电流会到300mA左右,最好用单独的LDO给模块供电,不要和STM32共用一颗AMS1117,否则电压跌落会导致系统重启。我在原理图里给ESP8266单独画了一路3.3V,这个细节很多人忽略,实际调试时会非常头疼。
3. 软件框架与关键代码实现
3.1 工程结构与任务划分
软件我基于Keil MDK + HAL库开发,整个工程按功能模块拆分了文件:main.c负责初始化和主循环;motor.c封装步进电机驱动;hx711.c封装称重读取和校准;oled.c负责屏幕显示;key.c处理按键扫描;rtc.c管理时间和闹钟;wifi.c负责ESP8266的数据上报;storage.c用内部Flash保存喂食参数。这样分文件的好处是每个模块可以单独测试,比如你不想用WiFi,直接把wifi.c从工程里拿掉,其他地方都不受影响。
主循环的逻辑其实不复杂。我设了一个全局结构体SystemState来保存设备状态,包含当前时间、储粮重量、目标出粮量、电机状态、工作模式等。每隔10毫秒,定时器中断会置一个标志位,主循环检测到标志位后依次执行按键扫描、屏幕刷新、串口数据接收和WiFi状态上报。喂食判断放在主循环里,通过RTC的当前时间与预设的喂食时间表做匹配,匹配成功就触发一次FeedOnce流程。这里有个关键点,喂食时间表要支持“同一天多次投喂”,我定义了一个长度为4的数组,每个元素包含小时、分钟、出粮克数,代码里依次比对就行。
3.2 步进电机驱动的核心代码
步进电机的八拍控制我用了一个静态数组,加上一个索引变量。来看下这段核心代码:
const uint8_t MotorStepTable[8] = { 0x09, 0x08, 0x0C, 0x04, 0x06, 0x02, 0x03, 0x01 }; void Motor_TurnStep(uint8_t step, uint8_t direction) { static uint8_t index = 0; if (direction == 1) { index = (index + 1) % 8; } else { index = (index + 7) % 8; } uint8_t val = MotorStepTable[index]; HAL_GPIO_WritePin(MOTOR_IN1_GPIO_Port, MOTOR_IN1_Pin, (val & 0x08) ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(MOTOR_IN2_GPIO_Port, MOTOR_IN2_Pin, (val & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(MOTOR_IN3_GPIO_Port, MOTOR_IN3_Pin, (val & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(MOTOR_IN4_GPIO_Port, MOTOR_IN4_Pin, (val & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_Delay(2); }每次调用Motor_TurnStep只走一个步进脉冲,方向通过direction来控制。为什么要用表驱动而不是挨个写四个GPIO?因为八拍时序里每一拍都是特定的引脚组合,用位运算和查表可以把代码写得很紧凑,而且如果你以后换电机,只需要改表里的值就行。我在喂食函数里会循环调用这个函数多次,然后定期检查HX711的重量,达到目标就退出循环。这个期间不能让其他逻辑打断,所以喂食流程要放在主循环的一个大状态里执行,不能在定时器中断里去跑。
3.3 HX711称重读取与校准算法
HX711读取时序比较固定,我用软件模拟,代码看起来是这样:
uint32_t HX711_Read(void) { uint32_t count = 0; uint8_t i; HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_RESET); while (HAL_GPIO_ReadPin(HX711_DOUT_GPIO_Port, HX711_DOUT_Pin) == GPIO_PIN_SET); for (i = 0; i < 24; i++) { HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_SET); count = count << 1; HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_RESET); if (HAL_GPIO_ReadPin(HX711_DOUT_GPIO_Port, HX711_DOUT_Pin) == GPIO_PIN_SET) { count++; } } HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_SET); count = count ^ 0x800000; HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_RESET); return count; }读取出来的24位数据是补码形式,所以要用异或0x800000把最高位反转成无符号数,方便后面做换算。原始读数要变成克数,需要先校准。校准方法很粗暴但有效:空桶时读取一个基准值,放上已知重量的砝码(比如100g)再读一个值,两次差值除以100就得到“每个ADC单位对应的克数”,我称之为scale。校准参数保存在Flash里,掉电不丢失。
float HX711_GetWeight_gram(void) { uint32_t raw = HX711_Read(); return ((float)raw - offset) / scale; }其中offset是空载时读到的原始值,scale是每克对应的原始值差。这里有个细节:HX711读取需要连续采样几次取平均,因为传感器本身会有细微震动和噪声,我在实际代码里是把10次读数的平均值作为一次有效值,这样显示出来的重量才稳定。
3.4 RTC、Flash参数存储和时间校准
STM32F103内部有RTC,但它本质上是一个计数器和备份寄存器,掉电后靠VBAT引脚供电保存。原理图里我在VBAT接了一个CR1220纽扣电池座和一个小二极管防止倒灌。RTC初始化代码里,我配置了外部低速晶振LSE为32.768kHz,这样计时精度比较高。在系统启动时,如果RTC还没有初始化过(可以通过判断备份寄存器的标志位),就要求用户通过按键设置当前时间;如果已经初始化过,就直接读取并恢复系统时间。
喂食时间表和校准参数我是通过Flash的最后一个扇区来保存的,STM32F103C8T6的Flash按页擦除,每页1KB,我使用的是最后一页。保存前要先把该页擦除,再写入一个结构体数据。结构体里我放了四个喂食时间段、校准参数和一组魔数,魔数用来判断Flash数据是否有效。代码封装了Storage_Save和Storage_Load两个函数,内部用HAL_FLASHEx_Erase和HAL_FLASH_Program来完成写入。如果你的板子没有纽扣电池,RTC每次上电都要重新校准,但只要喂食是通过网络时间同步或者按键设置,影响也不大。
3.5 ESP8266的串口协议与数据上报
ESP8266我刷的是AT固件,STM32通过串口发送AT指令控制。既然只做状态上报,流程很简单:初始化时发送“AT+CWMODE=1”设置成Station模式,然后“AT+CWJAP=你的WiFi名,密码”联网,联网成功后建立TCP客户端连接,我写了一个简单的上位机在PC上监听端口,ESP8266往这个端口发JSON字符串。数据格式是一串JSON,比如:
{"time":"12:30:00","weight":1250,"next_feed":"18:00:00","mode":"auto"}这里要注意,ESP8266的AT固件匹配串口波特率后,MCU发送的每条命令必须以回车换行结尾。我封装了ESP8266_SendCommand函数,先发指令,然后等待模块返回“OK”或者“ERROR”,超时时间设置为2秒。如果你发现模块没反应,别急着怀疑代码,先确认是不是固件版本太老或者模块波特率不对,后面排查章节我再细讲。
4. 仿真环境搭建与联调
4.1 用Wokwi还是Proteus
这个项目我同时放了Proteus仿真文件和Wokwi在线工程的链接,原因很简单:两者各有适用场景。Proteus可以仿真完整的电路,包括STM32、OLED、按键、电机驱动和传感器,但是HX711和ESP8266在Proteus里没有现成的模型,你得用虚拟串口和模拟器件去替代,所以适合做“电路逻辑验证”。Wokwi则是在浏览器里在线跑代码,它有专门的STM32F103C8T6模板,还可以挂载SSD1306 OLED、按键、LED等外设,更方便做软件逻辑的快速验证,只是它对步进电机和HX711这种复杂外设的仿真精度有限。
我自己的建议是:先用Wokwi把主状态机跑通,确认按键、OLED、时间调度这部分逻辑没问题;再用Proteus把所有电路连接好,验证硬件电气关系,比如电源网络、复位和晶振是否正常;最后在实际板子上联调称重和电机,因为这两个环节涉及物理量,仿真永远替代不了真实环境。
4.2 Proteus仿真要点
在Proteus里搭建这个项目,有几个容易让人抓狂的细节。首先是STM32F103C8T6的晶振配置,Proteus在仿真时不一定需要真实接8MHz晶振,默认用的就是内部RC,只要代码里确保RCC配置成HSE时能正常启动就行。我工程里的配置是预分频器PLL倍频到72MHz,在仿真里选“Use internal RC”反而更省事。然后是OLED模型的加载,Proteus从8.10版本开始支持SSD1306模型的直接放置,但你要注意I2C模型的总线地址要和代码一致,通常是0x3C。如果显示不出来,先查I2C时序和地址是不是匹配。
步进电机驱动在Proteus中可以用通用的STEPPER模型替代,但它的步距角和真实的28BYJ-48不一样,所以仿真里不能直接用“电机转几圈等于多少克”这种物理结论,只能验证“电机确实转了、转向正确、脉冲时序正常”。我在仿真工程里加了一组虚拟示波器探针,放在PD_SCK和DOUT引脚,用来观察HX711的时序,这样可以快速判断代码里模拟时序是不是卡住了。顺便说一句,Proteus里没有HX711模型时,我是用一个可调电阻分压模拟传感器输出,配合虚拟电压表来看ADC读数的变化,这个思路在嵌入式仿真里非常实用。
4.3 Wokwi工程的结构和用法
Wokwi在线仿真是我近几年很喜欢用的工具,这个项目里我也放了一个可以直接打开的Wokwi工程链接。Wokwi的工程文件是diagram.json,里面定义了芯片型号和连接的外设,代码则直接放在main.c里。用Wokwi调试这个项目的最大好处是,你不需要买任何硬件就能先看OLED显示效果,而且Wokwi内置了逻辑分析仪,可以抓取GPIO波形,对调试I2C和PWM时序特别有用。
我提供的Wokwi工程里,用到了STM32F103C8T6、SSD1306 OLED、三个按键(接ADC分压)、LED模拟电机运行状态,以及一个虚拟串口用来接ESP8266的AT命令输出。代码和真实板子共用同一套源文件,只做了少量条件编译。比如真实板子用HX711读取重量,Wokwi里没有HX711模型,我就用一个随机数模拟重量变化,并在代码里用宏定义切换:
#ifdef WOKWI_SIMULATION uint16_t sim_weight = 1150 + (HAL_GetTick() / 1000) % 50; #else uint16_t sim_weight = HX711_GetWeight_gram(); #endif这种条件编译的思路,能让你在仿真和真机之间无缝切换,也方便别人拿到代码后先在浏览器里跑通一遍再动手焊板子。
5. 常见问题与排查技巧实录
5.1 电机不转或抖动
这个绝对是问答区出现频率最高的问题。电机不转,先别怀疑代码,按照这个顺序排查:第一步,确认驱动板的电源指示灯亮不亮,ULN2003驱动板必须接5V,而不是3.3V;第二步,确认STM32的GND和驱动板的GND是不是共地,没共地的话信号根本没有参考电平,电机当然不动;第三步,用示波器或者逻辑分析仪看四个IN引脚有没有脉冲输出,如果有脉冲但电机不动,说明是电机线序接错或者驱动板坏了。很多新手会把IN1-IN4对应到电机的四根线时接错顺序,导致电机只是“嗡嗡”响但不转,这个可以通过调换中间两根线来尝试。
如果电机会动但抖动严重,多半是步进脉冲间隔太短,电机跟不上。我把延时调到2毫秒一拍,也就是500Hz的步进频率,对28BYJ-48来说比较稳妥。你把延时调大一点试试,如果好了,就是频率太高。
5.2 HX711读数一直为0或乱跳
这个问题也很典型。HX711读数一直为0,最常见原因是DOUT引脚一直为高,说明传感器根本没有数据输出。先查E+和E-有没有给传感器供电,用万用表量HX711模块上的E+和E-之间应该有5V电压。如果没有,检查AVDD是不是没接或者焊短路了。如果E+和E-电压正常但DOUT还是高,那就是接线顺序错了,把传感器的红黑线对调试试,或者检查A+、A-是不是接反了。
读数乱跳的根源通常是电源纹波大或者传感器固定不稳。注意HX711的模拟电源和数字电源要分开滤波,我在原理图里给AVDD加了一个10uF钽电容和一个0.1uF陶瓷电容,实测改善明显。另外传感器本身要固定在储粮桶底部,不能悬空,否则粮食落下时整个桶都在晃,读数自然没法稳定。
5.3 OLED无显示但不卡死
OLED不显示,你可能会觉得是代码卡在I2C等待上了,但更多时候是地址不对或者SDA/SCL接反。先用I2C扫描程序确认设备地址,如果扫描不到设备,大概率是接线错误。我自己的板子曾经出现这种情况:OLED模块上电瞬间正常,但过几秒就黑屏,后来发现是I2C总线上没有上拉电阻,信号不稳定,加上4.7k上拉后解决。如果你用的是自制的OLED转接板,这个问题尤其常见。
5.4 ESP8266模块不响应或频繁重启
ESP8266不响应,先确认模块供电是不是单独一路,很多情况下是因为供电压降导致模块反复复位,串口助手能看到“ready”出现很多次。另外,AT指令的波特率要和模块固件匹配,老版本固件可能是9600,新版本是115200。我代码里用115200,如果你的模块之前被设置过其他波特率,可以用串口工具先恢复默认。
还有一个小坑,ESP8266的TX和RX是3.3V电平,不能直接接5V单片机,但STM32是3.3V设备,所以可以直接连。不过ESP8266的TX引脚在发送时会产生尖峰,我建议在RX线串联一个1k电阻,保护STM32的引脚,这个我在原理图里也画了。
5.5 RTC时间每次上电都回到初始值
如果你没有接VBAT电池,RTC在上电后自然就丢了时间,这是正常的。但我遇到过一种情况:接了电池,时间还是会丢。排查后发现是复位电路设计问题,NRST引脚上的电容容量太大,导致上电瞬间复位时间过长,RTC初始化时备份寄存器还没稳定,标志位判断失效。解决方法是把复位电容从100nF改小到47nF,并且把备份寄存器标志位判定的代码放在RTC初始化之后多延时20毫秒再读取。
6. 开源资料清单与后续扩展建议
6.1 项目文件都包含什么
我把整个项目开源到了Gitee和GitHub,仓库里面包含的内容有:完整的Keil工程源码,基于HAL库,可以直接编译下载;PDF格式的原理图,以及AD格式的原理图源文件,方便你自己改;Proteus仿真工程文件,版本是8.11以上可以打开;Wokwi在线仿真工程链接,浏览器里就能跑;一份我写的硬件焊接与调试说明文档,里面包含了元件清单、接线表格和上电步骤。所有的代码注释我都尽量写成了中文,注释率大概有30%,不是那种“拷贝下来改个名”的水代码,是认真整理过的。
元件清单方面,主控是STM32F103C8T6最小系统板,也可以直接画进PCB;电机是28BYJ-48加ULN2003驱动板;称重模块是HX711加5kg压力传感器;屏幕是0.96寸I2C OLED;通信模块用ESP8266-01S;另外就是AMS1117-3.3、电阻电容若干、按键三个、纽扣电池座一个、电源接口和开关。如果你只是验证代码,这些模块加起来不超过80块钱。如果你要做一个完整的外壳,3D打印一个就行,我仓库里也放了STL文件。
6.2 我踩过的几个设计教训
这个项目前后迭代了三版,有些教训挺值得写出来。第一版我用的是旋转式出粮盘,结果发现粮仓里的粮食在转盘转动时会被卡住,尤其是猫粮颗粒比较大的品种,后来换成螺旋出粮才解决。第二版我在称重上面偷懒,直接放在出粮口下面测下料重量,结果发现电机震动对传感器影响太大,读数波动超过20克,根本没法闭环控制,最后改成把整个粮桶放在传感器上,用“出粮前后重量差”来校准。第三版就是现在这版,各项功能才稳定下来。所以如果你复刻的过程中遇到类似问题,别怀疑硬件不行,大概率是结构和算法的匹配问题。
另外还有个经验,程序里的喂食参数不要只保存在Flash里,还应该在OLED上提供一套“恢复默认”的菜单入口。因为我遇到过用户把粮食重量设置成0,结果设备到点后不停出粮,最后粮全撒了。在代码里加参数合法性判断,比如目标出粮量必须不小于5克,这行代码很便宜,但能避免很多麻烦。
6.3 这个项目还能怎么玩
如果你拿到这套系统的代码和原理图,后续的扩展空间其实很大。最简单的,把ESP8266换成ESP32或者加一个透传模块,就能接入现有智能家居平台,实现手机远程查看余量、远程触发喂食。想做图像识别的,可以挂一个K210或者OpenMV摄像头,识别猫是否在食盆旁边,只有宠物靠近时才出粮,这样能防止野鸟来偷吃。想更智能一点,可以在称重数据上做趋势分析,判断宠物每天进食的节奏,如果连续两天进食量异常下降,就在OLED上提示“可能是健康问题”。这些方向我和一些朋友也有讨论过,后面可能会做成系列项目继续开源。
提示一下,如果你准备直接打PCB,建议第一版不要做太小,模块化布线会更容易调试。等电路验证通过了,再考虑把最小系统、电机驱动、HX711全部集成到一块板子上,尺寸可以压缩到名片大小。
我个人实际使用下来,这套喂食器已经稳定跑了好几个月,出粮精度基本能控制在±5克以内,OLED上能直接看到下次喂食倒计时,出门在外用ESP8266上报的余量数据判断要不要添粮。如果你也想做一个,照着仓库里的资料一步步来就行。最后再分享一个小技巧:调试称重部分时,别用新鲜猫粮校准,因为颗粒大小和湿度会影响密度,最好用大米或者黄豆这种颗粒均匀的东西来标定,等真正用的时候误差会小很多。