news 2026/6/25 12:41:07

小爱同学语音控制 ESP8266 舵机转动(NodeMCU-tool Lua 开发实战记录)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
小爱同学语音控制 ESP8266 舵机转动(NodeMCU-tool Lua 开发实战记录)

本篇博客将完整记录如何使用 ESP8266 (NodeMCU) 固件,利用Lua 语言配合巴法云,最终实现小爱同学语音控制舵机转动的全流程。

文章目录

  • 准备工作
  • 一、开发lua环境搭建
  • 二、代码编写
    • 1.舵机初始化与控制
    • 2.巴法云 MQTT 连接逻辑
    • 3. WiFi 连接
  • 完整代码
  • 总结

准备工作

  1. 硬件接线 (以 SG90 舵机为例)
    红线 (VCC) ➡️ ESP8266 的 3V3(若动力不足可接 VIN)
    棕线 (GND) ➡️ ESP8266 的 GND
    橙线 (信号线) ➡️ ESP8266 的 D1 引脚(对应 Lua 底层 GPIO 5)
  2. 巴法云与米家端配置
    由于米家生态不对个人开发者直接开放,我们需要通过巴法云进行 MQTT/TCP中转:
    a. 注册巴法云:登录后台拿到你的唯一密钥 Private_Key(私钥)。
    b. 新建 MQTT设备云:新建一个主题,命名为 9nfwp3dxs002。
    c. 修改昵称:在巴法云后台将该主题的昵称修改为 “舵机” 或 “灯”等随便名字(小爱同学以此名字来识别语音指令控制舵机)。
    d. 绑定米家:打开手机 米家App ➡️ 我的 ➡️ 其他平台设备 ➡️ 绑定“巴法” 账号。随后在列表中找到该设备,分配房间并同步。

一、开发lua环境搭建

  1. 如果要使用nodemcu固件的烧录且使用Lua在esp8266上跑,需要先下载定制固件,由于我需要连接mqtt和使用舵机,所以我额外勾选MQTT和PWM。然后填写自己的邮箱就可以Start your bulid,注意是第二封finish邮件中找到
    上述两个的bin文件任意一个都行,integer稍微大小要小一点。
  2. 使用 NodeMCU PyFlasher 写入固件。
    NodeMCU firmware:点击 Browse,选择你下载好的 .bin 固件文件
    Baud rate波特率:选择 115200
    Flash mode:选择 Dual I/O (DIO)
  3. 直接在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)->占空比约76pwm.setup(SERVO_PIN,50,76)pwm.start(SERVO_PIN)functionsetServoAngle(angle)ifangle==90then pwm.setduty(SERVO_PIN,76)--90度 elseif angle==0then pwm.setduty(SERVO_PIN,25)--0度 endprint("执行动作: 舵机转动到 "..angle.." 度")end

关于占空比的数学换算:
NodeMCU 的 PWM 分辨率为 10 bit(范围 0 - 1023)。
0 ∘ 0^\circ0对应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.520ms0.5ms×102325.5(代码取 25)
90 ∘ 90^\circ90对应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.720ms1.5ms×102376.7(代码取 76)
这里我进行软件的粗略计算

2.巴法云 MQTT 连接逻辑

巴法云是一个个人开发者的免费物联网云平台。本段函数处理了从“握手连接”到“订阅监听”的全套逻辑。代码如下:

local mqtt_client=nil functionconnectBemfa()ifmqtt_client~=nil then mqtt_client:close()--清理旧实例,防止内存泄漏 endprint("正在连接巴法云 MQTT 服务器...")mqtt_client=mqtt.Client(BEMFA_UID,120,BEMFA_UID,BEMFA_UID)--事件1:收到云端指令 mqtt_client:on("message",function(client,topic,data)print("收到控制指令 ["..topic.."]: "..data)ifdata=="on"thensetServoAngle(90)elseif data=="off"thensetServoAngle(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)ifT.reason==201thenprint(" -> 诊断: 找不到指定的 WiFi。请检查是否误填了 5G 信号,或名字拼写错误!")elseif T.reason==202thenprint(" -> 诊断: WiFi 密码错误!")elseif T.reason==200or T.reason==203thenprint(" -> 诊断: 路由器拒绝接入或信号太弱。")end end)

完整代码

--==========================================--1. 全局参数配置 --==========================================localWIFI_SSID="WIFI名称"localWIFI_PASS="WIFI密码"localBEMFA_UID="巴法云个人私钥"localTOPIC="巴法云订阅MQTT主题"localSERVO_PIN=1-- 对应esp8266的D1引脚 --==========================================--2. 舵机初始化与控制函数 --==========================================-- 50Hz 周期(20ms)--0(0.5ms)->占空比约25--90(1.5ms)->占空比约76pwm.setup(SERVO_PIN,50,76)pwm.start(SERVO_PIN)functionsetServoAngle(angle)ifangle==90thenpwm.setduty(SERVO_PIN,76)--90度 elseif angle==0thenpwm.setduty(SERVO_PIN,25)--0度 end print("执行动作: 舵机转动到 "..angle.." 度")end --==========================================--3. 巴法云 MQTT 连接逻辑 --==========================================localmqtt_client=nilfunctionconnectBemfa()-- 如果旧实例存在,先清理避免内存泄漏ifmqtt_client ~=nilthenmqtt_client:close()end print("正在连接巴法云 MQTT 服务器...")-- 初始化 Client(Client_ID, KeepAlive, Username, Password)-- 巴法云规则:这四个参数除了 KeepAlive,其余全部填UIDmqtt_client=mqtt.Client(BEMFA_UID,120, BEMFA_UID, BEMFA_UID)-- 注册:收到云端指令事件 mqtt_client:on("message", function(client, topic, data)print("收到控制指令 ["..topic.."]: "..data)ifdata=="on"thensetServoAngle(90)-- 例如:小爱同学下发打开指令 $\rightarrow$ 舵机转到90度 elseif data=="off"thensetServoAngle(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)ifT.reason==201thenprint(" -> 诊断: 找不到指定的 WiFi。请检查是否误填了 5G 信号,或名字拼写错误!")elseif T.reason==202thenprint(" -> 诊断: WiFi 密码错误!")elseif T.reason==200or T.reason==203thenprint(" -> 诊断: 路由器拒绝接入或信号太弱。")end end)

总结

先烧录好bin文件到esp8266后,在restart,通过vscode打开上面的lua代码文件右键选择Upload activate file to device,就可以连接小爱同学进行语音控制舵机转动!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/25 12:37:47

027、反激变换器的PCB布局要点

027、反激变换器的PCB布局要点 去年夏天,一个12V/3A的适配器项目让我在实验室熬了三个通宵。输出纹波始终压不下去,满载时MOS管温升比仿真高了15度,更诡异的是——轻载时居然有间歇性啸叫。换了三版变压器、调了两轮环路补偿,问题纹丝不动。最后用热成像仪扫了一遍板子,才…

作者头像 李华
网站建设 2026/6/25 12:37:40

GEO内容结构化技术是什么?如何让AI精准提取和引用品牌信息?

GEO内容结构化技术底层逻辑全解析 引言 GEO(Generative Engine Optimization,生成式引擎优化)内容结构化技术,是指通过语义标注、知识图谱构建、自然语言问答对设计等手段,将品牌内容转化为AI模型可精准解析、检索和引…

作者头像 李华
网站建设 2026/6/25 12:34:05

一次不等式不是范围限制算式,是双螺旋生长轨迹在零基准单侧延伸的区间形态-《全域数学vs传统数学:人类文明进阶200讲》第44讲 中学通俗版逐字稿

作者: 乖乖数学 《全域数学vs传统数学:人类文明进阶200讲》第44讲 中学通俗版逐字稿讲次: 第44讲 主题: 一次不等式不是范围限制算式,是双螺旋生长轨迹在零基准单侧延伸的区间形态 对标课本知识点: 一元一次…

作者头像 李华
网站建设 2026/6/25 12:32:46

092、NPU的虚拟地址支持:MMU与IOMMU

092、NPU的虚拟地址支持:MMU与IOMMU 去年做一款AI摄像头方案时,遇到一个诡异的NPU崩溃问题。模型推理到第37帧,NPU突然报“page fault”,然后整个系统硬复位。查了两天,最后发现是NPU访问的输入缓冲区物理地址跨了4MB边界,而NPU内部的MMU页表只配置了连续虚拟地址映射,…

作者头像 李华
网站建设 2026/6/25 12:30:58

5个实战技巧:专业配置暗黑破坏神2存档编辑器

5个实战技巧:专业配置暗黑破坏神2存档编辑器 【免费下载链接】d2s-editor 项目地址: https://gitcode.com/gh_mirrors/d2/d2s-editor 暗黑破坏神2存档编辑器d2s-editor是一款基于Vue.js开发的Web应用程序,专门用于修改暗黑破坏神2的角色存档文件…

作者头像 李华