三、软件介绍1)遥控器

我们做了一个微信小程序,用来控制蓝牙小车行动并实时显示摄像头拍摄的画面,详见《》。

使用效果(摄像遥控小车怼扫地机器人):

2)小车程序

小车程序一共包含电机控制、摄像头、SD卡、OLED等模块。因小车需要使用蓝牙遥控,并使用WIFI进行图像传输,所以我们使用了双线程,分别控制,避免干扰。程序代码分解如下:

① 导入相关库,调整CPU频率:

from machine import Pin,freq,SDCard,Timer,SoftI2Cfrom time import sleep_ms,sleepimport bluetooth,network,camera,_thread,ssd1306,ufontfrom microdot import Microdot
# 设置CPU的工作频率为 240 MHzfreq(240000000)

设置CPU的频率可以提高图像效果。

② 初始化OLED:

# 初始化OLEDi2c = SoftI2C(scl=Pin(4), sda=Pin(5))           display = ssd1306.SSD1306_I2C(128, 64, i2c)       # 定义oledfont = ufont.BMFont("unifont-14-12917-16.v3.bmf") # 定义字体库

相关驱动详见《》。

③ 初始化SD卡,使用SDIO协议:

# 初始化SD卡sd = SDCard(    slot=1 , width=1, cd=None, wp=None,    sck=1,miso=3,mosi=2,    cs=None ,freq=20000000    )                                             #定义SD卡os.mount(sd, '/sd')                               #挂载SD卡

参考《》。

④ 定义小车电机引脚和动作函数:

''' 定义小车电机引脚和动作函数 '''Left0 = Pin(6,Pin.OUT)      # O6引脚,左前轮电机向前Left1 = Pin(7,Pin.OUT)     # O7引脚,左前轮电机向后Right0 = Pin(41,Pin.OUT)     # 41引脚,右前轮电机向前Right1 = Pin(42,Pin.OUT)      # 42引脚,右前轮电机向后
# 车向前进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)

参考《》。

⑤ 初始化摄像头,定义拍照、网页图传相关函数:

i=0'''定义网页图传相关函数,以及一些定义'''#拍照def take_picture():    global i    i = i+1    buf = camera.capture()    with open('/sd/{}.jpg'.format(i),'wb') as f:        f.write(buf)    print("拍照成功!")    sleep(0.01)
def connect(): wlan = network.WLAN(network.STA_IF)# 定义模式为连接热点 wlan.active(True) if not wlan.isconnected(): #是否连接热点        wlan.connect('SSID''password'#连接网络,SSID和password改成目前的2.4G网络的名称(SSID)和密码(password)        while not wlan.isconnected(): pass
connect() # 联网wlan = network.WLAN(network.STA_IF)# 定义模式为连接热点ifconfig = wlan.ifconfig() # 获取网络信息font.text(display, '请在浏览器打开:', 0, 0, show=True,clear=True, auto_wrap=True)font.text(display, str('{}:5000'.format(ifconfig[0])), 0, 16,show=True, auto_wrap=True) # 提示网页图传的对应网址app = Microdot() # 定义构建网站变量。

# 初始化摄像头cam = camera.init(0, d0=38, d1=47, d2=14, d3=13, d4=12, d5=11, d6=10, d7=9, format=camera.JPEG, framesize=camera.FRAME_HVGA,fb_location=camera.PSRAM, xclk_freq=camera.XCLK_20MHz, href=18, vsync=17, reset=8, pwdn=-1, sioc=16, siod=15, xclk=40, pclk=39)#y2-d0#调整camera参数 camera.brightness(2)camera.contrast(2)camera.quality(10)camera.speffect(camera.EFFECT_NONE)#camera.whitebalance(camera.WB_CLOUDY)

# 定义网站html代码。@app.route('/')def index(request): return ''' ESP32S3蓝牙小车网页图传

ESP32S3蓝牙小车网页图传:

''', 200, {'Content-Type': 'text/html; charset=utf-8'}
# 网页图传@app.route('/video_feed')def video_feed(request): def stream(): yield b'--framern' while True: frame = camera.capture() yield b'Content-Type: image/jpegrnrn' + frame + b'rn--framern'
return stream(), 200, {'Content-Type': 'multipart/x-mixed-replace; boundary=frame'}

参考《》。

⑥ 初始化蓝牙模块:

BLE_MSG = ""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'x02x01x02') + bytearray((len(name) + 1, 0x09)) + name self.ble.gap_advertise(100, adv_data) print("等待连接:%s" % adv_data) print("rn")

⑦ 定义线程1,启动网页图传:

'''定义线程1网页图传函数'''def main_cam(*args, **kwargs):    app.run(debug=True)#启动网页,并执行前面的网页图传相关函数

⑧ 定义线程2,启动蓝牙小车:

'''定义线程2蓝牙小车函数'''def main_car(*args, **kwargs):    global BLE_MSG,e        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 in ["!B516","upup"]: # 按下小程序的上键向前 print(">>%s<<————向前" % BLE_MSG) cargo() elif BLE_MSG in ["!B615","down"]: # 按下小程序的下键向后 print(">>%s<<————向后" % BLE_MSG) carback() elif BLE_MSG in ["!B714","left"]: # 按下小程序的左键向左运动 print(">>%s<<————向左" % BLE_MSG) carleft()                elif BLE_MSG in ["!B813","right"]:  # 按下小程序的右键向右运动 print(">>%s<<————向右" % BLE_MSG) carright() elif BLE_MSG in ["!B11:","act1"]: # 按下小程序的A键拍照 print(">>%s<<————拍照" % BLE_MSG) take_picture() BLE_MSG = "" sleep_ms(100) except KeyboardInterrupt: pass

按键A用来拍照,并保存一张照片到SD卡上。按键B暂时没有设计。

⑨ 最后是主函数和执行代码:

'''定义主函数'''def main():    thread_1 = _thread.start_new_thread(main_cam, (None,)) #定义线程1    thread_2 = _thread.start_new_thread(main_car, (None,)) #定义线程2    
'''运行'''if __name__ == "__main__": try: main() except KeyboardInterrupt: #当出现KeyboardInterrupt异常 camera.deinit() #释放摄像头避免运行第二次出先错误提示 print("ok")

四、测试效果

摄像遥控小车寻找躲在床底下的猫猫:

测试视频(农田探险):