单片机/MCUwilliam hill官网
直播中

jf_56450220

未满1年用户 14经验值
擅长:嵌入式技术
私信 关注

【xG24 Matter开发套件试用体验】蓝牙密码柜之舵机、蓝牙的驱动以及RTOS的引入

前言

首先需要对项目名称进行纠正,起初以“物联网密码柜”来命名项目是因为我误以为蓝牙连接也属于物联网的一种,后来得知并不是,故特此将项目名称改为“蓝牙密码柜”。

截至上个阶段,已经实现了对矩阵键盘和OLED显示屏的驱动以及密码校验部分。本阶段的任务:

  • 引入菜单逻辑,并优化矩阵键盘响应事件,根据所处不同场景响应对应事件
  • 优化OLED显示方法,设计简单UI
  • 驱动舵机,结合机械结构设计实现开锁、关锁的目的
  • 驱动蓝牙,可通过其它蓝牙设备进行开锁、关锁
  • 优化代码架构,引入RTOS,提高系统可维护性

菜单逻辑

上个阶段仅实现了密码输入单一界面的功能,本阶段会对其它界面的功能进行完善。

菜单基本结构如下:

  • 英国威廉希尔公司网站 (密码输入界面)
  • 设置页面
    • 主题修改(其实就是把背景颜色和文字颜色取反,类似于白天/黑夜主题切换)
    • 亮度修改
    • 密码修改

现在介绍一下各个界面之间的切换逻辑,在英国威廉希尔公司网站 长按‘ENTER’进入设置页面,在设置页面通过方向键(此页面的逻辑为2468按键对应方向键)切换设置项,如图,会有光标提示。

(顺带一提,上次的PCB布局不太合理,导致接线乱成一团,所以这次重新打了PCB)

微信图片_20240903211628.jpg

在设置页面,如果按下‘ENTER’,则会进入对应的设置项目;如果按下‘BACK’,则会返回到英国威廉希尔公司网站 。

在特定设置项目下,如果按下‘ENTER’,会保存当前的设置,并返回到设置页面;如果按下‘BACK’,则会舍弃当前的设置,并返回到设置页面。


矩阵键盘响应优化

上个阶段,仅仅考虑了密码输入和校验功能,所以矩阵键盘的事件响应设计的并不是很合理,主要体现在按键按下直接驱动事件,无法根据不同场景响应不同事件。正确的做法应该是按键按下仅返回按下的按键,由另外的事件管理器决定应该调用什么方法。

优化后的按键检测方法如下:

def scan_keyboard(self):
        """扫描矩阵键盘并返回按下的按键(row, col),支持单击和长按"""
        # 逐行扫描
        for i, row in enumerate(self.rows):
            # 当前行IO拉低
            row.value = False
            # 逐列检查
            for j, col in enumerate(self.cols):
                current_key = (i, j)
                # 按键按下
                if not col.value:
                    if self.press_time is None:
                        # 第一次检测到按键按下,记录时间
                        self.press_time = time.monotonic()
                    else:
                        # 已经记录了按键时间
                        press_duration = time.monotonic() - self.press_time
                        if press_duration > self.long_press_threshold:
                            # 长按事件
                            #print(f" {current_key} 长按")
                            self.press_time = None
                            self.pressed_key = None
                            return 'long_press', current_key
                # 按键释放
                else:
                    if self.press_time is not None:
                        # 单击事件
                        press_duration = time.monotonic() - self.press_time
                        if press_duration <= self.long_press_threshold:
                            #print(f" {current_key} 短按")
                            self.press_time = None
                            return 'short_press', current_key
                        else:
                            # 避免长按后再次触发单击
                            self.press_time = None
                            self.pressed_key = None
            # 重新拉高行IO
            row.value = True
        # 没有按键按下
        self.press_time = None
        return None

这个阶段引入了设置页面,需要有交互入口,由于没有多余的按键,所以选择用‘ENTER’键的长按事件来进入设置页面。


OLED简单UI

本阶段为OLED屏幕显示添加了简单的UI,比较遗憾的是这次还是没能为OLED屏幕显示方法加上中文字库。我所使用的adafruit_ssd1306库基于adafruit的framebuffer,字库是直接读取一个font5x8.bin文件,几经查找,也没能找到关于修改字库或添加中文的资料,只好继续使用英文显示。

核心代码如下:

# 定义切换主题方法
def change_theme(self, bgColor=BLACK):
    self.bgColor = bgColor
    
# 定义修改亮度方法
def set_brightness(self, brightness=127):
    #这里通过修改对比度达到提高亮度的效果
    self.oled.contrast(brightness)
    
# 定义显示设置界面方法,光标默认在第一个设置项上
def show_settings(self, index=0):
    # 显示设置项
    for i, setting in enumerate(settings):
        y_position = 8 + (i*16)
        self.oled.text(setting, 12, y_position, not self.bgColor, font_name='font5x8.bin', size=1)
    cursor_position = 8 + (index*16)
    self.oled.text("*", 2, cursor_position, not self.bgColor, font_name='font5x8.bin', size=1)
    self.oled.show()

上个阶段提到,我购买的OLED屏幕是1315驱动的,但是所找到的驱动只有1306比较接近。这次怕优化OLED显示方法出现不兼容的问题,还特意重新买了1306驱动的OLED屏幕。

微信图片_20240903213011.jpg


驱动舵机

密码柜的实现中,有一个很关键的点就是应该如何实现“开锁”、“关锁”,舵机是一个很好的选择,驱动起来简单,价格也便宜。

驱动舵机的核心代码如下:

# 初始化ServoKit对象,指定使用的通道数
kit = ServoKit(channels=16)

# 定义舵机连接的通道
servo_channel = 0

# 定义打开方法,使舵机转到90度
def open_servo():
    kit.servo[servo_channel].angle = 90

# 定义关闭方法,使舵机转到0度
def close_servo():
    kit.servo[servo_channel].angle = 0

驱动蓝牙

Silicon Labs xG24 Matter开发套件支持低功耗蓝牙协议BLE,非常适合作为蓝牙密码柜的主控。

首先到circuitpython的官方驱动库中找到BLE相关的驱动,下载和上传代码流程和之前一样,此处不再重复演示。

Snipaste_2024-09-03_21-39-46.jpg

驱动BLE的核心代码如下:

def start_bluetooth(self):
        # 启动蓝牙服务并监听命令
        #print("启动蓝牙服务...")
        while True:
            if not self.uart_connection:
                #print("尝试连接...")
                for adv in self.ble.start_scan(ProvideServicesAdvertisement):
                    if UARTService in adv.services:
                        self.uart_connection = self.ble.connect(adv)
                        #print("已连接")
                        break
                self.ble.stop_scan()
            
            if self.uart_connection and self.uart_connection.connected:
                uart_service = self.uart_connection[UARTService]
                while uart_service.in_waiting:
                    # 读取蓝牙接收的指令
                    command = uart_service.readline().decode('utf-8').strip()
                    if command == "open":
                        self.open_servo()
                        #print("接收到开锁指令")
                    elif command == "close":
                        self.close_servo()
                        #print("接收到关锁指令")
            else:
                # 如果连接断开,则重置连接变量
                self.uart_connection = None
                time.sleep(1)

引入RTOS

在编写各个功能代码的过程中,发现功能多起来之后各个功能之间有些地方会有些冲突,比如按键响应事件通过延时进行消除抖动,但这种延时的操作会造成系统资源闲置,耽误了其它事件的响应。于是决定引入RTOS,由操作系统统一管理任务,避免事件之间的冲突,同时也能提高系统的可维护性。

我在circuitpython的官方驱动库中没有找到RTOS相关的库,不过后面在一篇树莓派的文章内找到了circuitpython的RTOS库,这里贴出链接

Snipaste_2024-09-03_21-48-51.jpg

引入RTOS的核心代码如下:

# 添加任务
def add_task(task):
	if task.thread == None:
		task.initialize()
	tasks.append(task) 
	tasks.sort(key=lambda t: t.priority)

# 添加服务例程
def add_service_routine(service_routine):
	service_routines.append(service_routine)

# 启动RTOS
def start(scheduler=None):
	global tasks

	if scheduler == None:
		scheduler = pyRTOS.default_scheduler

	run = True
	while run:
		for service in service_routines:
			service()

		messages = scheduler(tasks)
		pyRTOS.deliver_messages(messages, tasks)

		if len(tasks) == 0:
			run = False

总结

至此,蓝牙密码柜的基本功能已经全部实现。等后续有时间会继续对项目进行优化,例如结合机械结构设计制作完整的密码柜。

更多回帖

发帖
×
20
完善资料,
赚取积分