The best kittens, technology, and video games blog in the world.

Thursday, April 23, 2015

Adventures with Raspberry Pi: Weather in London

Welcome to another of my Raspberry Pi adventures.

Previously, I connected raspberry pi to monitor and keyboard and blinked some diodes.

It's time to go a lot further. The first step was removing monitor and keyboard, installing sshd, and just controlling the pi from a regular computer. That leaves two cables - ethernet, and USB power (currently coming from the computer).

I'll replace ethernet with wifi at some point, and reroute power to standalone charger so it doesn't need to be anywhere near the computer.

I also got new multimeter - the old one was totally broken, it wasn't just the battery.


Shift registers

Project for today is driving two 8-segment displays. Even ignoring 8th segment on each which is used for decimal dot (and only really included because 7 segments is a silly number), that's 14 wires. Raspberry Pi presumably has that many usable pins (I'm not entirely sure how many of 40 pins it has can be used for full I/O), but we really don't want to spend out entire pin budget on this.

Instead - we're going to use shift registers. Shift register I use - 74HC595 - has 3 inputs: data, clock, and latch. Whenever clock pin goes from low to high, it reads data pin, pushes data it has left by one, and puts whatever it got in newly emptied bit slot. In 8 such cycles, we can set the whole 8 bit register, one bit at a time.

While we're sending the register data, it is temporarily in inconsistent state, with mix of old and new data. To make sure that's never output, there's a separate latch pin - whenever it goes from low to high, register snapshots its state, and keeps that an the output unless we tell it to get new value.

This way we reduced number of required pins per display from 8 to 3.

74HC595 has a few extra pins (clear, output enable, etc.), but we just hardwire them as we don't rely on that functionality.

Here's a photo of work in progress: data is sent to shift register, and from there to 8-sigment panel, but it's just random bits.


Here's the code:



require "pi_piper"

class ShiftRegister
  attr_reader :data, :clk, :latch
  def initialize(data, clk, latch)
    @data  = data
    @clk   = clk
    @latch = latch
  end

  def out_bit(bit)
    @clk.off
    @data.send(bit == 1 ? :on : :off)
    sleep 0.001
    @clk.on
  end

  def out(byte)
    @latch.off
    byte.each do |bit|
      out_bit(bit)
    end
    sleep 0.001
    @latch.on
  end
end

Eight-Segment Display

Next step is really simple - instead of sending random data telling segments to turn on/off, we want to send it data corresponding to shapes of digits:


The code is really simple:


class EightPanelDisplay < ShiftRegister
  def out_symbol(symbol)
    # DOT, LR, L, LL, UR, U, UL, M
    case symbol
    when "0"
      out [0,1,1,1,1,1,1,0]
    when "1"
      out [0,1,0,0,1,0,0,0]
    when "2"
      out [0,0,1,1,1,1,0,1]
    when "3"
      out [0,1,1,0,1,1,0,1]
    when "4"
      out [0,1,0,0,1,0,1,1]
    when "5"
      out [0,1,1,0,0,1,1,1]
    when "6"
      out [0,1,1,1,0,1,1,1]
    when "7"
      out [0,1,0,0,1,1,0,0]
    when "8"
      out [0,1,1,1,1,1,1,1]
    when "9"
      out [0,1,1,0,1,1,1,1]
    else
      warn "Don't know how to output symbol #{symbol.inspect}"
    end
  end
end

We could even extend that to some simple letters or such symbols - not everything can be distinguished, for example B and 8 will look the same, but it might be useful.

Two digits

So we got down from 2x8 to 2x3 pins. We can do better - we can use same clock and data lines for both shift registers, and only have separate latch pins for each. As registers output whatever was the data inside them at the time they got the most recent rising edge on their latch pin, as long as register's latch pin is steady (doesn't matter even matter if high or low), it doesn't matter what they get on other pins.

There are other ways to connect registers, including daily chaining shift registers so they shift into each other thanks to their extra pins, and we could get away with just 3 instead of 4 raspberry pi pins, but this simple setup is good enough.


 And corresponding code:



pin4  = PiPiper::Pin.new(:pin =>  4, :direction => :out)
pin17 = PiPiper::Pin.new(:pin => 17, :direction => :out)
pin22 = PiPiper::Pin.new(:pin => 22, :direction => :out)
pin27 = PiPiper::Pin.new(:pin => 27, :direction => :out)

register_a = EightPanelDisplay.new(pin4, pin27, pin17)
register_b = EightPanelDisplay.new(pin4, pin27, pin22)

Weather in London

After we had the hardware going, software is really simple - a simple JSON request to OpenWeather API, convert Kelvin to Celsius scale, and send it to our display.

And here's current weather in London, +9C (there's no need for sign, as London never has winters):


And the code:

require "json"
require "open-uri"

url = "http://api.openweathermap.org/data/2.5/weather?q=London,uk"
weather = JSON.parse(open(url).read)

temp_c = weather["main"]["temp"] - 273.15
temp_display = "%02d" % temp_c

register_b.out_symbol temp_display[0]
register_a.out_symbol temp_display[1]

No comments: