树莓派Raspbian系统下继电器定时关灯异常问题求助
树莓派Python继电器定时照明故障排查与解决
问题描述
在树莓派Raspbian系统中,使用Python脚本控制继电器实现照明模块12小时开/12小时关的定时规则:
- 设置10:00开灯、22:00关灯时,照明能准时开启,但永远无法自动关闭;
- 若在22:00后重启脚本,系统能识别并关闭照明,次日也能准时开灯;
- 测试短间隔定时(如12:00开、13:00关)时,功能完全正常,无任何报错。
现有代码实现
定时时间初始化
self.timer_on = datetime.time(self.thresholds['TON'],0) self.timer_off = datetime.time(self.thresholds['TOFF'], 0)
定时检查函数
def timer_check(self): now = datetime.datetime.now().time() if self.timer_on <= now < self.timer_off: GPIO.output(self.light_pin, False) #Turn the light on else: GPIO.output(self.light_pin, True) #Turn the light off
函数调用逻辑
def check_vs_thresholds(self): ''' Determines when and if to turn on or off the heater, ac, and humidifier. Also checks if the temps are high or low enough to warrant creating an alert. ''' # 温度过低时开加热器 if self.heat_pin: self.heater_check() # 温度过高时开空调 if self.ac_pin: self.cooler_check() # 湿度低时开加湿器 if self.hum_pin: self.humidity_check() # 检查照明定时 if self.light_pin: self.timer_check()
主循环执行
def run(self): while True: self.update_readings_from_sensor() self.check_vs_thresholds() self.record()
故障原因分析
从现象和代码来看,核心问题大概率出在时间判断逻辑的可靠性或主循环阻塞上:
datetime.time比较的潜在问题:虽然10:00 <= now <22:00的逻辑在理论上成立,但如果主循环执行间隔较长(比如超过1分钟),可能刚好错过22:00的时间节点,且后续因未知原因导致判断异常;- 主循环阻塞:
update_readings_from_sensor()或record()函数如果出现阻塞(比如传感器读取超时、磁盘写入卡顿),会导致timer_check()无法按时执行; - 状态未更新:长时间运行后,定时时间变量可能未正确跨天更新,但此情况不符合短间隔正常的现象。
解决方案
方案1:增强时间判断逻辑(改用datetime.datetime)
改用完整的日期时间对象进行比较,同时自动更新次日的定时时间,避免time对象比较的潜在问题:
修改初始化代码
import datetime def __init__(self, thresholds, light_pin): self.thresholds = thresholds self.light_pin = light_pin GPIO.setup(self.light_pin, GPIO.OUT) # 构造当天的开/关完整时间 today = datetime.date.today() self.timer_on = datetime.datetime.combine(today, datetime.time(thresholds['TON'], 0)) self.timer_off = datetime.datetime.combine(today, datetime.time(thresholds['TOFF'], 0)) # 处理跨天定时(比如22:00开,10:00关) if self.timer_off < self.timer_on: self.timer_off += datetime.timedelta(days=1)
修改定时检查函数
def timer_check(self): now = datetime.datetime.now() # 当前时间超过关灯时间时,自动更新次日的定时时间 if now >= self.timer_off: self.timer_on += datetime.timedelta(days=1) self.timer_off += datetime.timedelta(days=1) # 执行开关灯逻辑 if self.timer_on <= now < self.timer_off: GPIO.output(self.light_pin, False) # 开灯 else: GPIO.output(self.light_pin, True) # 关灯
方案2:使用专业定时任务库(APScheduler)
放弃自己轮询的方式,改用APScheduler库实现精确定时,避免主循环阻塞问题:
安装APScheduler
pip install apscheduler
重构照明控制代码
from apscheduler.schedulers.background import BackgroundScheduler import RPi.GPIO as GPIO import datetime class LightController: def __init__(self, light_pin, ton, toff): self.light_pin = light_pin GPIO.setmode(GPIO.BCM) GPIO.setup(self.light_pin, GPIO.OUT) # 初始化后台调度器 self.scheduler = BackgroundScheduler() # 添加每日定时任务:ton点开灯,toff点关灯 self.scheduler.add_job(self.turn_light_on, 'cron', hour=ton) self.scheduler.add_job(self.turn_light_off, 'cron', hour=toff) self.scheduler.start() # 初始状态判断 now = datetime.datetime.now().time() if ton <= now.hour < toff: self.turn_light_on() else: self.turn_light_off() def turn_light_on(self): GPIO.output(self.light_pin, False) print(f"[{datetime.datetime.now()}] 照明已开启") def turn_light_off(self): GPIO.output(self.light_pin, True) print(f"[{datetime.datetime.now()}] 照明已关闭") def stop(self): self.scheduler.shutdown() GPIO.cleanup()
在主程序中使用
if __name__ == "__main__": try: controller = LightController(light_pin=18, ton=10, toff=22) while True: # 处理其他传感器或设备逻辑 pass except KeyboardInterrupt: controller.stop()
方案3:添加日志排查
如果暂时不想修改核心逻辑,可以先添加日志输出,定位具体故障点:
def timer_check(self): now = datetime.datetime.now().time() # 打印关键信息 print(f"当前时间: {now} | 开灯时间: {self.timer_on} | 关灯时间: {self.timer_off}") condition = self.timer_on <= now < self.timer_off print(f"条件判断结果: {condition}") if condition: GPIO.output(self.light_pin, False) print("执行操作: 开灯") else: GPIO.output(self.light_pin, True) print("执行操作: 关灯")
运行脚本后,观察22:00前后的日志输出,即可明确是判断逻辑异常还是函数未执行。
内容的提问来源于stack exchange,提问作者Crayonthony
相关产品推荐
相关产品推荐

