小爱同学语音控制 ESP8266 舵机转动(NodeMCU-tool Lua 开发实战记录)
本篇博客将完整记录如何使用 ESP8266 (NodeMCU) 固件,利用 Lua 语言配合巴法云,最终实现小爱同学语音控制舵机转动的全流程。
准备工作
- 硬件接线 (以 SG90 舵机为例)
红线 (VCC) ➡️ ESP8266 的 3V3(若动力不足可接 VIN)
棕线 (GND) ➡️ ESP8266 的 GND
橙线 (信号线) ➡️ ESP8266 的 D1 引脚(对应 Lua 底层 GPIO 5) - 巴法云与米家端配置
由于米家生态不对个人开发者直接开放,我们需要通过巴法云进行 MQTT/TCP中转:
a. 注册巴法云:登录后台拿到你的唯一密钥 Private_Key(私钥)。
b. 新建 MQTT设备云:新建一个主题,命名为 9nfwp3dxs002。
c. 修改昵称:在巴法云后台将该主题的昵称修改为 “舵机” 或 “灯”等随便名字(小爱同学以此名字来识别语音指令控制舵机)。
d. 绑定米家:打开手机 米家App ➡️ 我的 ➡️ 其他平台设备 ➡️ 绑定“巴法” 账号。随后在列表中找到该设备,分配房间并同步。
一、开发lua环境搭建
- 如果要使用nodemcu固件的烧录且使用Lua在esp8266上跑,需要先下载定制固件,由于我需要连接mqtt和使用舵机,所以我额外勾选MQTT和PWM。
然后填写自己的邮箱就可以Start your bulid,注意是第二封finish邮件中找到
上述两个的bin文件任意一个都行,integer稍微大小要小一点。 - 使用 NodeMCU PyFlasher 写入固件。
NodeMCU firmware:点击 Browse,选择你下载好的 .bin 固件文件
Baud rate波特率:选择 115200
Flash mode:选择 Dual I/O (DIO) - 直接在VS Code左侧扩展市场搜索并安装 NodeMCU-Tools 插件,然后再 VS Code 中打开一个空文件夹(作为你的项目工作区),并在里面新建一个文件,命名为 init.lua。
为什么要以 init.lua命名? NodeMCU 芯片在上电开机后,底层系统会自动寻找并第一个执行名为 init.lua 的文件,它是整个程序的入口。
二、代码编写
local WIFI_SSID = “WIFI名称”
local WIFI_PASS = “WIFI密码”
local BEMFA_UID = “巴法云个人私钥”
local TOPIC = “巴法云订阅MQTT主题”
1.舵机初始化与控制
这里我就假设转动两个角度一个是90和0来模拟开灯和关灯,代码如下:
-- 50Hz 周期(20ms)
-- 90度(1.5ms) -> 占空比约 76
pwm.setup(SERVO_PIN, 50, 76)
pwm.start(SERVO_PIN)
function setServoAngle(angle)
if angle == 90 then
pwm.setduty(SERVO_PIN, 76) -- 90度
elseif angle == 0 then
pwm.setduty(SERVO_PIN, 25) -- 0度
end
print("执行动作: 舵机转动到 " .. angle .. " 度")
end
关于占空比的数学换算:
NodeMCU 的 PWM 分辨率为 10 bit(范围 0 - 1023)。
0 ∘ 0^\circ 0∘ 对应 0.5 ms 0.5\text{ms} 0.5ms 脉宽: 0.5 ms 20 ms × 1023 ≈ 25.5 \frac{0.5\text{ms}}{20\text{ms}} \times 1023 \approx 25.5 20ms0.5ms×1023≈25.5(代码取 25)
90 ∘ 90^\circ 90∘ 对应 1.5 ms 1.5\text{ms} 1.5ms 脉宽: 1.5 ms 20 ms × 1023 ≈ 76.7 \frac{1.5\text{ms}}{20\text{ms}} \times 1023 \approx 76.7 20ms1.5ms×1023≈76.7(代码取 76)
这里我进行软件的粗略计算
2.巴法云 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)
-- 事件1:收到云端指令
mqtt_client:on("message", function(client, topic, data)
print("收到控制指令 [" .. topic .. "]: " .. data)
if data == "on" then
setServoAngle(90)
elseif data == "off" then
setServoAngle(0)
end
end)
-- 事件2:掉线重连
mqtt_client:on("offline", function(client)
print("MQTT 已断开连接,5秒后尝试重连...")
tmr.create():alarm(5000, tmr.ALARM_SINGLE, connectBemfa)
end)
-- 发起 TCP 连接
mqtt_client:connect("bemfa.com", 9501, false, function(client)
print("已成功建立 MQTT TCP 连接!")
-- 核心:连接后必须订阅主题
client:subscribe(TOPIC, 0, function(client)
print("成功订阅主题: " .. TOPIC)
end)
end, function(client, reason)
print("MQTT 连接失败,底层错误码: " .. reason)
end)
end
3. WiFi 连接
wifi.setmode(wifi.STATION)
wifi.sta.config({ssid=WIFI_SSID, pwd=WIFI_PASS, auto=true})
-- 监听1:物理连接成功
wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, function(T)
print("已物理连接到路由器,等待 DHCP 分配 IP...")
end)
-- 监听2:成功获取 IP(真正的联网成功)
wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(T)
print("WiFi 连接彻底成功! 设备 IP 地址: " .. T.IP)
connectBemfa() -- 联动触发巴法云连接
end)
-- 监听3:断开或失败(智能诊断)
wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, function(T)
print("WiFi 连接断开或失败! -> 底层错误代码 (Reason): " .. T.reason)
if T.reason == 201 then
print(" -> 诊断: 找不到指定的 WiFi。请检查是否误填了 5G 信号,或名字拼写错误!")
elseif T.reason == 202 then
print(" -> 诊断: WiFi 密码错误!")
elseif T.reason == 200 or T.reason == 203 then
print(" -> 诊断: 路由器拒绝接入或信号太弱。")
end
end)
完整代码
-- ==========================================
-- 1. 全局参数配置
-- ==========================================
local WIFI_SSID = "WIFI名称"
local WIFI_PASS = "WIFI密码"
local BEMFA_UID = "巴法云个人私钥"
local TOPIC = "巴法云订阅MQTT主题"
local SERVO_PIN = 1 -- 对应esp8266的D1引脚
-- ==========================================
-- 2. 舵机初始化与控制函数
-- ==========================================
-- 50Hz 周期(20ms)
-- 0度(0.5ms) -> 占空比约 25
-- 90度(1.5ms) -> 占空比约 76
pwm.setup(SERVO_PIN, 50, 76)
pwm.start(SERVO_PIN)
function setServoAngle(angle)
if angle == 90 then
pwm.setduty(SERVO_PIN, 76) -- 90度
elseif angle == 0 then
pwm.setduty(SERVO_PIN, 25) -- 0度
end
print("执行动作: 舵机转动到 " .. angle .. " 度")
end
-- ==========================================
-- 3. 巴法云 MQTT 连接逻辑
-- ==========================================
local mqtt_client = nil
function connectBemfa()
-- 如果旧实例存在,先清理避免内存泄漏
if mqtt_client ~= nil then
mqtt_client:close()
end
print("正在连接巴法云 MQTT 服务器...")
-- 初始化 Client (Client_ID, KeepAlive, Username, Password)
-- 巴法云规则:这四个参数除了 KeepAlive,其余全部填 UID
mqtt_client = mqtt.Client(BEMFA_UID, 120, BEMFA_UID, BEMFA_UID)
-- 注册:收到云端指令事件
mqtt_client:on("message", function(client, topic, data)
print("收到控制指令 [" .. topic .. "]: " .. data)
if data == "on" then
setServoAngle(90) -- 例如:小爱同学下发打开指令 $\rightarrow$ 舵机转到90度
elseif data == "off" then
setServoAngle(0) -- 例如:小爱同学下发关闭指令 $\rightarrow$ 舵机转到0度
end
end)
-- 注册:掉线重连事件
mqtt_client:on("offline", function(client)
print("MQTT 已断开连接,5秒后尝试重连...")
tmr.create():alarm(5000, tmr.ALARM_SINGLE, connectBemfa)
end)
-- 发起连接 (域名: bemfa.com, 端口: 9501, false表示明文非加密)
mqtt_client:connect("bemfa.com", 9501, false, function(client)
print("已成功建立 MQTT TCP 连接!")
-- 核心步骤:成功连接后必须订阅主题,巴法云才会显示设备在线
client:subscribe(TOPIC, 0, function(client)
print("成功订阅主题: " .. TOPIC)
print("设备已完全上线!现在可以去巴法云网页端或联动音箱发送 on/off 测试了。")
end)
end, function(client, reason)
print("MQTT 连接失败,底层错误码: " .. reason)
end)
end
-- ==========================================
-- 4. WiFi 连接
-- ==========================================
print("====================================")
print("开始配置 WiFi...")
wifi.setmode(wifi.STATION)
wifi.sta.config({ssid=WIFI_SSID, pwd=WIFI_PASS, auto=true})
-- 监听事件:WiFi 成功连接到路由器 (尚未获取IP)
wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, function(T)
print("已物理连接到路由器,等待 DHCP 分配 IP...")
end)
-- 监听事件:成功获取到 IP (此时真正具备网络通信能力)
wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(T)
print("WiFi 连接彻底成功! 设备 IP 地址: " .. T.IP)
connectBemfa() -- 网络通畅后,自动触发巴法云连接
end)
-- 监听事件:WiFi 断开或连接失败 (诊断助手)
wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, function(T)
print("WiFi 连接断开或失败!")
print(" -> 底层错误代码 (Reason): " .. T.reason)
if T.reason == 201 then
print(" -> 诊断: 找不到指定的 WiFi。请检查是否误填了 5G 信号,或名字拼写错误!")
elseif T.reason == 202 then
print(" -> 诊断: WiFi 密码错误!")
elseif T.reason == 200 or T.reason == 203 then
print(" -> 诊断: 路由器拒绝接入或信号太弱。")
end
end)
总结
先烧录好bin文件到esp8266后,在restart,通过vscode打开上面的lua代码文件右键选择Upload activate file to device,就可以连接小爱同学进行语音控制舵机转动!
更多推荐
所有评论(0)