基于STM32的智能婴儿床系统设计与实现

基于STM32的智能婴儿床系统设计与实现 每年到毕设选题季智能婴儿床这个题目总会被很多人盯上。原因很简单市面上带声控摇晃、尿湿提醒、温湿度监测功能的智能婴儿床动辄两三千看起来“很有技术含量”但把外壳拆掉往里面看核心就是一颗STM32单片机、一组传感器、一个舵机加一个WiFi模块。用STM32做毕设不仅难度适中而且从硬件到软件再到调试每个环节都有东西可写论文也撑得住。这篇文章我就按我自己做完这套基于STM32的智能婴儿床系统的思路把整个设计的完整实现过程拆开来讲需求怎么定、芯片怎么选、传感器怎么接、逻辑怎么跑、远程报警怎么传、整机联调有哪些坑。无论是大四做毕业设计还是低年级做课程设计、电子竞赛练手这份笔记都能帮你省掉一大半自己踩坑的时间。1. 需求拆解智能婴儿床的“智能”到底落在哪很多同学拿到这类题目第一反应是“先把功能堆上去”温湿度、哭声检测、尿湿检测、自动摇晃、蓝牙、WiFi、摄像头、App能加的全加上。结果到了中期检查发现每个功能都是半吊子检测到了只会“在OLED上显示一下”老师一问“检测到之后系统做了什么闭环反应”人直接愣住。1.1 核心痛点与功能映射做设计之前我先列了一张“家长真实痛点 vs 系统功能“的对照表。智能婴儿床本质上解决的就是这几件带娃过程中最折腾人的事宝宝哭了需要人抱着摇。对应功能声音检测 舵机驱动的自动摇晃安抚。尿布湿了宝宝不舒服会哭拖久了还容易红屁股。对应功能尿湿检测触发本地声光提醒和远程推送。踢被子了体温受凉。对应功能床内温湿度检测温度过低或过高时报警。家长不在房间不知道宝宝状态。对应功能ESP8266将状态上报到手机端实现远程查看。这套映射关系是整篇论文逻辑链条的起点也是答辩时老师最认可的部分。你得让他看到你不是在“做一个遥控车”而是在“解决一个真实问题”。1.2 毕设的功能边界不是功能越多越好我的建议是把系统控制在6个以内的大模块并且每个模块必须形成闭环。什么叫闭环就是“检测到异常 触发本地动作 上报/提醒 恢复或停止”四个环节缺一个都不完整。我见过太多人把毕设做成一个“多功能检测仪”——PM2.5也测、火焰也测、烟雾也测、人脸也识别最后所有数据都堆在一个屏幕上一个串口里这不叫系统叫仪器。智能婴儿床要想拿到好成绩重点不在传感器数量而在“决策逻辑”和“闭环反馈”。1.3 系统总体技术指标在动手之前我先把一些可量化的指标定下来方便后期调试和论文里写测试报告指标项设计目标备注哭声检测响应时间≤2秒声音信号连续稳定触发尿湿检测响应时间≤5秒电极电阻变化判断温湿度测量误差±1℃ / ±5%RHDHT11的标称精度范围自动摇晃持续时长3~5分钟超过后自动停止防过度干预远程报警延迟≤3秒局域网TCP条件下整机静态工作电流≤200mA不含舵机堵转瞬时电流舵机摇晃角度范围±15°可调小角度低频往复有了这张表后面每一步选型和调试都有据可依论文里的“测试与结果分析”章节也就有东西写了。2. 硬件选型为什么是STM32F103C8T6传感器怎么挑2.1 主控芯片STM32F103C8T6是毕设的“安全牌”主控我选的是STM32F103C8T6这颗芯片在本科生毕设里几乎是统治级的存在理由非常实在价格低几块钱到十几块钱一片核心板也便宜画错板子不心疼。资料全网最多遇到问题一搜就有答案库函数版本和HAL库版本都有大量现成案例。外设够用3个USART、2个ADC、4个16位定时器、I2C、SPI覆盖这个项目需要的全部功能。能用STM32CubeMX图形化配置引脚和时钟Keil MDK直接编译下载开发效率比纯寄存器开发高太多。有人会问为什么不用51单片机51做这种多传感器、多状态、带PID频率控制的系统Flash和RAM都吃紧代码写起来非常憋屈。也有人问为什么不用ESP32ESP32强在WiFi和蓝牙但它的定位偏向物联网模组在“单片机课程设计”的评审语境下用STM32更容易和课程知识对应上导师也更容易给分。2.2 传感器选型能买到、够便宜、资料多传感器选型我踩过一些弯路最后定下来的方案是这样功能模块推荐型号备选方案选择的核心理由声音检测LM393麦克风模块MAX4466 / MAX9814LM393模块同时输出模拟量和数字量板上带灵敏度电位器便宜好用温湿度检测DHT11SHT30 / AHT20DHT11精度一般但毕设完全够用单总线协议简单代码量少尿湿检测自制不锈钢电极 LM393电压比较器土壤湿度传感器模块自制电极贴合场景成本几毛钱而且能体现你的硬件设计能力人体活动检测HC-SR501热释电红外无源红外传感器用于检测宝宝是否在床内或是否有大幅踢被动作摇晃执行SG90舵机MG996R / 减速电机SG90力矩对于婴儿床模型足够驱动简单PWM控制即可音乐播放无源蜂鸣器 三极管驱动DFPlayer Mini无源蜂鸣器可以播放不同频率的“白噪音”音调DFPlayer更好但成本高显示0.96寸OLED SSD1306LCD1602OLED显示内容丰富I2C接口省引脚显示效果好无线通信ESP8266-01SHC-05蓝牙 / ESP32蓝牙不能远程ESP8266能连WiFi走TCP/MQTT远程报警就靠它2.3 电路设计与供电分配最容易翻车的一环这个项目的供电设计非常关键也是最容易被忽视的。我最初把舵机、ESP8266、单片机全部挂在一个AMS1117-3.3V稳压器上结果只要舵机一转单片机就重启。整机供电分配建议如下外部电源5V/2A的USB适配器或者两节18650电池组。5V直接给舵机供电走单独一路不经过稳压。5V经过AMS1117-3.3V给STM32、DHT11、OLED供电。ESP8266-01S单独用一颗AMS1117-3.3V供电峰值电流大不能和单片机共用。主电源输入端并联一个大容量电解电容470µF以上用来吸收舵机启动时的大电流冲击。我当时还特意给每个传感器模块加了一个0.1µF的去耦电容说实话板子上不一定看得出区别但答辩老师问“为什么这里要加电容”的时候你能答上来这就是加分项。3. 传感器数据采集与处理从硬件信号到软件可用的数值3.1 多通道ADC采样声音传感器和尿湿检测的模拟量读取LM393麦克风模块和自制尿湿电极电路输出都是模拟电压信号需要接入STM32的ADC引脚。我用的是ADC1的通道0和通道1PA0声音传感器模拟输出连接LM393模块的AO引脚PA1尿湿检测比较器模拟输出或直接接电极分压点我用STM32CubeMX配置ADC为“扫描模式 连续转换模式”DMA搬运数据。为什么不直接用阻塞式读取因为主循环里有很多任务要跑如果每次读ADC都阻塞几十微秒系统流畅度会受影响。DMA方式读ADC是毕设答辩时一个不错的亮点你可以在论文里写“采用DMA方式采集降低CPU占用率”。采样数据处理部分我用了“中值滤波 滑动平均”两级处理// 伪代码示意数据处理逻辑 #define SAMPLE_NUM 10 uint16_t raw_buf[SAMPLE_NUM]; uint16_t get_median_adc(void) { // 1. 从DMA缓冲区拷贝原始数据 // 2. 对raw_buf排序 // 3. 返回中间值或中间两个值的平均 // 4. 对连续3次中值结果再做滑动平均 }为什么要用中值滤波而不是直接求平均因为声音信号里会有尖峰脉冲比如关门声、手机铃声这些尖峰如果参与直接平均会把阈值拉得很高导致后面真正的哭声触发不了。中值滤波能很好地剔除这种孤立尖峰。3.2 DHT11的时序与避坑细节DHT11用单总线通信一个引脚既做时钟又做数据STM32要精确控制延时特别是读时序时每一位的采样点位置。核心读取过程大概是主机拉低总线18ms以上发出启动信号 - 释放总线 - DHT11应答 - 连续输出40位数据8位湿度整数 8位湿度小数 8位温度整数 8位温度小数 8位校验和。用HAL库写的时候有一点特别坑STM32的GPIO速度如果配得太慢微秒级延时误差会很大导致数据位读错。我最终是这么处理的把DHT11数据引脚配置为开漏输出外部加上拉电阻4.7kΩ 到3.3V。切换输入/输出模式时用寄存器操作而不是HAL库函数减少函数调用开销。所有延时候选方案最终统一用DWT-CYCCNT精确延时而不是HAL_Delay。另一个重要细节是DHT11两次读取操作之间必须间隔1秒以上否则传感器会不更新数据返回的永远是上一次的值。这在主循环里要加一个时间戳判断。if ((HAL_GetTick() - last_tick) 1000) { dht11_read_data(temp, humi); last_tick HAL_GetTick(); }3.3 尿湿检测自制电极一个容易被忽略的“化学”问题尿湿检测我一开始用的是裸铜电极焊在洞洞板上直接放进小床垫结果用了两天就发现铜箔被腐蚀发绿了模拟输出电压漂移严重。原因是尿液里的盐分对铜有很强的腐蚀性工作一段时间后电极表面状态变了读取数值就不再可靠。后来我把电极换成了两片不锈钢片交叉排列成梳齿状固定在床垫表层下方间距控制在5mm左右。尿液浸湿床垫后电极之间的阻抗会从几百千欧掉到几十千欧配合一个LM393比较器电路输出电压会明显跳变。电路连接是5V —— 10kΩ —— 电极A —— 电极B —— GND电极A和电极B的连接点接到LM393的同相输入端反相输入端接一个可调电位器用来设定触发阈值。无水时节点电压接近5V有水时电压被拉低比较器输出翻转这个信号接到STM32的普通GPIO配置为数字输入即可。如果你不想自己焊比较器电路也可以直接用市场上的土壤湿度模块探头面积大检测灵敏但答辩时老师可能会问“你怎么解决金属电极极化问题”自己设计过电极的人回答起来明显更有底气。3.4 OLED显示与按键交互显示模块用的是0.96寸OLEDSSD1306驱动芯片I2C接口。STM32的硬件I2C在F103上存在一些兼容性问题很多人用软件模拟I2C稳定性更好。我这里直接用了硬件I2C配好时钟和引脚后反而没出问题但如果你遇到HAL_I2C卡死别纠结直接改成软件模拟I2C一劳永逸。OLED要显示中文需要用取模软件PCtoLCD2002生成中文字模取模方式选择“列行式、逆向、逐列式”这样和OLED驱动库里显示函数的扫描方式一致否则字会变成镜像或乱码。按键我用了3个独立按键菜单切换、加、减。按键消抖不用外部硬件直接在定时器中断里每10ms扫描一次连续3次电平一致才判定按下效果非常稳。4. 核心控制逻辑状态机架构比一锅粥的if-else强在哪4.1 为什么这个项目必须用状态机如果不做状态机整个主循环会变成这样先采集传感器、然后判断要不要摇床、摇床时调用延时函数、延时期间OLED不刷新、按键也没响应、突然又来一个报警整套流程卡死。状态机的价值在于把系统拆成几个明确的状态每个状态下只做当前该做的事状态之间通过明确的迁移条件跳转。这样代码可读性强、好调试也给论文增加了“软件设计”的深度。4.2 三个核心状态的定义与迁移我的系统定义了三个状态STATE_IDLE待机监测系统正常运行时大部分时间处于这个状态只做传感器采集、数据显示、阈值判断不输出任何执行动作。STATE_SOOTHING自动安抚检测到哭声信号后进入舵机开始小角度往复运动蜂鸣器播放柔和的“白噪音”音调持续3~5分钟后自动回到待机状态。STATE_ALARM报警状态哭声连续触发超过设定次数或温湿度越限或尿湿检测触发系统会持续蜂鸣报警并通过ESP8266向手机端推送报警信息。状态迁移条件我整理成了一张表写论文的时候也直接用这张表当前状态迁移条件下一状态STATE_IDLE哭声检测连续3次有效间隔≤10秒STATE_SOOTHINGSTATE_IDLE尿湿检测触发 / 温湿度越限STATE_ALARMSTATE_SOOTHING持续安抚3~5分钟无再次哭声STATE_IDLESTATE_SOOTHING安抚期间哭声仍持续且超过2分钟STATE_ALARMSTATE_ALARM手动按键确认 / 远程解除指令STATE_IDLE4.3 主循环的非阻塞写法状态机要想跑得流畅主循环里就不能有阻塞式延时。我的做法是用HAL_GetTick()做时间戳判断替代HAL_Delay()。// 主循环示意 while (1) { uint32_t tick HAL_GetTick(); // 每20ms执行一次按键扫描 if (tick - last_key_tick 20) { key_scan(); last_key_tick tick; } // 每100ms执行一次传感器读取和OLED刷新 if (tick - last_sensor_tick 100) { sensor_read_and_filter(); oled_refresh(); last_sensor_tick tick; } // 每500ms执行一次状态机逻辑 if (tick - last_state_tick 500) { state_machine_run(); last_state_tick tick; } // ESP8266数据发送 if (tick - last_upload_tick 5000) { esp8266_upload_status(); last_upload_tick tick; } }这样写的好处是无论舵机在转还是蜂鸣器在响OLED都能持续刷新按键也始终有响应整机不会有“卡死”的感觉。4.4 舵机PWM控制小角度往复才是安抚不是猛摆SG90舵机的控制信号是50Hz的PWM高电平宽度0.5ms对应0度1.5ms对应90度2.5ms对应180度。STM32用定时器产生PWM比如TIM2的CH1通道输出到PA0配置PWM频率为50Hz改变比较寄存器的值就能改变角度。自动摇晃的逻辑不是大幅摆动而是以中线±15度的小角度往复运动周期约2秒一次。这个参数我实测下来比较合适太大会让床体晃动过大太小又起不到安抚作用。void soothing_toggle(void) { // 每1秒切换一次方向 if (soothe_dir 1) { set_servo_angle(SOOTHE_MID 15); } else { set_servo_angle(SOOTHE_MID - 15); } soothe_dir !soothe_dir; }刚开始做的时候我试图在舵机上做PID闭环控制后来发现是过度设计——SG90这种模拟舵机本身有位置闭环没必要再用MCU做一次直接开环给PWM转角度就行。把PID留给论文里写“后续优化方向”就够了。5. 本地交互与远程报警OLED菜单和ESP8266数据上传5.1 OLED菜单设计一屏一件事两层深度屏幕虽然小但也得讲究信息架构。我设计了4个页面主页显示当前工作状态待机/安抚/报警、温度、湿度、尿湿状态。设置页1声音灵敏度阈值通过加减按键调整。设置页2自动摇晃持续时间默认3分钟。信息页显示WiFi连接状态、最近一次报警时间。按键逻辑是短按菜单键切换页面长按进入设置模式设置模式下短按加减键调整数值。这里有一个小技巧OLED刷新不要整屏全部清空再重画否则会有闪烁感。用局部区域更新的方式哪个数值变了就只刷新那一小块区域视觉上会流畅很多。5.2 ESP8266-01S的工作模式与通信流程ESP8266-01S是串口WiFi模块我用STM32的USART2与它通信波特率115200通过AT指令控制。下位机需要做的工作是模块复位发送ATRST等待重启完成。设置工作模式ATCWMODE1Station模式连路由器。连接路由器ATCWJAPssid,password等待返回WIFI CONNECTED和OK。建立TCP连接ATCIPSTARTTCP,192.168.1.100,8080。发送数据ATCIPSENDlen然后发送指定长度的数据内容。接收服务端下发指令串口会出现IPD开头的数据解析出来可以做远程控制。这里我要重点强调ESP8266-01S的3.3V供电必须单独供给它的峰值电流可以达到300mA以上。如果和STM32共用一颗AMS1117WiFi发包瞬间电压跌落轻则模块掉线重则单片机复位重启。我自己就是在这个地方折腾了整整两天最后把供电分开才彻底解决。AT指令解析的代码建议用定长数据包的思路每次收到\r\n就认为一条AT响应结束然后在缓冲区里查找OK或ERROR关键字。这样实现简单稳定性也够。// 伪代码等待AT指令返回OK uint8_t at_send_cmd(char *cmd, uint16_t timeout_ms) { // 1. 清空接收缓冲区 // 2. 发送cmd // 3. 轮询等待直到收到 OK 或超时 // 4. 返回 1 表示成功0 表示失败 }5.3 数据包格式与远程指令下发上报数据我用的是JSON格式一次传输大约50字节内容清晰{type:report,temp:26.4,humi:52,cry:1,wet:0,state:SOOTHING}解析端无论是用Python脚本还是用网络调试助手读起来都很直观。JSON的组装在STM32端用sprintf就能完成不需要引入cJSON库内容不需要动态嵌套结构。远程指令下发的格式也类似{type:cmd,action:stop_alarm}STM32收到后判断action关键字执行停止报警、复位系统等操作。这个“远程控制”的能力在答辩演示时非常加分老师能直观看到“双向通信”而不只是单向上报。5.4 手机端监测方案答辩演示最稳的组合远程监测这块有三个方案可选我给出的建议是方案优点缺点适用场景电脑端网络调试助手 路由器局域网TCP稳定、延迟低、不依赖外部服务器不能远程答辩现场演示首选巴法云 / OneNet 云平台 MQTT可远程访问有App配置复杂现场受网络影响大有充足时间调试的情况下可以尝试自建TCP服务器跑在PC上 简易上位机可控性强可做数据记录需要额外写上位机代码想体现“完整系统”能力时选这个做毕设有一个现实问题答辩现场的WiFi环境不一定给力。所以我的做法是系统优先连接手机热点电脑也连这个热点这样即使没有公网也能在一个局域网内完成全部演示。6. 整机联调我踩过的坑和实测数据6.1 舵机一动作单片机就重启这是我最开始遇到的第一个大问题。现象非常典型OLED显示正常蓝牙连接正常一切看起来没问题但只要舵机开始转动屏幕熄灭系统重启然后进入死循环。原因排查链路是这样的第一步用万用表测舵机转动时3.3V的电压发现电压跌落到2.1V左右。AMS1117输入端是5V输出3.3V电压是不可能跌到2.1V的问题一定出在输入侧。第二步测5V电源轨发现舵机启动瞬间5V被拉到3.8V。问题确认在电源输入侧。第三步看原理图5V适配器直接接到了舵机、AMS1117、ESP8266三路。舵机的启动电流瞬间可以达到700mA以上直接把5V电源轨砸出一个大坑。解决办法把舵机的电源线直接从主板供电端独立出来在5V输入端并联一颗470µF电解电容 一颗0.1µF陶瓷电容作为瞬态储能。ESP8266也用独立的AMS1117供电。改完之后舵机怎么转3.3V电压都稳稳的。这个坑非常有价值因为它能引出一大段论文里的“系统可靠性设计”内容包括电源完整性、去耦电容的作用、大功率执行器与数字电路供电隔离等话题。6.2 声音传感器阈值“白天乱响、晚上不响”LM393麦克风模块上有一个蓝色的电位器用来调节灵敏度。我一开始把电位器拧到中间位置阈值固定结果白天环境噪声大的时候一直误触发晚上环境安静的时候又检测不到哭声。解决思路是给系统加一个“环境基线校准”开机后系统连续采样2分钟环境噪声计算出平均值作为基线运行时用实时值减去基线值的差值来判断是否有突发声音。代码实现不复杂但效果提升非常明显。另外还有一个细节LM393模块放在床体框架上的时候要避开舵机附近的震动传导区域不然摇晃的时候舵机振动会产生很大的声音信号形成“一摇就响、一响就摇”的死循环。6.3 DHT11湿度卡在99%不再变化有段时间系统显示的湿度一直是99%我还以为是传感器坏了换了一块新的还是一样。后来发现是DHT11离尿湿检测区域太近吸水棉垫的水汽直接附着到了传感器的感湿元件上导致内部湿度饱和。这个问题要从两方面解决硬件上把DHT11移到床垫外侧加一个带透气孔的塑料外壳避免传感器直接接触高湿环境。软件上增加判断逻辑当湿度连续30分钟保持在85%以上时直接判定“尿湿或环境湿度过高”提醒用户检查而不是依赖具体数值。这里也顺便说明一下毕设做的是演示级、学习级的原型系统不能替代真正的医疗级监控设备。在论文的结尾安全说明里我会明确写出“本系统仅作为教学演示用途实际使用中家长仍需履行监护义务”这个不是套话是负责任的做法。6.4 尿湿电极被腐蚀与信号漂移前面提到的铜电极腐蚀问题让我后来换了不锈钢电极。这里还有一个关键参数要现场标定电极安装在床垫的哪个位置、间距多少会直接影响检测灵敏度。我做的标定流程是把电极安装在床垫样品上接入比较器电路。记录干燥状态下的比较器输出电压约4.8V。用5mL清水倒在电极区域记录电压变化约0.4V。用5mL生理盐水倒在电极区域模拟尿液记录电压约0.1V。根据电压变化设定阈值电位器的位置。这套“标定实验”数据放到论文里非常充实也是答辩时一个很自然的展示点。6.5 远程报警延迟实测完成整机联调后我做了几组实际测试数据拿来做论文的“系统测试”章节测试项目测试条件实测结果结论哭声响应时间持续正弦声音信号1.2~1.8秒达标尿湿报警响应5mL生理盐水滴入电极区2.5秒达标温湿度数据刷新周期主循环1秒查询间隔约1.1秒达标本地OLED按键菜单响应短按/长按100ms流畅TCP报警推送延迟手机热点局域网0.3~1.5秒达标舵机持续摇晃稳定性连续晃动10分钟无死锁、无重启稳定整机静态电流待机模式165mA低功耗目标达成6.6 关于“演示翻车”的保命经验最后分享几个现场演示的保命经验都是我实际经历过的教训面包板改焊接板面包板在调试阶段很方便但搬动两次后很容易有接触不良的问题现场一个引脚松动整个系统就废了。去嘉立创打样一块PCB几十块钱省心太多。所有插件用热熔胶固定杜邦线、排针、传感器插头全部点一下热熔胶防止搬运过程和演示过程中松动。备份一段完整的演示视频提前在环境稳定的时候录一段完整的功能演示视频拷到电脑里。就算现场设备出问题也可以放视频答辩不至于冷场。提前到答辩教室测试网络如果演示要用WiFi务必提前去教室测一下信号连不上就立即切手机热点不要到现场再找网络。做完整套系统后我最大的体会是毕设的重点不是“把每个传感器跑通”而是“把一堆独立零件组装成一个有决策能力、有反馈闭环的完整系统”。从需求定义、硬件选型、电路设计、嵌入式软件、通信协议到系统测试这一条线走下来才是毕设真正的价值所在。智能婴儿床这个题目之所以值得做就是因为它足够小、足够具体但又能把嵌入式开发的主线全流程都覆盖到。