pythonlinuxraspberry-piinfraredlirc

Python Lirc blocks code even when blocking is off


I'm trying to set up a scrolling weather feed using the OWN (Open Weather Network)on my Raspberry Pi B+ running the latest Rasbian Wheezy distro and I'm having trouble adding IR support using Python LIRC (Linux Infrared Remote Control).

What I'm trying to do: There are four weather variables: condition, temperature, humidity, and wind speed. They will appear on my 16x2 LCD screen, centered with their title on the top line and value on the second. They will stay on the screen for five seconds before being replaced with the next. Once it reaches the end it will loop through again. After it has looped through 180 times (roughly one hour) it will update the weather. I want to use my IR remote's buttons 1-4 to jump to a specific tile then continue back with it's loop.

What it's doing: When no button has been pressed, instead of skipping the empty queue like it should with LIRC blocking off, it hangs on lirc.nextcode() waiting for a button press, until I quit with KeyboardInterrupt.

Everything worked great, until I added IR. Now it displays the first weather variable, then when it tries to pull the next one, instead of skipping and going to the next tile if there's no IR code in queue, lirc.nextcode() halts the code until it receives an IR code, which shouldn't happen with LIRC blocking turned off.

I have the latest versions of everything (Python LIRC 1.2.1), I know a previous version of Python LIRC had a bug with the blocking parameter. I've spent two days researching and trying every possible thing. Here is one possible workaround that I found, but it's affected by the same problem this one is: "Python LIRC blocking Signal workaround not working"

I know lots of the code is improper, ie global variables, stuff needs to be in functions, OWN updates every three hours and I'm updating every hour, but this is temporary to get it working. I'll be tidying it up and making it object-oriented later. Sorry ahead of time if this makes it more difficult for some to read.

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

This is where the problem lies. Lirc.nextcode() should pull the next IR code from the LIRC queue and add it to codeIR as a list, but if no button has been pressed, and blocking is off, it should just skip over the code. Instead it acts as though blocking is on, and hangs until a button is pressed. and then it still won't continue my main loop. It just prints NEXT and hangs until I KeyboardInterrupt out.

    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

I've added the rest of my code, it all works fine, but I'm sure it'll help you understand what I'm trying to accomplish.

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()

When I KeyboardInterrupt out, Traceback always leads to lirc.nextcode(), I'd post the error, but I changed the code a bit and now it only traces up to the function that contains lirc.nextcode().

I've spent two days trying to work this out and I'm nearly pulling out my hair, so I'll take any solution or workaround you guys can give me. Thanks beforehand, I really appreciate any help I can find. I found a workaround using a Signal Module AlarmException but the moment I switch from raw_input() to lirc.nextcode() it also hangs the same way (even though it puts a timer on raw_input() no problem) and blocks the alarm from working right. Here's the link again: "Python LIRC blocking Signal workaround not working"


Solution

  • Turns out the bug is still in 1.2.1 I suppose. I switched to Pylirc2 and it turned off blocking no problem with pylirc.blocking(0). I also had to remove the return's from my next_tile() function.

    Here's the finished code I ended up using if anyone is interested, it sure would've saved me a load of time:

    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()