问题现象

在使用 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 指令:

if data == "on" then
    moveServo(...)
elseif data == "off" then
    moveServo(...)
end

执行函数:

pwm.setup(...)
pwm.start(...)

延时:

tmr.create():alarm(...)

到达预定时间后:

pwm.stop(...)

释放控制信号。

最后得到的效果是,动作完成后,PWM停止,舵机不再接收脉冲,不会继续修正位置而导致舵机发热。

完整代码

-- ==========================================
-- 1. 全局参数配置
-- ==========================================
local WIFI_SSID = "WIFI名称"  
local WIFI_PASS = "WIFI密码"
local BEMFA_UID = "巴法云个人私钥" 
local TOPIC    = "巴法云订阅MQTT主题"
local SERVO_PIN  = 1 -- 对应esp8266的D1引脚   

-- ==========================================
-- 2. 舵机初始化与控制函数
-- ==========================================
local SERVO_OPEN_DUTY  = 76
local SERVO_CLOSE_DUTY = 25

-- 转动时间(毫秒)
local SERVO_MOVE_TIME = 800

local servo_busy = false -- 采用状态机码防止重复执行相同命令

-- ==========================================
-- 3. 舵机动作函数
-- ==========================================

function moveServo(duty, angle)

    if servo_busy then
        print("舵机正在执行动作,忽略重复命令")
        return
    end

    servo_busy = true

    print("舵机开始转动 -> " .. 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 = false

    end)
end

function setServoAngle(angle)

    if angle == 90 then
        moveServo(SERVO_OPEN_DUTY, 90)
    elseif angle == 0 then
        moveServo(SERVO_CLOSE_DUTY, 0)
    else
        print("未知角度")
    end
end

-- ==========================================
-- 4. 连接巴法云 MQTT
-- ==========================================

local mqtt_client = nil

function connectBemfa()

    if mqtt_client ~= nil then
        mqtt_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)

        if data == "on" then
            setServoAngle(90)
        elseif data == "off" then
            setServoAngle(0)
        else
            print("未知指令")
        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 与系统启动流程
-- ==========================================

function startSystem()
    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)
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐