玩转树莓派Pico(21): 迷你气象站7——软件整合改进2

news2025/1/5 7:11:48

前言

        改进是无止境的,我迟迟没有装配是因为我一直对我的代码不满意,一改再改。

改进日志类

        这个是在别人的基础上,根据自己的需要稍微改动,难度不大。代码放在gitcode上,详见这里。

改进状态指示灯类

        这是根据自己的想法,在AI的辅助下,经过多次迭代而成。然而我今天又有了新想法,而且很简单,经过测试可行,彻底将以前的结果推翻。那我前几天算不算白折腾?

        起初我是用的嵌套函数,执行外层函数传入Pin对象,返回内层函数,这样就得到该led的专属对象,然后可以传入频率去执行。

        以前测试led的闪烁时都是在主程序里。而我这个项目设计有3个指示灯,我认为需要在另一个线程中循环控制闪烁,于是使用了_thread类。而该类并不完善,使用中出现一个问题就是连接电脑调试时,当点击停止按钮,子线程仍在运行,导致与pico失去联系,只能重新插拔usb。为了避免这个问题又增加了代码的复杂性,最后弄成这样:

"""
功能: 状态指示灯的类, 使灯可以在另一个线程闪烁
最后更新: 241229
"""
import utime
import _thread

class StatusLed():
    _mainthread_heart_beat_data = 0  #  主线程心跳数据(时间戳)
    _mainthread_heart_beat_alive = 6  # 超过该时间算主线程退出,秒
    
    def __init__(self, led, frequency = 1):
        self.led = led  # Pin对象       
        self.blink_frequency = frequency  # 闪烁频率,单位Hz
        self.cycle_accuracy = 10  # 循环精度,毫秒(每次循环的时间)
        self.cycle_times = 0  # 改变led状态需要的循环次数
        self.cycle_count = 0  # 循环计次
        self.running = False  # 控制是否闪烁的指针
        self.lock = _thread.allocate_lock()  # 线程锁
        self.thread_id = None  # 保存线程id
        
        self.frequency_to_cycle_times()

    # 根据频率计算循环次数
    def frequency_to_cycle_times(self):
        self.cycle_times = int((1000 / self.blink_frequency)/10)
    
    # 心跳数据
    @classmethod
    def heart_beat(cls):
        cls._mainthread_heart_beat_data = utime.time()
    
    # 检测主线程是否存活
    @property
    def is_mainthread_alive(self):
        if utime.time() - self.__class__._mainthread_heart_beat_data <= self.__class__._mainthread_heart_beat_alive:
            return True
        else:
            return False
    
    # 闪烁
    def blink(self):
        while self.running and self.is_mainthread_alive:
            if self.cycle_count >= self.cycle_times:  # 达到次数,才改变led的状态
                self.led.toggle()
                self.cycle_count = 0 
            else:
                self.cycle_count += 1
                
            utime.sleep_ms(self.cycle_accuracy)
        
        self.thread_id = None
        self.led.off() # 退出循环时,关闭灯    
            
    # 停止闪烁
    def stop_blink(self):
        with self.lock:
            self.running = False
        
        # 等待子线程结束,但是最大等待时间为循环精度
        start_time = utime.ticks_ms()  # 起始时间
        while self.thread_id is not None:
            if utime.ticks_diff(utime.ticks_ms(), start_time) > self.cycle_accuracy:
                break 
                            
    # 启动闪烁
    def start_blink(self, frequency=None):
        if frequency and (0.1 <= frequency <= 100):
            with self.lock:
                self.blink_frequency = frequency
                self.frequency_to_cycle_times()
                
            self.running = True
            if self.thread_id is None:     
                self.thread_id = _thread.start_new_thread(self.blink, ())
                
        else: # 频率为0或设置不符合要求,停止
            self.stop_blink()

    # 按档位闪烁
    def blink_adjust_speed_with_position(self, position):
        if position == 0:
            frequency = 0
        elif position == 1:
            frequency = 0.25    
        elif position == 2:
            frequency = 0.5
        elif position == 3:    
            frequency = 1
        elif position == 4:    
            frequency = 2
        elif position == 5:    
            frequency = 4
        elif position == 6:    
            frequency = 8
        elif position == 7:    
            frequency = 16
        elif position == 8:    
            frequency = 32
        else:
            frequency = 0
            
        self.start_blink(frequency)
 

            
# 调试代码
if __name__ == '__main__':
    from machine import Pin
    
    led = StatusLed(Pin(25, Pin.OUT))
    led.blink_adjust_speed_with_position(8)
    utime.sleep(20)
    led.blink_adjust_speed_with_position(0)

        今天我又有新的想法,上面的代码是一个指示灯生成一个实例对象,就要新开一个线程。我可以让多个指示灯共用一个线程,节省资源。

        然而还没等这个想法实施,我又来灵感。项目中我使用Timer类来定时执行读取气象数据并发布到MQTT服务器和校时任务, 为啥我不能用来控制led的闪烁呢? 只要将运行周期设为要闪烁的周期啊。这样就非常简单,根本无需使用_thread,代码如下:

"""
功能: 状态指示灯的类,封装了Timer类,用来来控制led的闪烁
最后更新: 250101
"""
import utime
from machine import Timer, Pin


class StatusLed():
    def __init__(self, pin_no, period=1000):
        self.led = Pin(pin_no, Pin.OUT)  # Pin对象       
        self.default_period = period  # 闪烁周期,毫秒
        self.timer = Timer()

    # 执行闪烁, 传入的参数是Timer的要求
    def do_blink(self, timer):
        self.led.toggle()
    
    # 启动闪烁,传入周期
    def start_blink(self, period):
        if period > 0:
            if period > 10000 or period < 10:  # 超出范围,按默认周期闪烁
                period = self.default_period           
            self.timer.init(period=period, mode=Timer.PERIODIC, callback=self.do_blink)
        
        else:  # 其他情况表明停止
            self.stop_blink()  
            
    # 停止闪烁
    def stop_blink(self):
        self.timer.deinit()
        self.led.off()  # 停止后指示灯熄灭
        
    # 按档位闪烁
    def blink_with_position(self, position):
        if position == 1:
            period = 4000    
        elif position == 2:
            period = 2000
        elif position == 3:    
            period = 1000
        elif position == 4:    
            period = 500
        elif position == 5:    
            period = 250
        elif position == 6:    
            period = 125
        elif position == 7:    
            period = 62
        elif position == 8:    
            period = 31
        else:
            period = 0
            
        self.start_blink(period)        


if __name__ == '__main__':
    green_led = StatusLed(25)
    
    for i in range(9):
        print(f"档位:{i}")
        green_led.blink_with_position(i)
        utime.sleep(10)
        
    green_led.stop_blink()    

总结

        虽然看起来白折腾,但是没有这个折腾过程,怎么会有这个结果呢?我还体验了使用多线程,对定时器的也有更深的理解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2269934.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

本地LLM部署--llama.cpp

–图源GitHub项目主页 概述 llama.cpp是以一个开源项目&#xff08;GitHub主页&#xff1a;llamma.cpp&#xff09;&#xff0c;也是本地化部署LLM模型的方式之一&#xff0c;除了自身能够作为工具直接运行模型文件&#xff0c;也能够被其他软件或框架进行调用进行集成。 其…

基本算法——分类

目录 创建项目 导入依赖 加载数据 特征选择 学习算法 对新数据分类 评估与预测误差度量 混淆矩阵 通过模型的预测结果生成 ROC 曲线数据 选择分类算法 完整代码 结论 创建项目 首先创建spring boot项目&#xff0c;我这里用的JDK8&#xff0c;springboot2.7.6&…

【系统配置】3种方式修改用户登录显示名|统信|麒麟|方德

原文链接&#xff1a;【系统配置】3种方式修改用户登录显示名&#xff5c;统信&#xff5c;麒麟&#xff5c;方德 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于 通过修改 /etc/passwd 文件、usermod 命令&#xff0c;以及图形化界面三种方式修改用户登录名 的…

TTL 传输中过期问题定位

问题&#xff1a; 工作环境中有一个acap的环境&#xff0c;ac的wan口ip是192.168.186.195/24&#xff0c;ac上lan上有vlan205&#xff0c;其ip子接口地址192.168.205.1/24&#xff0c;ac采用非nat模式&#xff0c;而是路由模式&#xff0c;在上级路由器上有192.168.205.0/24指向…

Cocos2dx Lua绑定生成中间文件时参数类型与源码类型不匹配

这两天维护的一个项目&#xff0c;使用arm64-v8a指令集编译时遇到了报错&#xff0c;提示类型不匹配&#xff0c;具体报错的代码【脚本根据C源文件生成的中间文件】如下&#xff1a; const google::protobuf::RepeatedField<unsigned long long>& ret cobj->equi…

连接Milvus

连接到Milvus 验证Milvus服务器正在侦听哪个本地端口。将容器名称替换为您自己的名称。 docker port milvus-standalone 19530/tcp docker port milvus-standalone 2379/tcp docker port milvus-standalone 192.168.1.242:9091/api/v1/health 使用浏览器访问连接地址htt…

走方格(蓝桥杯2020年试题H)

【问题描述】在平面上有一些二维点阵。这些点的编号就像二维数组的编号一样&#xff0c;从上到下依次为第1~n行&#xff0c;从左到右依次为第1~m列&#xff0c;每个点可以用行号和列号表示。 现在有个人站在第1行第1列&#xff0c;他要走到第n行第m列&#xff0c;只能向右或者向…

28. 二叉树遍历

题目描述 根据给定的二叉树结构描述字符串&#xff0c;输出该二叉树按照中序遍历结果字符串。中序遍历顺序为:左子树&#xff0c;根结点&#xff0c;右子树。 输入描述 由大小写字母、左右大括号、逗号组成的字符串: 1、字母代表一个节点值&#xff0c;左右括号内包含该节点的子…

Swift White Hawkstrider

Swift White Hawkstrider 迅捷白色陆行鸟 Swift White Hawkstrider - Item - 魔兽世界怀旧服TBC数据库_WOW2.43数据库_70级《燃烧的远征》数据库 Kaelthas Sunstrider (1) <Lord of the Blood Elves> 凯尔萨斯逐日者. 掉落 [80圣骑士][Alonsus-加丁][诺森德冒险补给品…

LeetCode算法题——有序数组的平方

题目描述 给你一个按非递减顺序排序的整数数组nums&#xff0c;返回每个数字的平方组成的新数组&#xff0c;要求也按非递减顺序排序。 题解 解法一&#xff1a;暴力解法 思路&#xff1a; 该题目可通过暴力解法解决&#xff0c;即利用for循环遍历数组&#xff0c;对数组每…

项目开发实践——基于SpringBoot+Vue3实现的在线考试系统(四)

文章目录 一、管理员角色功能实现1、添加教师功能实现1.1 页面设计1.2 前端功能实现1.3 后端功能实现1.4 效果展示2、教师管理功能实现2.1 页面设计2.2 前端功能实现2.3 后端功能实现2.3.1 后端查询接口实现2.3.2 后端编辑接口实现2.3.3 后端删除接口实现2.4 效果展示二、代码下…

DVWA靶场Brute Force (暴力破解) 漏洞low(低),medium(中等),high(高),impossible(不可能的)所有级别通关教程

目录 暴力破解low方法1方法2 mediumhighimpossible 暴力破解 暴力破解是一种尝试通过穷尽所有可能的选项来获取密码、密钥或其他安全凭证的攻击方法。它是一种简单但通常无效率的破解技术&#xff0c;适用于密码强度较弱的环境或当攻击者没有其他信息可供利用时。暴力破解的基…

基于feapder爬虫与flask前后端框架的天气数据可视化大屏

# 最近又到期末了&#xff0c;有需要的同学可以借鉴。 一、feapder爬虫 feapder是国产开发的新型爬虫框架&#xff0c;具有轻量且数据库操作方便、异常提醒等优秀特性。本次设计看来利用feapder进行爬虫操作&#xff0c;可以加快爬虫的速率&#xff0c;并且简化数据入库等操作…

抖音短视频矩阵系统源码开发技术解析

开发概览&#xff1a; 抖音短视频矩阵系统的构建基于一系列现代技术栈&#xff0c;主要包括VUE, Spring Boot和Django。本文档旨在为开发者提供关于短视频矩阵系统源代码的开发与部署指南。 技术框架分析&#xff1a; 前端技术选型&#xff1a; 对于前端界面的构建&#xf…

CentOS Stream 9 安装 JDK

安装前检查 java --version注&#xff1a;此时说明已安装过JDK&#xff0c;否则为未安装。如若已安装过JDK可以跳过安装步骤直接使用&#xff0c;或者先卸载已安装的JDK版本重新安装。 安装JDK 官网下载地址&#xff1a;https://www.oracle.com/java/technologies/downloads…

【git】git生成rsa公钥的方法

git生成rsa公钥的方法 一&#xff0c;简介二&#xff0c;操作方法三&#xff0c;总结 一&#xff0c;简介 在工作的过程中&#xff0c;经常需要生成rsa的密钥&#xff0c;然后提供给别人&#xff0c;然后别人给你开通代码下载权限。本文介绍如何在本地生成rsa的密钥供参考。 …

Solon 加入 GitCode:助力国产 Java 应用开发新飞跃

在当今数字化快速发展的时代&#xff0c;Java 应用开发框架不断演进&#xff0c;开发者们始终在寻找更快、更小、更简单的解决方案。近期&#xff0c;Solon 正式加入 GitCode&#xff0c;为广大 Java 开发者带来全新的开发体验&#xff0c;尤其是在国产应用开发进程中&#xff…

如何通过深度学习提升大分辨率图像预测准确率?

随着科技的不断进步&#xff0c;图像处理在各个领域的应用日益广泛&#xff0c;特别是在医疗影像、卫星遥感、自动驾驶、安防监控等领域中&#xff0c;大分辨率图像的使用已经成为了一项不可或缺的技术。然而&#xff0c;大分辨率图像带来了巨大的计算和存储压力&#xff0c;同…

硬件基础知识笔记(2)——二级管、三极管、MOS管

Part 2 二级管、三极管、MOS管 1、二级管1.1肖特基二极管和硅二极管选型比较1.2到底是什么决定了二极管的最高工作频率&#xff1f;1.3二极管结电容和反向恢复时间都是怎么来的1.4肖特基二极管的工作原理1.5为什么要用肖特基二极管续流&#xff1f; 2、三极管2.1三极管工作原理…

操作系统论文导读(八):Schedulability analysis of sporadic tasks with multiple criticality specifications——具有多个

Schedulability analysis of sporadic tasks with multiple criticality specifications——具有多个关键性规范的零星任务的可调度性分析 目录 一、论文核心思想 二、基本定义 2.1 关键性指标 2.2 任务及相关参数定义 2.3 几个基础定义 三、可调度性分析 3.1 调度算法分…