Python Lirc即使关闭阻塞仍然阻塞代码

2 投票
1 回答
2068 浏览
提问于 2025-04-28 05:07

我正在尝试在我的Raspberry Pi B+上设置一个滚动的天气信息显示,使用的是OWN(开放天气网络),系统是最新的Rasbian Wheezy。我在用Python LIRC(Linux红外遥控)添加红外支持时遇到了麻烦。

我想做的事情: 我有四个天气变量:天气状况、温度、湿度和风速。这些信息会显示在我的16x2 LCD屏幕上,标题在第一行居中,数值在第二行。每个信息会在屏幕上停留五秒钟,然后换成下一个。当显示到最后一个时,会重新开始循环。经过180次循环(大约一个小时)后,它会更新天气。我想用我的红外遥控器的1到4号按钮跳转到特定的信息,然后继续循环。

现在的情况: 当没有按下任何按钮时,LIRC应该让程序跳过空的队列,但现在它在lirc.nextcode()这里卡住了,等着我按按钮,直到我用KeyboardInterrupt退出。

一切都运作得很好,直到我添加了红外功能。现在它显示第一个天气变量,然后在尝试获取下一个时,如果队列里没有红外代码,它应该跳过并继续到下一个,但lirc.nextcode()却停住了,等着接收红外代码,这在LIRC阻塞关闭的情况下是不应该发生的。

我已经安装了所有最新版本的东西(Python LIRC 1.2.1),我知道之前的一个版本有关于阻塞参数的bug。我花了两天时间研究和尝试各种可能的方法。我找到了一种可能的解决办法,但也受到了同样的问题影响:"Python LIRC阻塞信号的解决办法不起作用"

我知道我的代码有很多不规范的地方,比如全局变量、一些东西需要放在函数里、OWN每三小时更新一次而我每小时更新一次,但这些都是暂时的,目的是让它能工作。我会在之后整理代码并做成面向对象的。提前抱歉,如果这样让某些人看起来更困难。

import pyowm
from sys import exit
import time
import RPi.GPIO as GPIO
from RPLCD import CharLCD, cleared, cursor
import lirc

# initialize lirc and turn of blocking
sockid = lirc.init("weather", blocking=False)
lirc.set_blocking(False, sockid)

# initialize weather network
owm = pyowm.OWM('API #')

# initialize LCD
lcd = CharLCD(pin_rs=26, pin_rw=None, pin_e=24, pins_data=[22, 18, 16, 12],
                  cols=16, rows=2)

# weather data
w = None # wind m/s
wind = None # wind km/h
windkm = None
humidity = None
temper = None
COUNTER = 0 #number of cycles before update

NEXT = 1

# switches to next tile
def next_tile():
    global NEXT

问题出在这里。Lirc.nextcode()应该从LIRC队列中获取下一个红外代码,并将其作为列表添加到codeIR中,但如果没有按钮被按下,并且阻塞关闭,它应该直接跳过这个代码。结果它却像是阻塞开启了一样,卡住了,直到按下按钮,然后仍然无法继续我的主循环。它只打印NEXT,然后卡住,直到我用KeyboardInterrupt退出。

    codeIR = lirc.nextcode() # pulls IR code from LIRC queue.
    # checks if there's a code in codeIR and goes to that tile. If not, it 
    # goes to the next tile instead.
    if not codeIR: 
        if NEXT != 4: # if it's the last tile, cycle to the first 
            NEXT += 1
            print NEXT
            return NEXT
        else: # if not last tile, go to next
            NEXT -= 3
            print NEXT
            return NEXT
    else:
        NEXT = codeIR[0]
        print NEXT
        return NEXT

我已经添加了我的其他代码,运行得很好,但我相信这能帮助你理解我想要实现的目标。

while True:
    try:
        if COUNTER == 0:
            COUNTER = 180

            # Search for current weather in London (UK)
            observation = owm.weather_at_place('City, State')
            w = observation.get_weather()

            # Weather details
            wind = w.get_wind()                  # {'speed': 4.6, 'deg': 330}
            windkm = (wind['speed'] * 3600) / 1000 #convet to km/h
            humidity = w.get_humidity() 
            # {'temp_max': 10.5, 'temp': 9.7, 'temp_min': 9.0}
            temper = w.get_temperature('celsius')  
        else:
            while NEXT == 1:
                # prints condition to lcd
                lcd.cursor_pos = (0, 4) #adjust cursor position
                lcd.write_string('Weather') # write to lcd
                lcd.cursor_pos = (1, 5) # adjust cursor position
                lcd.write_string(w.get_status()) # write to lcd
                time.sleep(5) # leave on lcd for 5 seconds
                lcd.clear() # clear lcd
                next_tile() # switches to next tile

            while NEXT == 2:
                # prints temp to lcd
                lcd.cursor_pos = (0, 2)
                lcd.write_string('Temperature')
                lcd.cursor_pos = (1, 6)
                lcd.write_string(str(temper['temp']))
                lcd.write_string(' C')
                time.sleep(5)
                lcd.clear()
                next_tile()

            while NEXT == 3:
                # prints temp to lcd
                lcd.cursor_pos = (0, 4)
                lcd.write_string('Humidity')
                lcd.cursor_pos = (1, 6)
                lcd.write_string(str(humidity))
                lcd.write_string(' %')
                time.sleep(5)
                lcd.clear()
                next_tile()

            while NEXT == 4:
                # prints wind speed to lcd
                lcd.cursor_pos = (0, 3)
                lcd.write_string('Wind Speed')
                lcd.cursor_pos = (1, 6)
                lcd.write_string(str(int(windkm)))
                lcd.write_string('km')
                time.sleep(5)
                lcd.clear()
                COUNTER -= 1
                codeIR = lirc.nextcode()
                next_tile()

    # quit with ctrl+C
    except(KeyboardInterrupt, SystemExit):
        print 'quitting'
        lcd.close(clear=True)
        lirc.deinit()
        exit()

当我用KeyboardInterrupt退出时,Traceback总是指向lirc.nextcode(),我本来想发错误信息,但我稍微改了一下代码,现在它只追溯到包含lirc.nextcode()的函数。

我花了两天时间试图解决这个问题,快要抓狂了,所以任何你们能给我的解决方案或变通方法我都非常欢迎。提前谢谢你们,我真的很感激任何能找到的帮助。我找到了一种使用Signal Module AlarmException的变通方法,但一旦我从raw_input()切换到lirc.nextcode(),它也会像之前一样卡住(尽管在raw_input()上设置计时器没有问题),并且阻止了警报正常工作。这里再给你链接一次:"Python LIRC阻塞信号的解决办法不起作用"

暂无标签

1 个回答

2

看来这个问题在1.2.1版本里还是存在。我换用了Pylirc2,它可以顺利地关闭阻塞,使用pylirc.blocking(0)就可以了。我还需要把我的next_tile()函数里的return语句去掉。

如果有人感兴趣的话,这里是我最终用的代码,真的是帮我节省了很多时间:

import pyowm
from sys import exit
import time
import RPi.GPIO as GPIO, feedparser, time
from RPLCD import CharLCD, cleared, cursor
import pylirc

sockid = pylirc.init('weather')
allow = pylirc.blocking(0)
owm = pyowm.OWM('API Key')
lcd = CharLCD(pin_rs=26, pin_rw=None, pin_e=24, pins_data=[22, 18, 16, 12],
                  cols=16, rows=2)


class mail(object):
    def __init__(self):
        self.username = "email address"    
        self.password = "password"
        self.newmail_offset = 0
        self.current = 0
        GPIO.setmode(GPIO.BOARD)
        GPIO.setup(15, GPIO.OUT)
        GPIO.setup(13, GPIO.OUT)
        GPIO.setup(11, GPIO.OUT)

    def buzz(self):
        self.period = 1.0 / 250
        self.delay = self.period / 2
        self.cycles = 250

        for i in range(self.cycles):
            GPIO.output(11, True)
            time.sleep(self.delay)

            GPIO.output(11, False)
            time.sleep(self.delay)

    def check(self): 
        self.newmails = int(feedparser.parse("https://" + self.username + ":" + 
                            self.password +"@mail.google.com/gmail/feed/atom")
                            ["feed"]["fullcount"])

        if self.newmails > self.newmail_offset:
            GPIO.output(15, True)
            GPIO.output(13, False)

            if self.newmails > self.current:
                self.buzz()
                self.current += 1    
        else:
            GPIO.output(15, False)
            GPIO.output(13, True)
            self.current = 0

### will be a class
class weather(object):
    def __init__(self):
        self.w = None
        self.wind = None
        self.windkm = None
        self.humidity = None
        self.temper = None
        self.counter = 0
        self.next = 1

    def update(self):
        if self.counter == 0:
            self.counter = 180
            self.observation = owm.weather_at_place('City, Country')
            self.w = self.observation.get_weather()
            self.wind = self.w.get_wind()
            self.windkm = (self.wind['speed'] * 3600) / 1000
            self.humidity = self.w.get_humidity()
            self.temper = self.w.get_temperature('celsius')
        else:
            pass

    def display_weather(self):
        lcd.cursor_pos = (0, 4)
        lcd.write_string('Weather')
        lcd.cursor_pos = (1, 5)
        lcd.write_string(self.w.get_status())
        time.sleep(3)
        lcd.clear()

    def display_temp(self):
        lcd.cursor_pos = (0, 2)
        lcd.write_string('Temperature')
        lcd.cursor_pos = (1, 6)
        lcd.write_string(str(self.temper['temp']))
        lcd.write_string(' C')
        time.sleep(3)
        lcd.clear()

    def display_hum(self):
        lcd.cursor_pos = (0, 4)
        lcd.write_string('Humidity')
        lcd.cursor_pos = (1, 6)
        lcd.write_string(str(self.humidity))
        lcd.write_string(' %')
        time.sleep(3)
        lcd.clear()

    def display_wind(self):
        lcd.cursor_pos = (0, 3)
        lcd.write_string('Wind Speed')
        lcd.cursor_pos = (1, 4)
        lcd.write_string(str(int(self.windkm)))
        lcd.write_string('km/h')
        time.sleep(3)
        lcd.clear()

    def next_tile(self):
        self.counter -= 1
        self.codeIR = pylirc.nextcode()
        if  not self.codeIR or self.codeIR[0] == self.next: 
            if self.next != 4:
                self.next += 1
            else:
                self.next -= 3
        else:
            self.next = int(self.codeIR[0])

email = mail()
weather = weather()
weather.update()
def up_next():
    weather.update()
    weather.next_tile()

while True:
    try:
        while weather.next == 1:
            weather.display_weather()
            up_next()

        while weather.next == 2:
            weather.display_temp()
            up_next()

        while weather.next == 3:
            weather.display_hum()
            up_next()

        while weather.next == 4:
            weather.display_wind()
            email.check()
            up_next()

    except(KeyboardInterrupt, SystemExit):
        print 'quitting'
        lcd.close(clear=True)
        exit()

撰写回答