ESP32使用MicroPython设置舵机[SG90 9G] 180度完成蓝牙开门

NO.1
使用场景

已使用智能门锁的,可以忽略本文

老式防盗门锁

此图片的alt属性为空;文件名为image-355-1024x768.png

当时使用小米的微信小程序拍照判断是否可以安装智能门锁,结果是不符合

中间遇到过一次出门丢个垃圾,刚出门关门发现钥匙忘记带了,没有放备用钥匙在门外,只好等朋友带备用钥匙过来(没有叫二房东帮忙,是因为二房东神出鬼没,等愿意带钥匙过来,天都黑了)

NO.2
物理开门

钥匙忘记带,手机一般不会忘

本文使用ESP32+MicroPython设置蓝牙控制舵机,达到开门的目的

NO.3
物品准备

完整老式门锁开门需要以下物品

  1. 一个ESP32开发板(成本25元左右,后期可换为12元左右的ESP8266)

  2. 一个SG90舵机(其他舵机也行,能操作转动就行)(成本8元左右)

  3. 程序开发辅助用品:USB数据线,杜邦线(默认随开发板配送)

  4. 门锁设置辅助用品:无痕双面胶,缝衣线(总计10元左右,如已有,则忽略成本)

此图片的alt属性为空;文件名为image-356-1024x767.png

此图片的alt属性为空;文件名为image-357-1024x767.png

NO.4
控制舵机

接线

  1. 红色电源线接5v5(5v)

  2. 灰色线接电源(GND)

  3. 橙色线接引脚GPIO13(本文代码设置的为P13)

此图片的alt属性为空;文件名为image-358.png

当门锁关闭时,舵机固定机翼在起始位置,也就是贴近门锁

只有当收到角度数据时,循环指定角度如180度,带动线的拉动,达成开锁条件,开锁后几秒自动返回初始位置,避免因为电机阻力,一直处理开锁状态,无法手动关闭

先放入依赖库到ESP32

文件名

servo.py

from machine import PWM
import math

# originally by Radomir Dopieralski http://sheep.art.pl
# from https://bitbucket.org/thesheep/micropython-servo

class Servo:
    """
    A simple class for controlling hobby servos.
    Args:
        pin (machine.Pin): The pin where servo is connected. Must support PWM.
        freq (int): The frequency of the signal, in hertz.
        min_us (int): The minimum signal length supported by the servo.
        max_us (int): The maximum signal length supported by the servo.
        angle (int): The angle between the minimum and maximum positions.
    """
    def __init__(self, pin, freq=50, min_us=600, max_us=2400, angle=180):
        self.min_us = min_us
        self.max_us = max_us
        self.us = 0
        self.freq = freq
        self.angle = angle
        self.pwm = PWM(pin, freq=freq, duty=0)

    def write_us(self, us):
        """Set the signal to be ``us`` microseconds long. Zero disables it."""
        if us == 0:
            self.pwm.duty(0)
            return
        us = min(self.max_us, max(self.min_us, us))
        duty = us * 1024 * self.freq // 1000000
        self.pwm.duty(duty)

    def write_angle(self, degrees=None, radians=None):
        """Move to the specified angle in ``degrees`` or ``radians``."""
        if degrees is None:
            degrees = math.degrees(radians)
        degrees = degrees % 360
        total_range = self.max_us - self.min_us
        us = self.min_us + total_range * degrees // self.angle
        self.write_us(us)

然后设置默认执行文件main.py

参考代码,可以将角度180,设置为自己需要的角度,随时变换0-180

from machine import Pin
from servo import Servo
# 定义舵机控制对象
my_servo = Servo(Pin(13), max_us=2500)
# 设置舵机转到指定角度
my_servo.write_angle(180)
NO.5
门锁设置

用无痕双面胶把舵机固定在可以开锁的位置

使用缝衣线一端勾住门把手,一端穿过舵机机翼的小孔并打结固定

注意

选定固定位置前,先实际通电测试开锁的最佳位置并做好标记,然后贴上无痕双面胶

此图片的alt属性为空;文件名为image-359.png

NO.6
蓝牙开门

本期所有的操作都从简单的来

之前写过ESP32蓝牙通信,并且已经写微信调试小程序

参考文章

ESP32使用MicroPython设置低功耗蓝牙广播,通过Chrome Web蓝牙通信
ESP32使用MicroPython设置低功耗蓝牙广播,通过微信小程序蓝牙通信

通过以下步骤

  1. 设置ESP32蓝牙广播

  2. 设置监听蓝牙广播收到字符前缀为deg时,执行字符过滤角度数据,并设置对应舵机转动

  3. 使用已写好的微信小程序DIY硬件 直接手动发送字符测试和开门(后期再优化更为方便的可视化操作)

在蓝牙接收字符的交互中添加判断

# 如果接收到字符为deg前缀,就操作舵机
if message.startswith('deg'):
    # 打印要转动的角度
    print('deg',message,message[3:])
    # 获取要转动的角度(数字)
    deg_num=int(message[3:])
    # 转动舵机
    my_servo.write_angle(deg_num)
    # 蓝牙返回数据
    ble.send(message)

完整代码

import time
from machine import Pin
from servo import Servo

# 引入依赖
import ubluetooth

# 定义舵机控制对象
my_servo = Servo(Pin(13), max_us=2500)



# 实例化蓝牙
class BLE():
    def __init__(self, name):   
        # 蓝牙名称
        self.name = name
        # 创建蓝牙实例
        self.ble = ubluetooth.BLE()
        # 开启蓝牙
        self.ble.active(True)
        # 蓝牙事件回调
        # 参考文档
        # https://docs.micropython.org/en/latest/library/bluetooth.html?highlight=irq
        self.ble.irq(self.ble_irq)
        # 配置蓝牙UUID
        self.register()
        # 特征和描述符的默认最大大小为 20 个字节,修改允许为100个字节(蓝牙数据的发送和接收字节大小限制)
        self.ble.gatts_write(self.rx, bytes(100))
        # 蓝牙广播
        self.advertiser()
        print("已开启蓝牙广播")

    # 蓝牙连接成功后回调
    def connected(self):        
        print("connected")
    # 蓝牙断开连接后回调
    def disconnected(self):        
        print("disconnected")  

    # 蓝牙事件回调函数
    def ble_irq(self, event, data):
        #蓝牙已连接
        if event == 1:
            print("蓝牙已连接")
            # 连接后的执行函数
            self.connected()
        #蓝牙已断开连接
        elif event == 2:
            print("蓝牙已断开连接")
            # 断开连接后的执行函数
            self.advertiser()
            self.disconnected()
        #蓝牙已发送数据
        elif event == 3 :
            print("蓝牙已接收到数据")        
            # 读取二进制数据
            buffer = self.ble.gatts_read(self.rx)
            # 使用UTF-8格式把二进制数据转为字符串
            message = buffer.decode('UTF-8').strip()
            # 打印收到的字符数据
            print("message",message)    
            # 对指定的数据做处理并蓝牙返回数据        
            if message == 'test':
                print('test')
                ble.send('test')
            if message == 'str':
                print('str')
                ble.send('str')
            if message.startswith('deg'):
                print('deg',message,message[3:])
                deg_num=int(message[3:])
                my_servo.write_angle(deg_num)
                ble.send(message)

    # 注册蓝牙UUID
    def register(self):        
        # 自定义UUID
        # 蓝牙服务UUID service_uuid(后续蓝牙建议连接会用到)
        NUS_UUID = 'AE25A5C4-4601-143C-12BB-8BC45A18749C'
        # 蓝牙接收特征UUId receive_uuid
        RX_UUID = 'AE25A5C5-4601-143C-12BB-8BC45A18749C'
        # 蓝牙发送特征UUId transmit_uuid
        TX_UUID = 'AE25A5C6-4601-143C-12BB-8BC45A18749C'
        # UUID组合(一个包含UUID和特征列表的二元元组)
        BLE_NUS = ubluetooth.UUID(NUS_UUID)
        BLE_RX = (ubluetooth.UUID(RX_UUID), ubluetooth.FLAG_WRITE)
        BLE_TX = (ubluetooth.UUID(TX_UUID), ubluetooth.FLAG_NOTIFY)
        BLE_UART = (BLE_NUS, (BLE_TX, BLE_RX,))
        SERVICES = (BLE_UART, )
        # 使用指定的服务配置外围设备
        # 文档地址:
        # https://docs.micropython.org/en/latest/library/bluetooth.html?highlight=irq#peripheral-role
        ((self.tx, self.rx,), ) = self.ble.gatts_register_services(SERVICES)
    # 发送数据
    def send(self, data):
        # 向连接的客户端发送通知请求
        # 文档地址:
        # https://docs.micropython.org/en/latest/library/bluetooth.html?highlight=irq#gatt-client
        self.ble.gatts_notify(0, self.tx, data + '\n')
    # 蓝牙广播配置
    def advertiser(self):
        name = bytes(self.name, 'UTF-8')
        # 以指定的时间间隔(以微秒为单位)开始广播
        # 文档地址
        # https://docs.micropython.org/en/latest/library/bluetooth.html?highlight=irq#broadcaster-role-advertiser
        self.ble.gap_advertise(100, bytearray('\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name)

# 创建一个名为ESP32的蓝牙广播
ble = BLE("Door")

Tips

本文有需要可以优化的地方

一.使用伸缩/直轴/往复马达电机

二.使用除了缝衣线以外的其他质量好的线,橡皮筋,尼龙线等

此图片的alt属性为空;文件名为image-360.png

三.设置专用微信小程序,app,web网页等,一键开门,无需填写deg角度参数

四.电池盒,锂电池等外置电源

无需时刻插座通电,只需要加电池,不用时设置睡眠模式,使用时唤醒,保持可持续时间三个月甚至半年以上使用,不用在门附近走线,直接和模块贴在门上

NO.8
问题汇总

掉电检测器被触发

错误提示信息:

Brownout detector was triggered

这个是在操作舵机转动一次后,就一定会报这个错

可能的原因

  • USB 电缆质量差或太长。

  • 您计算机的 USB 端口无法为开发板提供足够的电力。

  • ESP32Cam 有缺陷

  • 电路中的其他组件未正确接线,影响电源

本文先试减少了杜邦线的长度,确保线路尽可能短,但是无用

其次更换了USB数据线,更短和质量更好的,但是无用

最后把USB电源从电脑的USB改为了充电宝或者插座上的USB,就可以了,所以本文是供电不足或者电压电流问题

如果遇到上述问题,可尝试更换电源

END.