零基础学ESP32:蓝牙控制舵机——手机一点,舵机就转!

上一节课我们学了蓝牙BLE通信,实现了手机和ESP32的数据互传。今天我们把蓝牙和舵机结合起来------用手机APP远程控制舵机转动!

不需要WiFi,不需要插线,手机打开蓝牙,点一下按钮,舵机就转到指定角度。智能小车转向、机器人关节、摄像头云台......这些应用都可以用这个方案实现。

今天这节课,我们就用ESP32 + 蓝牙BLE + 舵机,做一个手机遥控的舵机控制系统!

第一步:找到课程,看看成品效果

打开逗脑IDE,点击顶部 "文档" ,在 "课程" → "ESP32 + MicroPython" 分类下,找到蓝牙控制舵机这节课。

打开课件,你会看到本节课的最终效果:手机通过蓝牙连接ESP32,发送指令后,舵机精准地转到0°、90°、180°,或者可以微调角度。

第二步:手机APP准备

本节课使用安卓手机上的蓝牙调试APP(如"蓝牙工坊",其他支持BLE的APP也可以)。

APP需要具备以下功能:

  • 搜索并连接BLE设备
  • 向特征值写入数据
  • 创建自定义快捷按键(方便发送预设指令)

第三步:动手写代码------ESP32端

python 复制代码
from machine import Pin, Timer, PWM
from time import sleep_ms
import bluetooth

BLE_MSG = ""

class ESP32_BLE():
    def __init__(self, name):
        self.led = Pin(2, Pin.OUT)
        self.timer1 = Timer(0)
        self.name = name
        self.ble = bluetooth.BLE()
        self.ble.active(True)
        self.ble.config(gap_name=name)
        self.disconnected()
        self.ble.irq(self.ble_irq)
        self.register()
        self.advertiser()

    def connected(self):
        self.led.value(1)
        self.timer1.deinit()

    def disconnected(self):
        self.timer1.init(period=100, mode=Timer.PERIODIC,
                         callback=lambda t: self.led.value(not self.led.value()))

    def ble_irq(self, event, data):
        global BLE_MSG
        if event == 1:   # 手机连接了此设备
            self.connected()
        elif event == 2: # 手机断开了此设备
            self.advertiser()
            self.disconnected()
        elif event == 3: # 手机发送了数据
            buffer = self.ble.gatts_read(self.rx)
            BLE_MSG = buffer.decode('UTF-8').strip()

    def register(self):
        service_uuid = '6E400001-B5A3-F393-E0A9-E50E24DCCA9E'
        reader_uuid = '6E400002-B5A3-F393-E0A9-E50E24DCCA9E'
        sender_uuid = '6E400003-B5A3-F393-E0A9-E50E24DCCA9E'
        services = (
            (bluetooth.UUID(service_uuid), (
                (bluetooth.UUID(sender_uuid), bluetooth.FLAG_NOTIFY),
                (bluetooth.UUID(reader_uuid), bluetooth.FLAG_WRITE),
            )),
        )
        ((self.tx, self.rx,), ) = self.ble.gatts_register_services(services)

    def send(self, data):
        self.ble.gatts_notify(0, self.tx, data + '\n')

    def advertiser(self):
        name = bytes(self.name, 'UTF-8')
        adv_data = bytearray('\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name
        self.ble.gap_advertise(100, adv_data)
        print(adv_data)

# 按键中断
def buttons_irq(pin):
    led.value(not led.value())
    print('LED is ON.' if led.value() else 'LED is OFF')
    ble.send('LED is ON.' if led.value() else 'LED is OFF')

if __name__ == "__main__":
    ble = ESP32_BLE("ESP32BLE")
    but = Pin(0, Pin.IN)
    but.irq(trigger=Pin.IRQ_FALLING, handler=buttons_irq)
    led = Pin(2, Pin.OUT)

    # 舵机初始化(GPIO15)
    p15 = PWM(Pin(15))
    p15.freq(50)
    duty_num = 1638
    p15.duty_u16(1638)  # 初始0度

    # 0度   p15.duty_u16(1638)
    # 90度  p15.duty_u16(4915)
    # 180度 p15.duty_u16(8192)

    while True:
        if BLE_MSG == 'read_LED':
            print(BLE_MSG)
            BLE_MSG = ""
            print('LED is ON.' if led.value() else 'LED is OFF')
            ble.send('LED is ON.' if led.value() else 'LED is OFF')
        elif BLE_MSG:
            print("接收到的信息:>>%s<<" % BLE_MSG)
            if BLE_MSG == "!B11:":       # 按下app上数字1
                duty_num += 100
                if duty_num > 8192:
                    duty_num = 8192
                p15.duty_u16(duty_num)
            elif BLE_MSG == "!B219":     # 按下app上数字2
                duty_num -= 100
                if duty_num < 1638:
                    duty_num = 1638
                p15.duty_u16(duty_num)
            elif BLE_MSG == "!B516":     # 按下app上up键
                p15.duty_u16(4915)       # 转到90度
                duty_num = 4915
            elif BLE_MSG == "!B714":     # 按下app上left键
                p15.duty_u16(8192)       # 转到180度
                duty_num = 8192
            elif BLE_MSG == "!B813":     # 按下app上right键
                p15.duty_u16(1638)       # 转到0度
                duty_num = 1638
            BLE_MSG = ""
        sleep_ms(100)

代码核心逻辑:

指令 对应操作 效果
!B11: 按下APP上数字1 角度+100(微调增大)
!B219 按下APP上数字2 角度-100(微调减小)
!B516 按下APP上up键 转到90°
!B714 按下APP上left键 转到180°
!B813 按下APP上right键 转到0°

📌 关键点: 每个按键对应一个特定的指令字符串(如!B516),ESP32收到后执行对应的角度控制。

第四步:保存运行,手机连接蓝牙

保存代码到开发板,命名 ble_servo.py

点击运行按钮,ESP32开始广播BLE信号。

步骤1:连接蓝牙

打开手机APP,搜索到名为 "ESP32BLE" 的设备,点击连接。

步骤2:连接成功

连接成功后,APP显示日志:

复制代码
> 正在连接...
> 连接成功,等待发现服务
> 连接成功,正在发现服务...
> 连接成功,并发现服务
> [6e400003] 通知已开启

详细的蓝牙连接步骤,可以看上一篇文章

第五步:发送指令,控制舵机转动

步骤3:输入操作指令,点击写入

在APP底部选择写入目标 6E400002 ,输入指令(如 !B516),点击 "写入"。

ESP32收到后: 舵机立刻转动到90°,终端打印接收到的信息。

第六步:创建快捷按键,一键控制

每次手动输入指令太麻烦?APP支持创建快捷按键,把常用指令做成按钮,点一下就能发送。

步骤4:创建按键,点击按键控制舵机

在APP中创建三个按键(上、左、右),分别对应:

按键名称 编码内容 对应角度
上 !B516 90°
左 !B714 180°
右 !B813 0°

创建完成后,APP上会出现 "上"、"左"、"右" 三个按键。

点击"上" → 舵机转到90°

点击"左" → 舵机转到180°

点击"右" → 舵机转到0°

连续点击日志:

复制代码
11:04:36.828> [6e400002] !B516
11:04:38.266> [6e400002] !B714
11:04:39.661> [6e400002] !B813

指令与角度对照表

APP操作 发送指令 ESP32执行 舵机角度
数字1 !B11: duty_num += 100 微调增大
数字2 !B219 duty_num -= 100 微调减小
上 !B516 duty_u16(4915) 90°
左 !B714 duty_u16(8192) 180°
右 !B813 duty_u16(1638) 0°

蓝牙控制舵机能做什么?

应用场景 用途
遥控小车 手机控制前轮转向
摄像头云台 手机控制摄像头上下左右转动
机器人关节 手机控制机械臂角度
智能窗帘 手机控制窗帘开合
航模控制 手机控制机翼舵面

全程回顾

步骤 操作 效果
找到课程 文档→课程→ESP32+MicroPython→蓝牙控制舵机 了解项目原理
打开代码 点击"打开"按钮 代码进入编辑器
保存运行 Ctrl+S → 选择ESP32 → 运行 ESP32开始广播
连接蓝牙 手机APP搜索"ESP32BLE"并连接 连接成功
发送指令 向6E400002写入"!B516" 舵机转到90°
创建按键 添加上/左/右三个快捷按键 一键控制
点击按键 点击"左"按键 舵机转到180°
相关推荐
网盘数据实测1 小时前
手机相册自动备份方案选型:2026 本地、云盘与自建 NAS 对比
智能手机·个人开发
清霜之辰2 小时前
骁龙 8397 舱驾融合平台端侧大模型部署:7B 模型本地推理与 64GB 内存账本
android
烽火聊员2 小时前
Android屏幕卡顿traces.txt拉取查看
android·java·android studio
只睡四小时4 小时前
JS 手写 D-pad 空间导航:电视端焦点引擎实战
android·开发语言·前端·javascript·ecmascript·hls·空间导航
恋猫de小郭4 小时前
Android 原生的 Compose A2UI 也来了,你还抱着 XML 养老吗?
android·前端·flutter
程序媛_4 小时前
【JMeter】准备token文件
android·jmeter
维克兜率天4 小时前
【维克】弹性策略:用“乖离率“捕捉超跌反弹
android·开发语言·python·深度学习·kotlin·量化
古法安卓5 小时前
Android-Ext4文件系统问题排查
android·java·android studio
Name_NaN_None5 小时前
电脑/手机QQ屏幕共享详细教程(含跨平台) ——「小白教程」
智能手机·实用技巧·屏幕共享·手机共享屏幕·电脑共享屏幕