本文原创自微信公众号【PY学习笔记】,经过联系并通过开发者的同意,开源分享该项目。
原著链接:点击跳转
前言
在这个快速发展的技术世界中,无线通信已经成为我们日常生活不可或缺的一部分。但是,您有没有想过自己动手实现无线技术,控制一个实实在在的物理设备呢?这不仅仅是对技术理解的一次深化,也是对创造力的一次挑战。 想象一下,如果您能够不借助任何线缆,仅通过手机就能控制一个小车的移动,那会是怎样的体验?如果您想将这种想象变为现实,那么本文章正是您所需要的。 我们将要探索的,是如何利用ESP32S3——一这款具有强大无线通信功能的微控制器——来制作一个可通过蓝牙控制的小车。这不仅仅是关于组装几个部件或编写一些代码那么简单;这是关于理解无线通信如何工作,以及如何将这些抽象的概念应用到实际的物理对象上。 但这个任务并非不无挑战:
- 您将需要理解ESP32S3的工作原理,以及它如何处理无线信号。
- 您需要设计一种方式,让小车能够接收和解析来自手机的蓝牙信号。
- 您需要动手实践,从零开始组装小车,确保所有部件都能正常工作。
所以,如果您热爱技术,渴望挑战自我,想要亲手制作一个既酷炫又实用的科技作品,那么跟随本文章,让我们一起开启这趟学习之旅吧!
项目说明
蓝牙遥控小车我们已经做了好几个了,有《ESP32-C3三轮蓝牙小车、《麦克纳姆轮的四轮小车、《摄像照相蓝牙小车》(未发布)。
本项目利用9.9元到手的立创ESP32S3开发板做了一些创新和改进,做一辆极简主义的蓝牙遥控小车:
极简主义,尽量少用电子元器件,提高集成度,也减少成本。
简化制作,尽量不用贴片电子元器件,采用直插件,减少焊接难度。
实用主义,采用橡胶轮代替麦克纳姆轮,行驶更稳定,噪声更小。
2
3
实物视频效果如下:
硬件设计
1. 主控芯片
立创ESP32S3R8N8开发板,高性能随身WiFi与 蓝牙开发板,所有资料全开源,丰富教程案例,轻松上手,项目式学习。支持ESP-IDF、Arduino IDE、MicroPython等多种开发环境,是创客们的独心宠。
详见官网介绍立创ESP32S3R8N8开发板。
2. 电机驱动
橡胶轮不能像麦克纳姆轮一样左右水平移动,左边的两个轮子和右边的两个轮子都是同向转动的,所以可以将左边两个电机并联组成一组,右边两个电机并联组成一组,只采用一块国产小体积DRV8833电机驱动模块驱动4个直流减速电机。这也是本项目精简最大的地方。
原理图如下:
3. 电源方案
采用两路电源方案,一路为ESP32S3开发板供电,一路为电机供电,避免互相干扰。
- ESP32S3开发板的供电参考《常用开发板的供电设计方案(3.3V、5V供电方案),直接将3.7V锂电池接入ESP32S3开发板的5V引脚,开发板会将3.7V降压到3.3V供MCU使用。
- 电机供电采用一个3.7V锂电池通过DRV8833直接供电,不需要降压。如果要让小车的速度更快,可以考虑采用2个3.7V锂电池供电,7.4V的电压能让小车速度更快。
4. 设计的小车主板(PCB板)
小车主板不使用任何贴片电子元器件,ESP32S3、DRV8833等模块采用2.54排针固定,电池、电机采用2P的2.54接线端子连接,方便焊接、组装,以及元件重复利用。
原理图:
PCB效果图:
开源地址:立创开源广场-我的工程
5. 电机、车轮、固定支架
某宝上买的,电机、车轮、固定支架各4个,不超过15元。
麦克纳姆轮具有随意改变行驶方向的特性,但稳定性较差,容易打滑,而且噪声较大。使用橡胶轮代替后,行驶稳定,且轮胎几乎没有噪声(噪声来自减速电机)。
6. 遥控器
蓝牙区分为经典蓝牙Classic Bluetooth)和低功耗蓝牙(BLE),低功耗蓝牙一般为蓝牙4.0及以上的。
立创ESP32S3开发板支持BLE,本项目使用BLE手机蓝牙APP——Bluefruit Connect,详见 《基于合宙ESP32-C3和MicroPython的蓝牙遥控小(二)
操作示意图:
7. 电池
3.7V锂电池两颗,带XH2.54接线端子。我们用的是从坏的小米电动牙刷上拆下来的。
8. 最终效果展示
正面照片:
背面照片:
硬件说明:
- 立创ESP32S3蓝牙遥控小车不计算PCB打板、运费、人工费,成本大概在30元 (ESP32S3开发板9.9元+DRV8833模块2元+电机、车轮、支架15元+其他排针、线等约3元)。
- 小车主板(PCB板)没有任何贴片电子元器件没有任何贴片电子元器件/span>,只有直插的排针、2P接线端子、开关需要焊接,容易焊接,非常适合新手练习。
- 根据功能需求,可以在小车主板(PCB板)上增加舵机、超声波、红外巡迹、OLED、摄像头等模块,组成超声波避障小车红外巡迹小车等。
软件设计
1. 车轮转向测试
硬件设计时,我们将左边两个电机并联组成一组,右边两个电机并联组成一组,需要先测试两个一组的电机是否能同向转动,并测试出转动方向对应的引脚。 参考测试代码如下:
# 定义电机引脚,Left表示左,Right表示右,1表示向前,0表示向后
Left0 = Pin(15,Pin.OUT)
Left1 = Pin(16,Pin.OUT)
Right0 = Pin(17,Pin.OUT)
Right1 = Pin(18,Pin.OUT)
# 测试
Left1.value(1)
Left0.value(0)
Right1.value(1)
Right0.value(0)
2
3
4
5
6
7
8
9
10
焊接电机电源线时建议用不同颜色的线来区分,要注意两条线要同向,组装到蓝牙遥控小车上时,电源线刚好是相反的。如下图:
如果焊接不同向,需要重新焊接。
2. 行驶函数
确定了车轮转向后,可以编写行驶函数。橡胶轮不能像麦克纳姆轮一样能左右水平移动,所以行驶函数比较简单。参考代码如下:
# 车向前进
def cargo():
Left1.value(1)
Left0.value(0)
Right1.value(1)
Right0.value(0)
# 车向后退
def carback():
Left1.value(0)
Left0.value(1)
Right1.value(0)
Right0.value(1)
# 车停止
def carstop():
Left1.value(0)
Left0.value(0)
Right1.value(0)
Right0.value(0)
# 车向左转
def carleft():
Left1.value(0)
Left0.value(1)
Right1.value(1)
Right0.value(0)
# 车向右转
def carright():
Left1.value(1)
Left0.value(0)
Right1.value(0)
Right0.value(1)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
● 转弯原理:当左边车轮向前,右边车轮向后时,小车右转。左转同理。
3. 蓝牙广播和连接
需要先配置蓝牙(从网上找的代码),让手机BLE蓝牙APP(Bluefruit Connect)能正常连接蓝牙,再测试APP每个按键发送的数据,参考代码如下:
class ESP32_BLE():
#蓝牙初始化
def __init__(self, name):
self.led = Pin(48, Pin.OUT) #配置LED灯引脚为输出模式
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() #配置蓝牙的uuid
self.ble.gatts_write(self.rx, bytes(100))#默认蓝牙只接收20字节,这里更改为接收100字节
self.advertiser() #蓝牙广播
self.ok = 0
#蓝牙连接,关闭LED灯
def connected(self):
self.timer1.deinit()
self.led.value(0)
print("connected ok")
#蓝牙未连接,LED闪烁
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: #_IRQ_CENTRAL_CONNECT 手机连接了此设备
self.connected()
elif event == 2: #_IRQ_CENTRAL_DISCONNECT 手机断开此设备
if self.ok==0:
self.advertiser()
self.disconnected()
elif event == 3: #_IRQ_GATTS_WRITE 手机发送了数据
buffer = self.ble.gatts_read(self.rx)
BLE_MSG = buffer.decode('UTF-8').strip()
#蓝牙UUID配置
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 advertiser(self):
name = bytes(self.name, 'UTF-8')
adv_data = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name
self.ble.gap_advertise(100, adv_data)
print("等待连接:%s" % adv_data)
print("\r\n")
# 配置蓝牙
ble = ESP32_BLE("ESP32S3BLE")
# 配置LED
led = Pin(48, Pin.OUT)
BLE_MSG = ""
while True:
if len(BLE_MSG)>0:
print(BLE_MSG)
BLE_MSG = ""
sleep_ms(100)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
当蓝牙等待连接时LED灯闪烁,当手机蓝牙APP连接到ESP32S3时则关闭闪烁;
● 当手机APP断开连接时,重新进行蓝牙广播,LED灯重新闪烁。
● 蓝牙连接成功后,手机蓝牙APP按下按键发送一组数据,松开按键时再发送另一组数据,程序将蓝牙发送的数据print出来。
测试结果:
4. 实现蓝牙遥控
根据蓝牙发送的数据,使用if、elif决定小车行动(前进、后退、左转、右转、停止),实现遥控功能。参考代码如下:
while True:
if len(BLE_MSG)>0:
if BLE_MSG in ["!B507","!B606","!B705","!B804","!B10;","!B20:","!B309","!B408","stop"]: #松开按键停止
print(">>%s<<————停止" % BLE_MSG)
carstop()
elif BLE_MSG == "!B516": # 按下app的上键向前
print(">>%s<<————向前" % BLE_MSG)
cargo()
elif BLE_MSG == "!B615": # 按下app的下键向后
print(">>%s<<————向后" % BLE_MSG)
carback()
elif BLE_MSG == "!B714": # 按下app的左键向左水平运动
print(">>%s<<————向左" % BLE_MSG)
carleft()
elif BLE_MSG == "!B813": # 按下app的右键向右水平运动
print(">>%s<<————向右" % BLE_MSG)
carright()
BLE_MSG = ""
sleep_ms(100)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
● 所有松开按键时蓝牙发送的数据都要放到停止的if语句中,才能实现按下按键行动,松开按键停止。
5. 完整代码
from machine import Pin
from machine import Timer
from time import sleep_ms
import time
import bluetooth
# 定义电机引脚,Left表示左,Right表示右,1表示向前,0表示向后
Left0 = Pin(15,Pin.OUT) # GPIO15引脚,左轮电机向后
Left1 = Pin(16,Pin.OUT) # GPIO16引脚,左轮电机向前
Right0 = Pin(17,Pin.OUT) # GPIO17引脚,右轮电机向后
Right1 = Pin(18,Pin.OUT) # GPIO18引脚,右轮电机向前
BLE_MSG = ""
# 车向前进
def cargo():
Left1.value(1)
Left0.value(0)
Right1.value(1)
Right0.value(0)
# 车向后退
def carback():
Left1.value(0)
Left0.value(1)
Right1.value(0)
Right0.value(1)
# 车停止
def carstop():
Left1.value(0)
Left0.value(0)
Right1.value(0)
Right0.value(0)
# 车向左转
def carleft():
Left1.value(0)
Left0.value(1)
Right1.value(1)
Right0.value(0)
# 车向右转
def carright():
Left1.value(1)
Left0.value(0)
Right1.value(0)
Right0.value(1)
class ESP32_BLE():
#与步骤3重复,请自行复制
def main():
global BLE_MSG
try:
# 车先待命
carstop()
# 配置蓝牙
ble = ESP32_BLE("ESP32S3BLE")
# 配置LED
led = Pin(48, Pin.OUT)
while True:
if len(BLE_MSG)>0:
if BLE_MSG in ["!B507","!B606","!B705","!B804","!B10;","!B20:","!B309","!B408","stop"]: #松开按键停止
print(">>%s<<————停止" % BLE_MSG)
carstop()
elif BLE_MSG == "!B516": # 按下app的上键向前
print(">>%s<<————向前" % BLE_MSG)
cargo()
elif BLE_MSG == "!B615": # 按下app的下键向后
print(">>%s<<————向后" % BLE_MSG)
carback()
elif BLE_MSG == "!B714": # 按下app的左键向左水平运动
print(">>%s<<————向左" % BLE_MSG)
carleft()
elif BLE_MSG == "!B813": # 按下app的右键向右水平运动
print(">>%s<<————向右" % BLE_MSG)
carright()
BLE_MSG = ""
sleep_ms(100)
except KeyboardInterrupt:
pass
if __name__ == "__main__":
main()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92