文章目录
- 问题现象
- 原始方案分析
- 事件驱动控制
- 实现思路
- 完整代码
问题现象
在使用 NodeMCU(ESP8266)控制 SG90 舵机时,项目已经能够正常接收 MQTT 指令并完成动作,例如:
1.小爱同学控制开关
2.巴法云 MQTT 下发 on/off 指令
3.舵机执行转动角度动作
但运行后发现一个明显问题:
即使舵机已经停止转动,仍然持续发出滋滋滋声音,
同时伴随着:
1.舵机发热
2.电流持续消耗
3.偶尔轻微抖动
下面分析一下原始的方案。
原始方案分析
在代码一开始就把PWM启动
pwm.setup(SERVO_PIN,50,76)pwm.start(SERVO_PIN)得到的效果就是
ESP8266 ↓ 持续输出50Hz PWM ↓ 舵机持续接收控制信号 ↓ 持续保持当前位置这就导致整个运行过程中 PWM 从未停止。即使舵机已经转到目标位置,ESP8266 仍然每 20ms 输出一次控制脉冲。
所以,更合理的方案是下发命令在调用启动PWM,运行结束后在关闭PWM,通过事件驱动控制舵机转动。
事件驱动控制
其实我们就是想实现的是当我们下发命令后舵机再启动,然后执行完就关闭
优化后的控制流程: 收到MQTT指令 ↓ 启动PWM ↓ 舵机转动 ↓ 等待到位 ↓ 停止PWM ↓ 释放舵机实现思路
收到 MQTT 指令:
ifdata=="on"thenmoveServo(...)elseif data=="off"thenmoveServo(...)end执行函数:
pwm.setup(...)pwm.start(...)延时:
tmr.create():alarm(...)到达预定时间后:
pwm.stop(...)释放控制信号。
最后得到的效果是,动作完成后,PWM停止,舵机不再接收脉冲,不会继续修正位置而导致舵机发热。
完整代码
--==========================================--1. 全局参数配置 --==========================================localWIFI_SSID="WIFI名称"localWIFI_PASS="WIFI密码"localBEMFA_UID="巴法云个人私钥"localTOPIC="巴法云订阅MQTT主题"localSERVO_PIN=1-- 对应esp8266的D1引脚 --==========================================--2. 舵机初始化与控制函数 --==========================================localSERVO_OPEN_DUTY=76localSERVO_CLOSE_DUTY=25-- 转动时间(毫秒)localSERVO_MOVE_TIME=800localservo_busy=false-- 采用状态机码防止重复执行相同命令 --==========================================--3. 舵机动作函数 --==========================================functionmoveServo(duty, angle)ifservo_busythenprint("舵机正在执行动作,忽略重复命令")returnend servo_busy=trueprint("舵机开始转动 -> "..angle.." 度")pwm.setup(SERVO_PIN,50, duty)pwm.start(SERVO_PIN)tmr.create():alarm(SERVO_MOVE_TIME, tmr.ALARM_SINGLE, function()pwm.stop(SERVO_PIN)print("舵机动作完成")print("PWM已关闭,释放舵机")servo_busy=falseend)endfunctionsetServoAngle(angle)ifangle==90thenmoveServo(SERVO_OPEN_DUTY,90)elseif angle==0thenmoveServo(SERVO_CLOSE_DUTY,0)elseprint("未知角度")end end --==========================================--4. 连接巴法云 MQTT --==========================================localmqtt_client=nilfunctionconnectBemfa()ifmqtt_client ~=nilthenmqtt_client:close()end print("正在连接巴法云 MQTT...")mqtt_client=mqtt.Client(BEMFA_UID,120, BEMFA_UID, BEMFA_UID)mqtt_client:on("message", function(client, topic, data)print("收到消息")print("Topic : "..topic)print("Data : "..data)ifdata=="on"thensetServoAngle(90)elseif data=="off"thensetServoAngle(0)elseprint("未知指令")end end)mqtt_client:on("offline", function(client)print("MQTT掉线")print("5秒后重连")tmr.create():alarm(5000, tmr.ALARM_SINGLE, connectBemfa)end)mqtt_client:connect("bemfa.com",9501, false, function(client)print("MQTT连接成功")client:subscribe(TOPIC,0, function(client)print("已订阅主题: "..TOPIC)print("设备在线")end)end, function(client, reason)print("MQTT连接失败")print("错误码: "..reason)end)end --==========================================--5. WiFi 与系统启动流程 --==========================================functionstartSystem()print("====================================")print("开始连接 WiFi...")wifi.setmode(wifi.STATION)wifi.sta.config({ssid=WIFI_SSID,pwd=WIFI_PASS, auto=true})wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, function(T)print("已连接路由器,等待获取 IP...")end)wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(T)print("WiFi连接成功,IP: "..T.IP)connectBemfa()end)wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, function(T)print("WiFi断开,Reason: "..T.reason)--(保留你原有的错误码诊断逻辑...)end)end --==========================================--6. 核心防锁死:开机延时启动 --==========================================print("====================================")print("系统上电就绪。")print("进入 3 秒安全等待期,此时可使用 nodemcu-tool 强行打断...")-- 开机后强制等待3秒,不执行任何业务逻辑 tmr.create():alarm(3000, tmr.ALARM_SINGLE, function()print("安全等待结束,启动主控程序!")startSystem()end)