Rpi Weather LED

Raspberry-Pi-logo

In this post I will describe how to use a raspberry pi to give feedback according to specific daily weather. And yes, feedback just means LEDs 🙂

The Cheltenham Science Festival

Past weekend I was volunteering on a Raspberry PI workshop in the Cheltenham science festival 2013, mostly explaining at a beginner entry level, what to do with a PI and see how people from all ages started their way in programming.

I have to say it was an amazing experience and I would gladly repeat. Rob and Clive were two awesome hosts.

What I will do is explain step by step one of the small and easy projects that you can assemble home. Small other projects can be found in the ocr website, and I am sure in a lot of other places if you just google for it.

Weather blinking LED

The idea is to obtain information on the weather forecast using python, and give a little bit of feedback about it with the raspberry pi.
For this we only need very basic knowledge of python and “electronics”, so let’s go!.

Groceries list

In this case, for first time, you need some stuff to try it yourself.

  • A Raspberry PI
  • 1 LED
  • 1 resistor 1KΩ
  • 1 resistor 220Ω
  • 1 pushbutton
  • Breadboard is optional, but cheap enough to consider getting one
  • Jumper wires (recommended), or simple cables

You can get your own Raspberry PI via amazon or element14 or wherever you find one.

The rest is not difficult to obtain. I keep reading about Radio shack (lucky north americans!), In Cambridge there is a place called Maplin to buy the stuff, and in Barcelona you may want to go to Diotronics or Onda Radio. You can also check on amazon for some packs, but it’s not half the fun of actually going out.

If you don’t mind savaging and old toy, there are high chances of finding LEDs, resistors and pushbuttons inside it. Just be sure that no kid is using it anymore!.

Wiring

The wiring is very easy. I just want to freak out about a software that I discovered when writing this post (I wanted to add some easy schematics). Fritzing! I won’t talk about it now, but it is a neat piece of software.

This two images show the all wiring that needs to be done. Be careful not to wire to the 5v pin!.

Raspberry PI connected to a led and a pushbutton into a breadboard.

Raspberry PI connected to a LED and a pushbutton into a breadboard.

Raspberry PI connected to a led and a pushbutton.

Raspberry PI connected to a LED and a pushbutton.

The LED

The LED is connected from one pin, to ground, in serial with a 220Ω resistor to avoid it to be burned.

The Pushbutton

The switch is connected from the 3.3v source, in serial with a 1KΩ pull-down resistor to ground. The pin 11 of the pi is connected closer to the ground between the switch and the resistor. In this way, when the button is not pressed it will read close to 0, and when the switch is pressed close to 3.3v.

That is all from the wiring.

Writing

We have our rpi connected to stuff, now let’s make it light everything!.

I will explain the parts separately, and at the end provide a link to my github joining all the pieces together plus some more bells and whistles like logging, basic error handling etc.

What I expect from this software:

  • Blink me the LED when is updating.
  • Light the LED if is going to rain today.
  • The LED is off when it is safe to go out without getting wet.
  • Maybe I don’t trust the update, so I want to be able to update with the last weather information when I press the button.

Setting Up

Before doing anything on the raspberry pins, we need the RPi.GPIO module for python. If you are using Raspbian it is installed by default (lucky you!). If not, it is up to you to find and install it.

def board_set_up():
    """Set up the Raspberry pins"""
    GPIO.cleanup() #clean other configurations
    GPIO.setmode(GPIO.BOARD) #pin numbering to board style
    GPIO.setup(LEDPIN, GPIO.OUT)
    GPIO.setup(BUTPIN, GPIO.IN)
    GPIO.add_event_detect(BUTPIN, GPIO.RISING) #Check for Rising edges on BUTPIN

Note that LEDPIN and BUTPIN are defined 7 and 11 respectively.

Most of the basic button examples involve some kind of polling, but surprisingly, RPi.GPIO provides a better and cleaner way to check for raising and falling edges on a pin. 🙂

Lighting LEDs

Well, this is not rocket science!:

def led_on():
    GPIO.output(LEDPIN, GPIO.HIGH)

def led_off():
    GPIO.output(LEDPIN, GPIO.LOW)

A little bit more fanciness, but no so much. The LED blinking function is a
must!

def led_blink():
    led_off()
    for x in range(5):
        led_on()
        time.sleep(0.1)
        led_off()
        time.sleep(0.1)

This was easy!

Pushing Buttons

Because of the edge detection of the library, there is no need to do a direct
polling of the button pin. But, this is not like a signal that will be raised
immediately. When the button is pressed a flag is set inside RPi.GPIO (probably, that is what I assume) so it can later be checked somewhere else.

What the software will do is check on every loop if this flag is set.

#Inside the main loop
# Button Pressed handler
if GPIO.event_detected(BUTPIN):
    led_blink()
    update = True #update the info!

Three lines of code, not bad.

Getting Weathers

There is a lot of places to obtain your weather information. We live in this
amazing world where the information is everywhere and for everyone to use (at least some of the information).

You can directly get this data just going to the website, but we don’t want to parse HTML (ugh), but to play with the web service provided.
To use it, you need to have an account in Weather Underground and create an API key. Yes, I know, another account?!.. it is not compulsory, you can use another web service if it fits you.

Obtaining the weather forecast for Cambridge UK, is a matter of getting the
response from a specific url http://api.wunderground.com/api/YOURAPIKEY/forecast/q/UK/Cambridge.json. And it is fairly easy to accomplish using python:

def get_url(url):
    f = urllib2.urlopen(url)
    json_string = f.read()
    return json.loads(json_string)

The received json is transformed to a python data structure via json.loads

This json contains a lot of information about the forecast. Separed in different periods. The documentation was not very clear about it, but the day is separed in two periods. For the purpose of this example, I simply retreive the first period.

{
	"response": {
		"version": "0.1"
		,"termsofService": "http://www.wunderground.com/weather/api/d/terms.html"
		,"features": {
		"forecast": 1
		}
	}
		,
	"forecast":{
		"txt_forecast": {
		"date":"1:00 AM BST",
		"forecastday": [
		{
		"period":0,
		"icon":"partlycloudy",
		"icon_url":"http://icons-ak.wxug.com/i/c/k/partlycloudy.gif",
		"title":"Wednesday",
		"fcttext":"Mostly cloudy. High of 66F. Winds from the NE at 5 to 10 mph.",
		"fcttext_metric":"Mostly cloudy. High of 19C. Winds from the NE at 10 to 15 km/h.",
		"pop":"0"
		}
		,
		{
		"period":1,
		"icon":"partlycloudy",
		"icon_url":"http://icons-ak.wxug.com/i/c/k/partlycloudy.gif",
		"title":"Wednesday Night",
		"fcttext":"Partly cloudy. Fog overnight. Low of 43F. Winds from the NE at 5 to 10 mph.",
		"fcttext_metric":"Partly cloudy. Fog overnight. Low of 6C. Winds from the NE at 10 to 15 km/h.",
		"pop":"0"
		}
		,
		{
		"period":2,
		"icon":"clear",
		"icon_url":"http://icons-ak.wxug.com/i/c/k/clear.gif",
		"title":"Thursday",
		"fcttext":"Clear in the morning, then partly cloudy. High of 64F. Winds from the ENE at 5 to 15 mph.",
		"fcttext_metric":"Clear in the morning, then partly cloudy. High of 18C. Breezy. Winds from the ENE at 10 to 20 km/h.",
		"pop":"0"
		}
		,
		{
		"period":3,
		"icon":"clear",
		"icon_url":"http://icons-ak.wxug.com/i/c/k/clear.gif",
		"title":"Thursday Night",
		"fcttext":"Clear. Low of 43F. Winds from the NE at 5 to 15 mph.",
		"fcttext_metric":"Clear. Low of 6C. Breezy. Winds from the NE at 10 to 20 km/h.",
		"pop":"0"
		}
		,
		{
		"period":4,
		"icon":"clear",
		"icon_url":"http://icons-ak.wxug.com/i/c/k/clear.gif",
		"title":"Friday",
		"fcttext":"Clear. High of 66F. Winds from the NE at 10 to 15 mph.",
		"fcttext_metric":"Clear. High of 19C. Breezy. Winds from the NE at 15 to 20 km/h.",
		"pop":"0"
		}
		,
		{
		"period":5,
		"icon":"clear",
		"icon_url":"http://icons-ak.wxug.com/i/c/k/clear.gif",
		"title":"Friday Night",
		"fcttext":"Partly cloudy. Fog overnight. Low of 43F. Winds from the NE at 5 to 15 mph.",
		"fcttext_metric":"Partly cloudy. Fog overnight. Low of 6C. Breezy. Winds from the NE at 10 to 20 km/h.",
		"pop":"0"
		}
		,
		{
		"period":6,
		"icon":"clear",
		"icon_url":"http://icons-ak.wxug.com/i/c/k/clear.gif",
		"title":"Saturday",
		"fcttext":"Partly cloudy in the morning, then clear. High of 64F. Winds from the NE at 10 to 15 mph.",
		"fcttext_metric":"Partly cloudy in the morning, then clear. High of 18C. Breezy. Winds from the NE at 15 to 20 km/h.",
		"pop":"0"
		}
		,
		{
		"period":7,
		"icon":"clear",
		"icon_url":"http://icons-ak.wxug.com/i/c/k/clear.gif",
		"title":"Saturday Night",
		"fcttext":"Partly cloudy. Fog overnight. Low of 43F. Winds from the NE at 5 to 15 mph.",
		"fcttext_metric":"Partly cloudy. Fog overnight. Low of 6C. Breezy. Winds from the NE at 10 to 20 km/h.",
		"pop":"0"
		}
		]
		},
		"simpleforecast": {
		"forecastday": [
		{"date":{
	"epoch":"1370466000",
	"pretty":"10:00 PM BST on June 05, 2013",
	"day":5,
	"month":6,
	"year":2013,
	"yday":155,
	"hour":22,
	"min":"00",
	"sec":0,
	"isdst":"1",
	"monthname":"June",
	"weekday_short":"Wed",
	"weekday":"Wednesday",
	"ampm":"PM",
	"tz_short":"BST",
	"tz_long":"Europe/London"
},
		"period":1,
		"high": {
		"fahrenheit":"66",
		"celsius":"19"
		},
		"low": {
		"fahrenheit":"43",
		"celsius":"6"
		},
		"conditions":"Partly Cloudy",
		"icon":"partlycloudy",
		"icon_url":"http://icons-ak.wxug.com/i/c/k/partlycloudy.gif",
		"skyicon":"partlycloudy",
		"pop":0,
		"qpf_allday": {
		"in": 0.00,
		"mm": 0.0
		},
		"qpf_day": {
		"in": 0.00,
		"mm": 0.0
		},
		"qpf_night": {
		"in": 0.00,
		"mm": 0.0
		},
		"snow_allday": {
		"in": 0,
		"cm": 0
		},
		"snow_day": {
		"in": 0,
		"cm": 0
		},
		"snow_night": {
		"in": 0,
		"cm": 0
		},
		"maxwind": {
		"mph": 9,
		"kph": 14,
		"dir": "NE",
		"degrees": 54
		},
		"avewind": {
		"mph": 8,
		"kph": 13,
		"dir": "NE",
		"degrees": 47
		},
		"avehumidity": 85,
		"maxhumidity": 99,
		"minhumidity": 62
		}
		,
		{"date":{
	"epoch":"1370552400",
	"pretty":"10:00 PM BST on June 06, 2013",
	"day":6,
	"month":6,
	"year":2013,
	"yday":156,
	"hour":22,
	"min":"00",
	"sec":0,
	"isdst":"1",
	"monthname":"June",
	"weekday_short":"Thu",
	"weekday":"Thursday",
	"ampm":"PM",
	"tz_short":"BST",
	"tz_long":"Europe/London"
},
		"period":2,
		"high": {
		"fahrenheit":"64",
		"celsius":"18"
		},
		"low": {
		"fahrenheit":"43",
		"celsius":"6"
		},
		"conditions":"Clear",
		"icon":"clear",
		"icon_url":"http://icons-ak.wxug.com/i/c/k/clear.gif",
		"skyicon":"mostlysunny",
		"pop":0,
		"qpf_allday": {
		"in": 0.00,
		"mm": 0.0
		},
		"qpf_day": {
		"in": 0.00,
		"mm": 0.0
		},
		"qpf_night": {
		"in": 0.00,
		"mm": 0.0
		},
		"snow_allday": {
		"in": 0,
		"cm": 0
		},
		"snow_day": {
		"in": 0,
		"cm": 0
		},
		"snow_night": {
		"in": 0,
		"cm": 0
		},
		"maxwind": {
		"mph": 11,
		"kph": 18,
		"dir": "ENE",
		"degrees": 63
		},
		"avewind": {
		"mph": 9,
		"kph": 14,
		"dir": "ENE",
		"degrees": 57
		},
		"avehumidity": 78,
		"maxhumidity": 98,
		"minhumidity": 59
		}
		,
		{"date":{
	"epoch":"1370638800",
	"pretty":"10:00 PM BST on June 07, 2013",
	"day":7,
	"month":6,
	"year":2013,
	"yday":157,
	"hour":22,
	"min":"00",
	"sec":0,
	"isdst":"1",
	"monthname":"June",
	"weekday_short":"Fri",
	"weekday":"Friday",
	"ampm":"PM",
	"tz_short":"BST",
	"tz_long":"Europe/London"
},
		"period":3,
		"high": {
		"fahrenheit":"66",
		"celsius":"19"
		},
		"low": {
		"fahrenheit":"43",
		"celsius":"6"
		},
		"conditions":"Clear",
		"icon":"clear",
		"icon_url":"http://icons-ak.wxug.com/i/c/k/clear.gif",
		"skyicon":"sunny",
		"pop":0,
		"qpf_allday": {
		"in": 0.00,
		"mm": 0.0
		},
		"qpf_day": {
		"in": 0.00,
		"mm": 0.0
		},
		"qpf_night": {
		"in": 0.00,
		"mm": 0.0
		},
		"snow_allday": {
		"in": 0,
		"cm": 0
		},
		"snow_day": {
		"in": 0,
		"cm": 0
		},
		"snow_night": {
		"in": 0,
		"cm": 0
		},
		"maxwind": {
		"mph": 12,
		"kph": 19,
		"dir": "NE",
		"degrees": 52
		},
		"avewind": {
		"mph": 11,
		"kph": 18,
		"dir": "NE",
		"degrees": 50
		},
		"avehumidity": 78,
		"maxhumidity": 92,
		"minhumidity": 54
		}
		,
		{"date":{
	"epoch":"1370725200",
	"pretty":"10:00 PM BST on June 08, 2013",
	"day":8,
	"month":6,
	"year":2013,
	"yday":158,
	"hour":22,
	"min":"00",
	"sec":0,
	"isdst":"1",
	"monthname":"June",
	"weekday_short":"Sat",
	"weekday":"Saturday",
	"ampm":"PM",
	"tz_short":"BST",
	"tz_long":"Europe/London"
},
		"period":4,
		"high": {
		"fahrenheit":"64",
		"celsius":"18"
		},
		"low": {
		"fahrenheit":"43",
		"celsius":"6"
		},
		"conditions":"Clear",
		"icon":"clear",
		"icon_url":"http://icons-ak.wxug.com/i/c/k/clear.gif",
		"skyicon":"sunny",
		"pop":0,
		"qpf_allday": {
		"in": 0.00,
		"mm": 0.0
		},
		"qpf_day": {
		"in": 0.00,
		"mm": 0.0
		},
		"qpf_night": {
		"in": 0.00,
		"mm": 0.0
		},
		"snow_allday": {
		"in": 0,
		"cm": 0
		},
		"snow_day": {
		"in": 0,
		"cm": 0
		},
		"snow_night": {
		"in": 0,
		"cm": 0
		},
		"maxwind": {
		"mph": 11,
		"kph": 18,
		"dir": "NE",
		"degrees": 50
		},
		"avewind": {
		"mph": 10,
		"kph": 16,
		"dir": "NE",
		"degrees": 49
		},
		"avehumidity": 82,
		"maxhumidity": 99,
		"minhumidity": 60
		}
		]
		}
	}
}

The value we sant to check is the Probability Of Rain, or “pop”. This value is buried under the json for each of the periods. It is just a matter of doing some dictionary digging.

def get_today_pop(dataset):
    """Get the Probability of Precipitation for the current day, given
    the result set
    """
    return int(dataset['forecast']['txt_forecast']['forecastday'][0]['pop'])

All together

All the ingredients are on the table. Turn on and off the LED, Blink a little
bit, read the button status and obtaining the weather information.

The main loop is as follows:

  • If an update is needed, retrieve weather and light LED appropriately
  • If the button was pressed in some moment, force an update
  • If the day is different than the last time we checked, force an update
  • Sleep a little

The first start up of the software has to set up the script and the GPIO pins,
set the current day, force and update and start looping over and over.

def main():
    script_set_up()
    board_set_up()
    update = True
    now = datetime.datetime.now()
    cday = now.day

    while True:
        # Update handling
        if update:
            update_weather_and_led()
            update = False

        # Button Pressed handler
        if GPIO.event_detected(BUTPIN):
            led_blink()
            update = True

        now = datetime.datetime.now()
        if now.day != cday:
            update = True
            cday   = now.day

        # Wait for next loop
        time.sleep(0.5)

What is update_weather_and_led()?, this is just a function that gets the current pop value from weather underground. If it is bigger than a certain threshold lights the LED, if not, it turns it off.

What is a good value to tell me that I have to get my raincoat before
getting to the wild? I am not sure! in this case I am conservative and set
it to a 30%. If there is more than 30% chance that I will get wet, I want to
be prepared!

The full software for this small entertainment project is on my github as usual.Be aware that the code in github sets APIKEY to nothing, you should add your key there.

What to do now?

A lot to do left!

First. If you want to keep this small project around and swap it in and out of
your Pi, the breadboard is not the most stable option, as you can see on this image, just a couple of wires and my god! Already bothering. The next obvious step is to solder it all together in a shield for the raspberry. I did not perform this last step, but soldering is pretty fun!

The weather.py wiring

The weather.py wiring

Second. Why not add more lights?! From the web service the full week weather can be retrieved. 6 more lights from Monday to Sunday and we have a nice show.

Add a case. If soldered, a nice little case would be nice for it. I can imagine a very small wood case with 7 LEDs blinking around 😛

The script checks if there is rain in the day checking the first value of the forecast. As I noted before, this is not the best. Using another of the possible weather services may give you better results, for example the weather underground hourly will return “pop” for each hour.

Conclusions

That was a little ride with the raspberry pi GPIO. There is nothing complicated in this post, but you can see that it sets some basic steps for doing anything with your pi.

What I like a lot from this board is that it fills a hole for the very young or unexperienced to start using a computer in other ways, different than web browsing and watching movies. Current laptops and devices are very enclosed to themselves, there are no serial and parallel ports anymore to plug some stuff in and drive it directly!

Raspberry PI sets a common and known environment (floating windows, and start menus) making it very easy to start. I really like the idea, and I really like to see how is it already working around me (at least here in UK) with school code clubs and lots of workshops.

References

Weather Underground API ⇒GO
GPIO Library inputs ⇒GO
Raspberry PI ⇒GO
My Github ⇒GO

Advertisements

Leave a comment

Filed under code, electronic

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s