2012-11-15

Raspberry Pi solenoid alarm bell

Controlling components on a separate power supply from the Raspberry Pi. This project uses a PIR sensor, an IR range sensor, a solenoid, a reception bell and some bright LEDs to form a proximity alarm system.

I'm not sure if there's any real practical use for what I've made here but some of the techniques, both with the electronics and the software may be of interest. It was fun making it anyway!

Hardware:



The first video is the main event. The IR range sensor is set to ring the bell when it detects an object within 40 cm. In the second video you can see the PIR which switches on the bright LEDs when it detects motion nearby. The idea is that as you approach the lights come on and if you get really close the bell rings.

Below is a breadboard diagram and photos. There are a few things to note:

  • I'm using a split rail breadboard which means I can keep the 3.3v from the Raspberry Pi separated from the 5v from the breadboard power supply. You'll notice that the ground is common which I have achieved by bridging over the split on the ground rail.
  • I've used a rectifier diode on the fet for the solenoid to avoid reverse spike problems.
  • I adapted this Arduino PIR guide for the Raspberry Pi






The code:

The mcp3008.py unit is for talking to the analog to digital chip with the same name. I have included routines in here now for reading the raw 10-bit number, a voltage relative to 3.3V and the distance in cm from the range sensor. Read these articles for more info: Raspberry Pi hardware SPI analog inputs using the MCP3008 , Raspberry Pi distance measuring sensor with LCD output.

Bell.py is a very simple program to read the status from the sensors and activate the bell or LEDs accordingly. The main loop sleeps for 0.1 seconds each iteration so we're checking the sensor status about 10 times a second which is fast enough. Once an alarm has been activated it will not be activated again for about 3 seconds. This has two benefits. Firstly it stops the bell constantly ringing when you're within 40cm. Secondly the PIR sensor takes a couple of seconds to settle after each motion detection. During this period the sensor fluctuates between low and high so if you just have the LEDs triggered directly from the True/False state of the GPIO pin then you'll have flickering lights. An important difference in the code is that we switch the LEDs on when motion is detected and leave them on for the 3 second window. For the solenoid though, we set it high to hit the bell but then on the next iteration 0.1 seconds later we set it low again to retract the pin. This gives a nice "flick" motion onto the bell pusher and it avoids everything getting hot!

Shameless plug:
Just before you look at the code I wanted to promote an interesting project that a colleague is working on: The PiXi board.
The PiXi-200 is designed to expand the I/O capabilities of the Raspberry Pi and provide a low-cost means of introducing the user to the world of digital electronics and FPGA technology while at the same time give the 'Pi enthusiast' a little more to play with or even provide a basis for some product development.

I'm looking forward to getting my hands on this but the project needs some support to get off the ground. So please head on over to Astro Designs and register your interest.

Here's the code:


2012-09-08

Raspberry Pi distance measuring sensor with LCD output

Measure distances from the Sharp GP2Y0A02YK0F sensor using an MCP3008 ADC and hardware SPI.


The Sharp GP2Y0A02YK0F can be powered from the 5V supply on the Raspberry Pi. The Analog output is less than 3V and so can easily work with the logic level circuit. If you buy one of these look for the cable that goes with it to save you some bother.

This project builds on two previous projects in this blog. For the Analog to Digital SPI electronics and Python code first go here: Raspberry Pi hardware SPI analog inputs using the MCP3008. For the TextStar LCD first read this: Raspberry Pi with TextStar Serial LCD Display.

In the first video below the sensor is held in a clamp at the very top left of the picture. You can just about make out the display showing the distance to my hand. At the other end of the table there's a chair so when I lift my hand up it shows the distance to that instead - about 118 cm. The second video is a close-up of the screen while I move my hand forward and backwards in front of the sensor. Again the jumps to 118 cm are when I raise a lower my hand. 



All the code is available on github. You'll need mcp3008.py and distance-screen.py to run this project. The code assumes that you have the sensor output connected to CH1 on the MCP3008. The main routine to look at in the code is the translation from the sensor output to a distance. In the datasheet there's a graph which plots distance against voltage output:
I compared this against my sensor by laying a tape measure on the table and looking at the voltage output - it matched perfectly. I was about to start working out a formula to fit this curve when I found a good set of comments on the Sparkfun page including a magic formula!

Here's my Python routine which is called 10 times a second using the on_tick handler from the screen driver:

def write_distance():
    display.position_cursor(1, 1)
    r = []
    for i in range (0,10):
        r.append(mcp3008.readadc(1))
    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))
    val = '%d cm' % cm
    percent = int(cm/1.5)
    display.ser.write(str(val).ljust(16))
    display.capped_bar(16, percent)


  • Firstly, take ten readings and find the average (a) - this smooths things out a little.
  • Convert this to a voltage (v)
  • Use the magic formula to get the distance (d)
  • Round this to the nearest centimetre (cm)
  • Then write this to the display along with a 16 character capped bar graph
So the code happily reads from the sensor 100 times a second and does the calculation and screen update 10 times a second. The CPU usage hovers around 2% in top.

2012-09-05

Raspberry Pi hardware SPI analog inputs using the MCP3008

A hardware SPI remake of the bit-banged Adafruit project:  Analog Inputs for Raspberry Pi Using the MCP3008.


Take a look at the Adafruit project and particularly the datasheet for the MCP3008 - what we're making is a hardware volume control using a 10K potentiometer. Instead of using the GPIO pins and bit-banging the SPI protocol I'm using the proper SPI pins and the hardware driver.

Hardware


The connections from the cobbler to the MCP3008 are as follows:
  • MCP3008 VDD -> 3.3V (red)
  • MCP3008 VREF -> 3.3V (red)
  • MCP3008 AGND -> GND (orange)
  • MCP3008 CLK -> SCLK (yellow)
  • MCP3008 DOUT -> MISO (green)
  • MCP3008 DIN -> MOSI (yellow)
  • MCP3008 CS -> CE0 (red)
  • MCP3008 DGND -> GND (orange)

Operating system and packages

This project was built using Occidentalis v0.2 from Adafruit which takes the hassle out of fiddling with Linux. It comes with the hardware SPI driver ready to go. (It also has super-simple wifi setup if you have a dongle like the Edimax EW-7811UN). A couple of packages are required to complete this project. Firstly the mp3 player:
sudo apt-get install mpg321

and secondly the Python wrapper for SPI:
cd ~

git clone git://github.com/doceme/py-spidev

cd py-spidev/

sudo python setup.py install

Talking to the MCP3008

With the bit-banging code you're in control of the chip-select, clock, in and out pins and so you can effectively write and read a single bit at a time. When you use the hardware driver you talk using 8-bit words and it takes care of the lower level protocol for you. This changes the challenge slightly because the MCP3008 uses 10-bits for the values giving a range from 0 to 1023. Thankfully the Python wrapper is excellent and the datasheet has good documentation:

So from the diagram above you can see that to read the value on CH0 we need to send 24 bits:

.... ...s S210 xxxx xxxx xxxx
0000 0001 1000 0000 0000 0000

Let's say the current value is 742 which is 10 1110 0110 in 10-bit binary. This is what is returned in B9 to B0 in the diagram. The driver and Python wrapper returns this as 3 8-bit bytes as seen above, the mildly confusing thing is that you'll get some bits set where the question marks are in the diagram which you have to ignore (if you were sending a continuous stream these would be more useful). The 24 bits returned will be something like this:

???? ???? ???? ?n98 7654 3210
0110 0111 1000 0010 1110 0110

The python wrapper will give you 3 ints in a list:

[103,130,230]

So, we ignore the first int and then mask out all the bits apart from the last two of the second int by anding it with 3 (0000 0011). In this case you get 0000 0010. We then shift this left by 8 positions to give 10 0000 0000 and then add the third int to give 10 1110 0110 which is 742 decimal. Here's the Python for that:

# 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
The rest of the code is pretty much the same as the Adafruit example. My version is available on git here: https://github.com/jerbly/Pi/blob/master/raspi-adc-pot.py

Run it

Start up mpg321 with your favourite mp3 in the background and then run the Python code:
mpg321 song.mp3 &

sudo python raspi-adc-pot.py
Turn the pot to adjust the volume.


Extra goodies

Combine this with the TextStar screen and the Python code from my previous blog entry: Raspberry Pi with TextStar serial LCD to have a nice bar graph for the pot position. Just use this method on one of the pages and in the on_tick handler so it updates every 0.1 seconds:

# Add this to the display class
    def capped_bar(self, length, percent):
        self.ser.write(ESC+'b'+chr(length)+chr(percent))

s = spidev.SpiDev()
s.open(0,0)

def get_val():
    r = s.xfer2([1,128,0])
    v = ((r[1]&3) << 8) + r[2]
    return v

def write_pots():
    display.position_cursor(1, 1)
    val = get_val()
    percent = int(val/10.23)
    display.ser.write(str(val).ljust(16))
    display.capped_bar(16, percent)


2012-08-26

7 segment display webcam stats

I have finally switched off and dismantled my 7 segment display webcam project. It ran for a couple of weeks before the post went up on the Raspberry Pi web site: "Controlling a 7-segment display from half a world away" on 30th July. My poor little Raspberry Pi couldn't cope with all this attention very well but after the immediate spike of activity subsided it stood up to the challenge admirably.

I decided to call it a day because the number of visitors slowed to about 20 a day and I wanted to use the breadboards and bits and bobs for some new projects. So here are some stats from the MongoDB database where I stored all this info. I have removed duplicate entries by visitor - so if you sat there banging away entering the same number repeatedly it will only contribute once to the totals.

Most popular number

99

The number 99 was entered by 635 people. This was followed by 42 and 69 in equal second place each was entered by 590 people. 0 and 88 came third and fourth.

Least popular number

60

The number 60 was entered by a mere 13 people.

The full listings are available below, just keep scrolling.

Thanks!

Here's a note to say thank you to everyone who left encouraging comments. I'm glad you enjoyed it and I'll be back with more very soon.

Total unique visitors

6223

Total numbers entered

14899

Favourite number by country

Country Favourite Visitors who entered it
AFGHANISTAN321/2
ALAND ISLANDS61/1
ANTIGUA AND BARBUDA771/1
ARGENTINA994/15
AUSTRALIA6931/228
AUSTRIA9912/93
BANGLADESH01/2
BELARUS01/1
BELGIUM699/73
BOSNIA AND HERZEGOVINA01/4
BRAZIL996/37
BRUNEI DARUSSALAM761/1
BULGARIA443/16
CANADA6923/201
CHILE422/6
CHINA123/7
COLOMBIA01/5
COSTA RICA421/2
COTE D'IVOIRE331/2
CROATIA993/15
CYPRUS11/1
CZECH REPUBLIC8812/74
DENMARK010/72
DOMINICAN REPUBLIC111/1
EGYPT01/1
EL SALVADOR11/1
ESTONIA992/10
FAROE ISLANDS423/3
FINLAND4212/80
FRANCE4229/208
GAMBIA881/1
GERMANY4293/713
GHANA131/1
GREECE115/24
HONG KONG883/22
HUNGARY09/56
ICELAND882/12
INDIA9911/56
INDONESIA371/4
IRAN, ISLAMIC REPUBLIC OF11/1
IRELAND697/50
ISLE OF MAN141/1
ISRAEL113/15
ITALY8817/140
JAPAN961/11
KAZAKHSTAN121/1
KENYA251/1
KOREA, REPUBLIC OF772/7
LATVIA122/10
LIECHTENSTEIN121/2
LITHUANIA03/12
LUXEMBOURG331/6
MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF341/4
MALAYSIA123/12
MALI21/1
MALTA881/3
MEXICO05/19
MOLDOVA, REPUBLIC OF341/4
MONACO351/1
MOROCCO101/2
NAMIBIA141/1
NEPAL161/1
NETHERLANDS025/218
NEW CALEDONIA301/1
NEW ZEALAND427/70
NORWAY426/66
PAKISTAN02/3
PARAGUAY111/1
PERU02/4
PHILIPPINES233/12
POLAND013/85
PORTUGAL699/53
PUERTO RICO161/1
ROMANIA119/38
RUSSIAN FEDERATION124/33
SAUDI ARABIA661/2
SERBIA661/6
SEYCHELLES931/1
SINGAPORE652/15
SINT MAARTEN101/1
SLOVAKIA996/32
SLOVENIA133/23
SOUTH AFRICA777/58
SPAIN6911/79
SURINAME121/1
SWEDEN018/171
SWITZERLAND07/65
TAIWAN992/6
THAILAND331/4
TRINIDAD AND TOBAGO661/2
TUNISIA01/2
TURKEY996/41
UKRAINE112/9
UNITED ARAB EMIRATES201/2
UNITED KINGDOM69201/1450
UNITED STATES42130/1288
URUGUAY11/1
VENEZUELA, BOLIVARIAN REPUBLIC OF691/1

Worldwide number popularity

Number Times entered
99635
42590
69590
0557
88468
11462
12405
1332
13302
66280
77278
22267
33260
23248
55225
21176
10172
44167
7152
2147
14128
3123
17123
45116
34113
25111
56111
24108
5105
27105
32103
496
1593
3193
7888
6786
984
3783
4380
7380
4777
1676
875
1975
1874
5473
666
2065
9863
6462
7662
4161
7257
2855
4854
3652
5251
6551
2950
8950
4648
5048
8748
9645
5744
8044
3543
7543
5142
8439
9739
7137
7437
3036
6336
9136
5335
6835
2634
4934
3933
3832
6232
8132
8632
9031
5829
4027
7027
8327
8526
9325
9525
6124
5923
7921
9221
8219
9415
6013