2013-03-21

Raspberry Pi parking camera with distance sensor

This build brings together a few other projects to make something potentially quite useful for a change - a parking camera with distance sensor. The feed from the webcam is shown on the LCD with a distance read-out underneath. As you get closer to the object (my hand in the videos) a circle is overlaid on the video which gets larger as you move closer. Once you get to within 30cm of the object the word "STOP" is overlaid and everything turns red.

Here it is in action:

And a close-up of the screen:


The project is made up of the following:

Camera: Microsoft Lifecam Cinema - I've used this with lots of Raspberry Pi projects - it works nicely and has a good microphone too.

Distance sensorSharp GP2Y0A02YK0F - my article Raspberry Pi distance measuring sensor with LCD output explains how to put this together.

I'm also using an Adafruit Pi Cobbler to breakout the header onto the breadboard.




For the software I'm using Pygame. Thankfully the camera supports a 176x144 resolution and since my screen is 176x220 this fits perfectly. So, after some initializing there is a main loop which simply: blits the image from the camera, reads from the distance sensor, draws the circle and text. Finally update() is called to send this to the framebuffer.

import pygame
import pygame.camera
import os
import mcp3008

BLACK = 0,0,0
GREEN = 0,255,0
RED = 255,0,0

if not os.getenv('SDL_FBDEV'):
    os.putenv('SDL_FBDEV', '/dev/fb1')

if not os.getenv('SDL_VIDEODRIVER'):
    os.putenv('SDL_VIDEODRIVER', 'fbcon')

pygame.init()
lcd = pygame.display.set_mode((176, 220))
pygame.mouse.set_visible(False)
lcd.fill(BLACK)
pygame.display.update()

pygame.camera.init()
 
size = (176,144)
cam = pygame.camera.Camera('/dev/video0', size, 'RGB')

cam.start()

font_big = pygame.font.Font(None, 50)
surf = pygame.Surface(size)
while True:
    lcd.fill(BLACK)
    cam.get_image(surf)
    lcd.blit(surf, (0,0))

    cm = mcp3008.read_2Y0A02_sensor(7)
    colour = GREEN
    if cm < 30:
        colour = RED
        text_surface = font_big.render('STOP', True, colour)
        rect = text_surface.get_rect(center=(88,72))
        lcd.blit(text_surface, rect)

    if cm < 140:
        pygame.draw.circle(lcd, colour, (88,72), (150-cm)/2, 3)
    
    text_surface = font_big.render('%dcm'%cm, True, colour)
    rect = text_surface.get_rect(center=(88,180))
    lcd.blit(text_surface, rect)

    pygame.display.update()


Finally here's the mcp3008 module which is imported above. NOTE: Since the LCD is using SPI 0.0 I have used SPI 0.1 for the mcp3008. You'll see this in the code below:

import spidev

spi = spidev.SpiDev()
spi.open(0,1)

# read SPI data from MCP3008 chip, 8 possible adc's (0 thru 7)
def readadc(adcnum):
    if ((adcnum > 7) or (adcnum < 0)):
        return -1
    r = spi.xfer2([1,(8+adcnum)<<4,0])
    adcout = ((r[1]&3) << 8) + r[2]
    return adcout

def read_3v3(adcnum):
    r = readadc(adcnum)
    v = (r/1023.0)*3.3
    return v

def read_2Y0A02_sensor(adcnum):
    r = []
    for i in range (0,10):
        r.append(readadc(adcnum))
    a = sum(r)/10.0
    v = (a/1023.0)*3.3
    d = 16.2537 * v**4 - 129.893 * v**3 + 382.268 * v**2 - 512.611 * v + 306.439
    cm = int(round(d))
    return cm


2013-03-17

Raspberry Pi playing video on 2.2" LCD.

I used the software and followed the guide here: https://github.com/notro/fbtft - Then I resized and rotated a video from my blog to fit the display.




The original video was 1920x1080 and this screen is 176x220 so I rotated it anti-clockwise and resized it to 220x124 to keep the aspect ratio the same but make best use of the screen. The "transpose=2" does the anti-clockwise rotation.

# First install ffmpeg if you haven't already
sudo apt-get -y install ffmpeg

# Now convert ("experimental" was flagged as required for this h264 movie - try without first)
avconv -i Jeremy.mp4 -s 220:124 -vf transpose=2 -strict experimental j.mp4


Then to play it, make sure to pick the correct framebuffer device - mine is /dev/fb1:
mplayer -vo fbdev2:/dev/fb1 j.mp4

2013-03-11

Raspberry Pi system monitor embedded on your own site


*** NOTE: ControlMyPi shutting down ***

Above is an embedded ControlMyPi panel showing some system stats from my Raspberry Pi.

If you want to run one of these yourself set up your Raspberry Pi for ControlMyPi by following the instructions on the site and then run the script below (after changing it to use your account and password).

To embed it on your site use an iframe using the instructions on the ControlMyPi FAQ.

If you want this to run automatically every time you boot up just add a line to /etc/rc.local e.g. python /path/to/script/pimonitor.py &

'''
Created on 10 Mar 2013

ControlMyPi Raspberry Pi system monitor. See www.controlmypi.com.

@author: Jeremy Blythe
'''

import subprocess
import logging
import time
from controlmypi import ControlMyPi
        
def get_ip_address(interface):
    "Returns the IP address for the given interface e.g. eth0"
    try:
        s = subprocess.check_output(["ip","addr","show",interface])
        return s.split('\n')[2].strip().split(' ')[1].split('/')[0]
    except:
        return '?.?.?.?'

def get_ram():
    "Returns a tuple (total ram, available ram) in megabytes. See www.linuxatemyram.com"
    try:
        s = subprocess.check_output(["free","-m"])
        lines = s.split('\n')        
        return ( int(lines[1].split()[1]), int(lines[2].split()[3]) )
    except:
        return 0

def get_process_count():
    "Returns the number of processes"
    try:
        s = subprocess.check_output(["ps","-e"])
        return len(s.split('\n'))        
    except:
        return 0

def get_up_stats():
    "Returns a tuple (uptime, 5 min load average)"
    try:
        s = subprocess.check_output(["uptime"])
        load_split = s.split('load average: ')
        load_five = float(load_split[1].split(',')[1])
        up = load_split[0]
        up_pos = up.rfind(',',0,len(up)-4)
        up = up[:up_pos].split('up ')[1]
        return ( up , load_five )        
    except:
        return ( '' , 0 )

def get_connections():
    "Returns the number of network connections"
    try:
        s = subprocess.check_output(["netstat","-tun"])
        return len([x for x in s.split() if x == 'ESTABLISHED'])
    except:
        return 0
    
def get_temperature():
    "Returns the temperature in degrees C"
    try:
        s = subprocess.check_output(["/opt/vc/bin/vcgencmd","measure_temp"])
        return float(s.split('=')[1][:-3])
    except:
        return 0

def on_msg(conn, key, value):
    pass

if __name__ == '__main__':
    logging.basicConfig(level=logging.ERROR)

    total_ram = get_ram()[0]
    
    p = [ 
        [ ['O'] ],
        [ ['L','Up time'],['S','up',''] ],
        [ ['L','Processes'],['S','pcount',''] ],
        [ ['L','Connections'],['S','ncount',''] ],
        [ ['C'] ],
        [ ['O'] ],
        [ ['G','ram','free Mb',0,0,total_ram],['G','load','load',0,0,4],['G','temp',u'\xB0C',0,0,80] ], 
        [ ['C'] ]
        ]

    conn = ControlMyPi('you@yours.com', 'password', 'pimonitor', 'Pi system monitor', p, on_msg)
    if conn.start_control():
        try:
            status = {'ram':0, 'temp':0, 'load':0, 'pcount':0, 'ncount':0, 'up':''}
            while True:
                to_send = {}
                
                to_send['ram'] = get_ram()[1]
                to_send['temp'] = get_temperature()
                up, load = get_up_stats()
                to_send['load'] = load
                to_send['pcount'] = get_process_count()
                to_send['ncount'] = get_connections()
                to_send['up'] = up
                
                for k,v in to_send.items():
                    if status[k] == v:
                        del to_send[k]
                
                if len(to_send) > 0:
                    status.update(to_send)
                    conn.update_status(to_send)
                    
                time.sleep(30)
        finally:
            conn.stop_control()

2013-03-09

Raspberry Pi midi driven solenoid bell

This is completely pointless but a bit of fun I had to share. I've been thinking about hooking my Roland TD9 v-drum kit up to a Raspberry Pi for a while for another project so I bought a really cheap Midi to USB gadget: USB Midi Cable Lead Adaptor

To my surprise this worked out-the-box, nothing to install. I made sure my Raspbian OS was up to date before I started but that was it. I have never done anything with Midi before but I knew it was a simple serial protocol and so assumed I'd be able to open some kind of tty device. In fact the ALSA driver on the OS detects it correctly as a USB-Midi input/ouput device. You can see this by running the amidi command:
pi@raspberrypi ~ $ amidi -l
Dir Device    Name
IO  hw:1,0,0  USB2.0-MIDI MIDI 1
 O  hw:1,0,1  USB2.0-MIDI MIDI 2

There are probably loads of proper Midi tools you can use since it has been discovered correctly by I just wanted to look at the raw bytes coming in. The device node that's been created in the file system can be found in /dev/snd:
pi@raspberrypi ~ $ ls /dev/snd
by-id  by-path  controlC0  controlC1  midiC1D0  pcmC0D0p  seq  timer

So all my program has to do is read bytes from /dev/snd/midiC1D0. To get this to work I didn't need to understand or decode much of the protocol - I'm basically just looking for a sequence when an "instrument" is hit. In this case it is a sequence (in hex) of 99 XX where XX is the instrument code, 26 for snare drum, 24 for kick drum etc. There's a lot more going on but I can ignore the rest apart from a useful continual pulse of FE. This is known as "Active sense" and you get this once every 300ms. I'm using this to switch the GPIO off again as you'll see in the code later. In order to ring the bell you need a quick on/off motion. You can read more about the solenoid bell in my previous post: Raspberry Pi solenoid alarm bell.

Anyway, here's a rather shakey video (sorry) and the code. Enjoy!


import RPi.GPIO as GPIO

inst = {
        '\x26':'snare',
        '\x28':'snare rim',
        '\x30':'tom1',
        '\x32':'tom1 rim',
        '\x2d':'tom2',
        '\x2f':'tom2 rim',
        '\x2b':'tom3',
        '\x3a':'tom3 rim',
        '\x24':'kick',
        '\x1a':'hi-hat rim',
        '\x2e':'hi-hat head',
        '\x2c':'hi-hat close',
        '\x31':'crash head',
        '\x37':'crash rim',
        '\x33':'ride head',
        '\x3b':'ride rim',
        '\x35':'ride bell',
}

f=open('/dev/snd/midiC1D0')

note=False

GPIO_NUM = 18
GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_NUM, GPIO.OUT)

while True:
        b = f.read(1)
        if b == '\xfe':
                GPIO.output(GPIO_NUM, False)
        else:
#               if b == '\x40':
#                       print hex(ord(b))
#               else:
#                       print hex(ord(b)),
                if b == '\x99':
                        note=True
                elif note:
                        if b in inst:
                                print inst[b]
                                if b == '\x26':
                                        GPIO.output(GPIO_NUM, True)
                        else:
                                print hex(ord(b))
                        note=False