☰
ESP32智能晾衣杆实战:从传感器采集到MQTT云端接入全解析
2026/10/3 7:51:47 网站建设 项目流程

1. 项目概述:一门期末大作业的“面试官视角”

这个故事得从2024年那阵子说起。当时我刷到一个嵌入式应届生的作品集,里面放着一个期末大作业——家用智能晾衣杆,而这件作品堂而皇之地躺在他面试蚂蚁金服的PPT里。很多人第一反应是“这东西也敢拿出来面大厂?”但仔细看下去,你会发现这个作品其实把嵌入式常见的传感器采集、电机驱动、无线通信、小程序/云端联动全串起来了,几乎覆盖了嵌入式岗位面试里“项目问答”环节80%的高频考点。

这个项目解决的是日常晾衣场景里的一个真实痛点:家里没人,突然下雨了,晾在外面的衣服怎么办?传统晾衣杆要么靠人手动收,要么买几千块的电动晾衣架,但对学生党或租房党来说,自己动手做一个几十块钱成本的智能版本,既能练手又能压在面试桌上讲出花来,性价比拉满。

对正在做嵌入式期末作业、毕业设计,或者准备嵌入式/物联网方向面试的人来说,这个项目特别值得参考。它不挑平台,STM32、STC、ESP32都能跑;难度适中,从GPIO到中断到RTOS再到云平台接入,层层递进;而且每一层都可以在面试时做深度追问。

我写这篇文章的初衷,不是让你抄一份作业交差,而是想把这个项目拆开揉碎,讲讲它背后的设计逻辑、调试过程,以及我从面试官角度看到的“亮点与雷区”。如果你正在为期末项目发愁,或者简历里缺一个能打的物联网作品,这篇内容应该能给你不少启发。

2. 需求拆解与系统方案选型:别急着买元器件

2.1 需求清单:先想清楚“自动”到底自动在哪

做嵌入式项目,最忌讳拿到题目就开干,先把需求梳理清楚。家用智能晾衣杆的核心需求不用整得太花哨,三条主线就够了:

  • 自动收晾:检测到下雨(雨滴传感器)就自动把晾衣杆收回;检测到阳光充足(光照传感器)就自动伸出去晒。
  • 远程可控:人在公司或地铁上,打开手机小程序,能看到当前天气状态和晾衣杆位置,并能手动控制伸出/收回。
  • 安全保护与状态反馈:电机运转要有极限停止逻辑,不能把绳子拉断了还在转;晾衣杆位置和传感器状态需要实时上报。

我当时做的时候,额外加了一个“手动优先”的逻辑:只要人在家,就能通过按键直接操作,不受自动逻辑干扰。这个设计加分很大,面试官一听就会觉得你考虑过真实使用场景——纯自动机器在家里是会被骂的,手动接管永远是刚需。

2.2 主控选型:为什么我最后选了ESP32而不是STM32

很多学校的嵌入式课程还在用STM32F103,但如果你把“智能晾衣杆”当成一个物联网项目来做,我建议优先考虑ESP32,理由很实际:

  • ESP32自带Wi-Fi和蓝牙,省掉外挂ESP8266模块的接线和调试成本。STM32要连Wi-Fi,要么串口接ESP8266,要么接ESP-01S,多一层通信调试就多一批坑。
  • ESP32的ADC精度和输入电压范围对光电传感器、雨滴传感器的适配更友好,不需要额外做电平转换。STM32F103的ADC是3.3V参考,如果传感器模块输出5V电平,还要分压。
  • ESP32支持Arduino框架和ESP-IDF两种开发方式,期末时间紧,用Arduino能快速出效果;想卷一点,上ESP-IDF还能讲FreeRTOS任务调度。

当然,如果你学校指定必须用STM32,这个项目的逻辑也能完整复现,只是Wi-Fi部分换成“STM32 + ESP8266串口透传”,代码量会多出一截,调试串口波特率不匹配、数据乱码这类问题也会更耗时。我在下面的实现方案里会同时给两条路线的关键差异点。

2.3 电机与结构方案:减速电机、步进电机还是舵机

晾衣杆的伸缩机构,看起来简单,实际上电机的选择直接决定项目的成败。我见过有同学用普通直流电机直接驱动滑轮,结果电机扭矩不够,晾衣杆纹丝不动;也有人用舵机改,但舵机角度有限,没法实现长距离伸缩。

最终我采用的是双向减速直流电机 + 行程开关 + 限位挡块的方案。选减速电机的原因是它自带齿轮箱,扭矩够大,能把转速降下来,控制精度足够用于晾衣杆这种慢速直线运动。行程开关装在轨道两端,钩子碰到就断电,防止电机堵转过流。

更讲究一点的做法,是用步进电机做开环位置控制,靠脉冲数精确知道晾衣杆位置,但步进电机的驱动(A4988或TB6600)会额外增加成本和控制复杂度,而且步进电机在低速大扭矩时容易发热,家庭场景没必要这么较真。

2.4 传感器选型:便宜大碗的三件套

  • 雨滴传感器:市面上最常见的LM393雨滴模块,几块钱一块,板上有一个十字形的感应区域,水滴落在上面会导致两个电极间电阻变化,输出模拟量和数字量两路信号。我用的是模拟量输出,通过ADC读取电压值,这样可以判断小雨、中雨、大雨,而不是简单地触发一个“下雨/不下雨”的开关量。
  • 光照传感器:用的光敏电阻模块(也是LM393比较器输出),输出模拟电压随光强变化。放在晾衣杆支架顶部,朝上安装,注意不要被电机外壳或晾衣杆本体遮挡。
  • 温湿度传感器:这个是我自选的,用了DHT11,算是给项目增加一个“天气看板”的功能,数据能上报到小程序里。DHT11精度一般,但胜在便宜且驱动简单,网上例程一抓一大把。想追求精度可以用DHT22,也就贵个十块钱。

2.5 通信方案:MQTT是物联网的门票

物联网项目的核心在“网”。ESP32采集到传感器数据、电机状态之后,必须上传到云端,手机端才能实时查看和控制。我选的是MQTT协议,理由很简单:它是物联网领域的事实标准协议,轻量、基于发布/订阅模式,对嵌入式设备的内存占用极低,而且阿里云、腾讯云、百度云物联网平台全部原生支持。

云平台我用的阿里云物联网平台,因为学生有免费额度,而且文档是全中文的,对新手友好。设备端通过MQTT连接阿里云,上报传感器数据主题(/sys/xxx/thing/event/property/post),接收云端下发的控制指令主题(/sys/xxx/thing/service/property/set)。小程序端则通过阿里云的HTTP API或AMQP服务端订阅来获取设备数据。

这里有个关键认知:**MQTT不是HTTP那种请求-响应的模式,而是一种类似“订阅报纸”的模式。**设备订阅了“控制指令”这个主题,云端一发消息,设备就能收到;设备把状态发布到“状态上报”主题,云端所有订阅了这个主题的客户端(比如你的手机小程序)都能看到。理解了这个模型,后面写代码基本不会跑偏。

3. 硬件搭建与电路设计实操

3.1 电路图核心节点

整个系统的接线并不复杂,核心节点如下:

  • 雨滴传感器(AOUT)→ ESP32 GPIO34(ADC通道)
  • 光敏传感器(AOUT)→ ESP32 GPIO35(ADC通道)
  • DHT11(DATA)→ ESP32 GPIO4
  • 继电器模块(IN1)→ ESP32 GPIO25,继电器输出端接减速电机的正负极
  • 行程开关1(收回限位)→ GPIO26(上拉输入)
  • 行程开关2(伸出限位)→ GPIO27(上拉输入)
  • 按键(手动收回)→ GPIO14(上拉输入)
  • 按键(手动伸出)→ GPIO12(上拉输入)

电源方面,ESP32开发板用USB 5V供电,电机和继电器用外部5V/2A电源供电,注意继电器模块的信号输入端和电机电源之间要做好隔离。如果你用STM32+ESP8266的方案,就多出来一组串口连接:STM32的USART1_TX/RX接ESP8266的RXD/TXD,共地必须接好。

3.2 继电器控制电机的反向旋转技巧

直流减速电机要正转和反转,普通的单路继电器做不到,必须用双路继电器模块或者一个双刀双掷继电器来控制电机的H桥接法。我用的是一块2路继电器模块:

  • 继电器1控制电机的VCC端
  • 继电器2控制电机的GND端

当继电器1吸合、继电器2断开时,电流从VCC流向GND,电机正转;反向操作则电机反转。两个继电器同时吸合或同时断开,电机就停止。代码里要加一个严格的“互斥保护”:在任何时刻都禁止两个继电器同时吸合,否则相当于电源短路,轻则烧保险丝,重则起火。

这是整个项目里最大的安全隐患,我第一次调试时差点烧了电机驱动模块,后来在代码里强制加了状态互斥判断,并且硬件上串联了一个1A自恢复保险丝,双保险才敢放心跑。

3.3 行程开关的接法与去抖

行程开关本身就是一个机械触点,电机运行时的振动会导致触点抖动,如果直接用GPIO中断去检测,容易产生误触发。我采用“定时轮询 + 软件去抖”的方式:每20ms读取一次行程开关状态,连续读到3次相同的电平才认为状态稳定。

独立按键也做了同样的去抖处理,用的是经典的“延时10ms再读一次”方法。这个细节很小,但面试官很喜欢在项目问答里追着问“你怎么消抖”,两种方式(硬件RC滤波和软件延时去抖)你都答得上来,项目可信度会高很多。

3.4 供电拓扑与布线避坑

做这类项目,布线顺序比你想的重要。我一开始把所有器件都挂在ESP32的3V3引脚上,结果DHT11数据跳动剧烈,雨滴传感器读数也忽高忽低,后来才发现是供电不足导致传感器工作不稳定。ESP32开发板的AMS1117稳压器本来就只能输出几百毫安,带不动一堆传感器。

正确的供电拓扑是:

  • ESP32开发板单独用USB 5V供电
  • 雨滴传感器、光敏模块、DHT11统一接在3V3排针上(这几个传感器电流都很小,ESP32能带得动)
  • 继电器模块的JD-VCC引脚接外部5V电源,VCC引脚接ESP32的5V(用于给继电器线圈供电)
  • 电机单独用5V/2A电源适配器供电,不经过开发板

地线必须所有模块共地,具体做法是把ESP32的GND、外部5V电源的负极、电机驱动模块的GND全部接到一起,否则电平基准不一致,通信和采样全会飘。

4. 软件设计:从裸机逻辑到任务调度

4.1 主状态机:这个项目的灵魂

我不建议把控制逻辑写成一大坨顺序执行的if-else,因为晾衣杆的状态切换太频繁,顺序逻辑容易漏状态。我用了一个简单的主状态机,五个状态足够覆盖所有场景:

  • 状态A:晾衣杆缩回(收回限位触发)
  • 状态B:晾衣杆伸出(伸出限位触发)
  • 状态C:正在收回中(电机反转)
  • 状态D:正在伸出中(电机正转)
  • 状态E:停止(初始状态或手动停止)

任何外部事件(下雨、光照充足、远程指令)都只是触发状态迁移的条件,真正决定电机要不要转,只看当前状态和迁移目标的组合。这种设计的好处是逻辑清晰,不容易出现“电机卡在两个状态之间一直转”的死循环情况。

4.2 控制逻辑核心代码(ESP32 Arduino框架)

核心逻辑可以这样组织(关键部分贴出来,其余初始化代码省略):

typedef enum { STATE_RETRACTED, // 已收回 STATE_EXTENDED, // 已伸出 STATE_RETRACTING, // 收回中 STATE_EXTENDING, // 伸出中 STATE_STOPPED // 停止 } ClothesRackState; ClothesRackState currentState = STATE_STOPPED; void rackControlLoop() { int rainValue = analogRead(RAIN_SENSOR_PIN); int lightValue = analogRead(LIGHT_SENSOR_PIN); bool isRaining = (rainValue < RAIN_THRESHOLD); bool isSunny = (lightValue > LIGHT_THRESHOLD); switch (currentState) { case STATE_RETRACTED: // 收回状态下,如果雨停且光照充足,且没有手动收回指令,才伸出 if (!isRaining && isSunny && !manualRetractRequest) { motorForward(); // 正转伸出 currentState = STATE_EXTENDING; } break; case STATE_EXTENDED: // 伸出状态下,如果下雨、光照不足或手动收回,就收回 if (isRaining || !isSunny || manualRetractRequest) { motorReverse(); // 反转收回 currentState = STATE_RETRACTING; } break; case STATE_EXTENDING: if (digitalRead(LIMIT_EXTEND_PIN) == LOW) { motorStop(); // 行程开关触发,停止 currentState = STATE_EXTENDED; } break; case STATE_RETRACTING: if (digitalRead(LIMIT_RETRACT_PIN) == LOW) { motorStop(); currentState = STATE_RETRACTED; } break; case STATE_STOPPED: // 手动停止后的恢复逻辑:根据当前环境重新判断 if (manualRetractRequest) { motorReverse(); currentState = STATE_RETRACTING; manualRetractRequest = false; } else if (manualExtendRequest) { motorForward(); currentState = STATE_EXTENDING; manualExtendRequest = false; } break; } }

注意传感器阈值不能写死,我这里是把“雨滴传感器ADC值小于阈值”判定为下雨。不同环境下传感器的基值差异很大,因此我在程序初始化时做了一个20秒的自校准:记录上电后前20秒的传感器平均值作为基准值,阈值设为基准值的60%(雨滴传感器)和基准值的130%(光照传感器),这样在不同天气、不同光照环境下运行,误判率会低很多。

4.3 传感器阈值校准:为什么固定阈值不靠谱

雨滴传感器有个很坑的特性:没有水滴时,AO引脚输出电压接近3.3V,ADC读数大概在3800-4095之间;但只要沾了一点点水汽,读数会瞬间掉到2000以下。如果阈值设高了,可能晚上露水重一点就误判成下雨;设低了,小雨淋了半天才触发,衣服早湿了。

我的做法是“上下阈值滞回”:

  • 判定下雨的阈值:ADC < 1500 持续2秒
  • 判定雨停的阈值:ADC > 2200 持续10秒

这里故意让“雨停”比“下雨”更难触发,中间留了700的滞回区间,避免雨滴传感器表面的水分在蒸发过程中读数不停抖动,导致电机频繁启停。类似的滞回逻辑也用在了光照传感器上。

这个滞回思想是自动控制里非常经典的做法,面试时主动讲出来,会让面试官觉得你不是简单抄代码,而是理解背后的工程考量。

4.4 联网模块:阿里云物联网平台的接入要点

如果你用ESP32,接入阿里云的流程是:

  1. 在阿里云物联网平台创建产品“smart_clothes_rack”,选择自定义品类,节点类型选“设备”,连网方式选“Wi-Fi”。
  2. 添加物理模型功能定义:定义“RainStatus”(整数类型,0-1)、 “RackPosition”(整数类型,0-100)、“MotorState”(枚举类型,0停止/1伸出/2收回)等属性。
  3. 注册设备,拿到三元组(ProductKey、DeviceName、DeviceSecret)。
  4. ESP32侧使用pubsubclient库连接MQTT broker,地址格式是“${ProductKey}.iot-as-mqtt.${RegionId}.aliyuncs.com”,端口1883。
  5. 连接参数里,ClientId格式固定为“${DeviceName}|securemode=3,signmethod=hmacsha1,timestamp=xxx”,username是“${DeviceName}&${ProductKey}”,password用HmacSHA1算法对“clientId${ClientId}deviceName${DeviceName}productKey${ProductKey}timestamp${timestamp}”做签名。

这套流程在阿里云官方文档里有详细说明,但很多同学卡在密码签名这一步。我建议直接用阿里云官方提供的“设备端SDK”,不要自己手写HmacSHA1签名,省时省力。用MQTT.fx工具先在电脑上模拟设备接入,确认云端通了你再烧到ESP32里,排查起来会顺畅很多。

4.5 小程序端:不是必须自研,但动手做会加分

我当时做了一个微信小程序来显示状态和控制,核心就两个页面:

  • 设备状态页:显示温湿度、光照强度、降雨状态、晾衣杆位置,通过WebSocket或HTTP定时轮询从自己的服务器(或者直接用阿里云提供的API)拿数据。
  • 控制页:两个大按钮,“收回”和“伸出”,点击后调用云端API下发指令到设备。

小程序的技术栈不复杂,就是微信官方的WXML+JS,难点在于域名证书和HTTPS要求。为了快速演示,可以直接在微信开发者工具里勾选“不校验合法域名”,然后调用阿里云HTTP API。但这只适合开发调试,上架需要正式域名。

如果你不想做小程序,改成用“阿里云天猫精灵”或“MQTT客户端App”直接控制也能达到效果。不过从小程序动手做一个完整闭环,对于面试展示来说,效果会好得多——你能讲的东西从嵌入式扩展到了前端和云端。

5. 调试实录:我踩过的那些坑

5.1 现象一:电机不转,但继电器“哒哒”响

排查步骤:

  • 用万用表测继电器输出端的COM和NO口电压,确认是否有5V输出。如果没有,说明继电器触点没接通或模块供电异常。
  • 测继电器模块的JD-VCC和GND之间是否有5V。很多继电器模块上面有个跳帽,出厂时默认把VCC和JD-VCC短接,如果你给JD-VCC单独接外部电源,就必须拔掉这个跳帽,否则电源会打架。
  • 确认电机本身是好的是。直接把5V电源接电机两极,能转就说明电机没问题。

最后我发现是跳帽没拔,外部电源和开发板电源短接,电机端电压被拉低到2V左右,根本带不动。拔掉跳帽后一切正常。

5.2 现象二:雨滴传感器过于灵敏,对着它哈气就触发

雨滴传感器的感应面是裸露的铜箔走线,不仅对水滴敏感,对空气中的水汽、甚至人体靠近时的电场干扰都很敏感。我一开始把传感器放在晾衣杆支架侧面,结果每次有人从旁边走过,读数就剧烈跳动。

解决办法:

  • 把传感器抬高,正面朝上安装,让雨水能直接滴落,而不是靠气流带上去。
  • 用热熔胶把传感器背面的焊点和排针封起来,防止受潮短路。
  • 在代码里加入“持续时间确认”:连续2秒都满足“ADC < 1500”才判定下雨。这个延时过滤非常有效。

5.3 现象三:Wi-Fi偶尔掉线,重连逻辑必须自愈

ESP32的Wi-Fi连接并不是永久的,家里路由器偶尔重启、信号波动,设备就会掉线。掉线后如果代码里没有重连逻辑,整个远程控制就瘫痪了。

我在loop()函数里加了一个心跳检测:

  • 每5秒检查一次Wi-Fi连接状态,如果断开了,尝试重连,最多尝试3次。
  • MQTT连接也是一样的逻辑,掉线后先重连Wi-Fi,再重连MQTT。
  • MQTT重连时注意需要重新订阅主题,否则收不到远程指令。

这个自愈逻辑让我在面试时意外加分了,因为很多同学的能远程控制的设备,演示时一旦网络波动就当场翻车。做嵌入式产品,自恢复能力往往是面试官考察工程素养的关键点。

5.4 现象四:电机堵转后发热严重,必须加过流保护

有几次调试行程开关没装到位,晾衣杆已经顶到边界了,电机还在转。减速电机的堵转电流可以达到正常工作电流的3-5倍,持续十几秒,电机外壳就会烫手。

我在硬件上加了两个保护:

  • 电路串联1A自恢复保险丝,电流超过阈值自动断开,冷却后自动恢复。
  • 代码里加入了“最长时间保护”:电机持续运转超过15秒,不管行程开关有没有触发,都强制停止,并上报“运行超时”警告状态。

6. 常见问题速查表

现象可能原因排查/解决办法
雨滴传感器读数飘供电不稳接3V3而不是5V,或者给传感器单独加强滤波电容
继电器吸合但电机不转跳帽未拔导致电源短接断开VCC与JD-VCC的短接跳帽,外部供电直接接JD-VCC
电机转向反了继电器输出线接反交换电机两根线的位置即可
DHT11读数恒为0数据线没接对或漏上拉DHT11的DATA线需要4.7K-10K上拉电阻,部分模块内置,裸传感器需外接
手机控制无响应MQTT未重连开启Wi-Fi/MQTT掉线自动重连,并重新订阅所有主题
收衣晾衣逻辑反复横跳阈值紧贴着环境噪声加入滞回区间,雨通阈值1500,雨停阈值2200
晾衣杆收回超时行程开关触点氧化用万用表通断档测行程开关,确认按压有反应;必要时换微动开关
编译报内存不足ESP32的Flash分区太小确认分区表选的是“Huge APP”而非默认的“No OTA”,大固件需要更大的APP分区

7. 写在最后:从期末作业到面试谈资

回到开头那个问题:为什么带着“家用智能晾衣杆”这个作品,也敢去面大厂?我现在再回头看这个作品,它最大的价值不在于“晾衣杆”本身有多高级,而在于它体现了一个嵌入式开发者必须具备的系统思维——从物理世界采集信号,到数据处理和状态判断,到执行机构控制,再到云端通信和应用层交互,这条完整的链路你都亲手打通了,并且你踩过坑、解决过问题,能讲清楚每一个取舍背后的原因。

用到这个项目里学到的东西,我后来在不少实际工作中都反复用到:状态机的设计模式是在任何嵌入式系统里都能复用的;MQTT的发布/订阅模型,几乎成了我做任何物联网设备接入的默认选择;而传感器阈值滞回的理念,更是让我在后来的工业项目中少走了很多弯路。

最后再分享一个小技巧:如果你想把这类作品放进面试简历,不要只写“做了一个智能晾衣杆”,而是写成“基于ESP32的物联网智能晾衣杆系统,通过雨滴/光照传感器实现自动收展控制,采用状态机模型管理执行逻辑,经MQTT接入阿里云平台,开发微信小程序实现远程状态监控与指令下发”。这样一句话,把技术栈、架构、功能全部交代清楚,面试官瞄一眼就能抓到重点,自然也更有兴趣深挖你的设计思路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询