The Techno-Bird Box, a Wildlife Monitor - Hardware Projects - Raspberry Pi Projects (2014)

Raspberry Pi Projects (2014)

Part III. Hardware Projects

Chapter 17. The Techno-Bird Box, a Wildlife Monitor

by Dr. Andrew Robinson

In This Chapter

• Build a light beam

• Get the current time and format it

• Store data to a file

• Implement a state machine

• Filter out noise

• Draw a graph in Python

Have you ever wondered what the birds in your garden get up to when you’re not watching? Thanks to the Raspberry Pi, with a couple of taps on a smartphone you can monitor the bird activity in your garden from anywhere in the world.

In this project, you will build a “techno–bird box” that will keep an eye on the comings and goings of our feathered friends, recording when they enter and leave a nest box.

You can go on to program your Raspberry Pi to do a range of things when it detects a bird. You might want to make it send a message by SMS, Twitter or e-mail or trigger a camera to take a picture. You could log the activity and use it to draw a graph such as the one shown in Figure 17.1.

Figure 17-1: Bird activity shown against temperature.

image

You might want to combine this with other data to find out more about bird behaviour. Are they busier in the evening or early morning? Are your birds more active than your friends’ birds? You could store weather data and then see if temperature, rainfall and wind speed make a difference.

Remote Monitoring

Computers and technology are playing an increasing role in monitoring our wildlife. Zoological Society London (ZSL) is using Raspberry Pis in Africa to save rhinos from poachers. They are using the computational power of the Raspberry Pi to analyse images to detect poacher activity and raise an alert over a satellite data connection. They’re also deploying Raspberry Pis in the sea to monitor sharks.

I used a similar setup to the one in this chapter to collect information about blue tits for the BBC Springwatch programme; for details you can go to the blog at www.bbc.co.uk/blogs/natureuk/posts/Raspberry-Pi-bird-box.

Using computers as data loggers to make measurements is not new, but the increase in computing power means monitoring can be more sophisticated. Better digital communications mean that it’s easy to collect data from around the world in real time. The falling cost of hardware is opening up the opportunity to gather massive data sets. If more people get involved in citizen science, and collect and contribute data through the Internet, new scientific discoveries can be made. Technology has made it easier for everyone to become a scientist, with the potential to be a part of the next earth-shattering discovery.

Alternatively, you could adapt the sensors to monitor small mammals, and perhaps use it to photograph rodents, or even keep an eye on your pet hamster.

In this project, you will detect bird activity with a pair of infrared light beams that are broken when a bird enters or leaves the nest box. One beam is mounted just inside the nest box, and the other is mounted just outside. You will write code that determines bird direction based on the order the beams are broken and unbroken. This chapter introduces some fairly advanced background information, which will help you understand how you can build your own projects in the future. However, if you just want to build a techno–bird box, you can just follow the steps and type in the complete program listing.

Building Invisible Light Beam Sensors

The first step is to build the light sensors. Light beam sensors offer a way of detecting objects without making physical contact. You could use them in another project that detects other animals or for one of the suggestions in the “Over to You” section at the end of this chapter.

The light beam sensors are based on of a pair of components – an emitter and a detector. In order to measure the speed or direction an object (in this case a bird) is moving, you will need to build two pairs.

The Required Parts

It’s quite easy to build your own light beam detectors; parts are readily available for less than £1.

There is a range of many different LEDs and phototransistors that all work in a similar way. They’re available from the typical electronics suppliers online. Although it’s possible to substitute parts, I have tested only the following components. Because you are wiring up electronics to the Raspberry Pi, I suggest that you use PiFace Digital to provide additional protection, and enough current to drive the emitters. You will need

• 2 IR LEDs, such as part number OSRAM - SFH484-2 - INFRARED EMITTER, 5MM, 880NM

• 2 IR photodetectors, such as part number QSE113 - INFRARED PHOTOTRANSISTOR

• 2 1k ohm resistors

• 2 330 ohm resistors

• Wire

Wiring Up the Emitter

The emitters are made using an infrared LED. Infrared LEDs are much the same as normal LEDs, but they emit infrared light rather than the typical red, yellow, blue and so on LEDs you might see on electronic devices. Here’s a reminder of things to consider when using LEDs:

• LEDs work only one way around, so you need to ensure that the correct leg is connected to a +ive (positive) power supply.

• LEDs can be damaged by too much voltage, so you may need a resistor in series.

For most LEDs, the longer lead of the LED (called the anode) should be connected to the power supply. However, if you have the SFH484-2 IR LED, the longer lead is the cathode and should be connected to 0V (also sometimes called ground, negative or -ive).

Connect the anode of the LED to the 330 ohm resistor. Connect the other side of the resistor to the power supply as shown in Figure 17.2.

Figure 17-2: IR SFH484-2 IR LED emitter wiring.

image

Wiring Up the Detector

The detector is made from a phototransistor, which allows current to flow when light falls on it. It must be connected the correct way around, or it may be damaged, and it should also have a resistor in series to limit the current. Phototransistors have pins labelled emitter and collector. When illuminated, current flows into the collector and out of the emitter.

Take care to identify the legs of the phototransistors. Lay the flat side of the phototransistor on your desk, with its legs facing you. The leg on the left is the emitter, and the leg on the right the collector (see Figure 17.3). If you are not using a QSE113 phototransistor, check the data sheet for your device.

Figure 17-3: Identifying the emitter (left) and collector (right) legs of the QSE113 phototransistor.

image

Wire the emitter of the phototransistor to ground. Connect the collector to one side of a 1k ohm resistor. Connect the other side of the resistor to the input pin, as shown in Figure 17.4.

Testing the Sensors

It’s easier to check that your sensors are working before you attach them to the bird box! Start the PiFace Digital emulator (as described in Chapter 9, “Test Your Reactions”) and make sure that Update Inputs is selected. Point the IR LED at the phototransistor. The appropriate input should indicate that it is turned on. As you stop the light falling on the detector, you should notice that the corresponding input is no longer turned on.

Figure 17-4: IR phototransistor detector wiring.

image

If the circuit doesn’t work, check the wiring. Use a multimeter set on resistance to check the resistance of the joints – it should read a few ohms at the most. It’s also worth checking that the polarity is correct – that you’ve connected the IR LED and the phototransistor the right way around. Some digital cameras are sensitive to IR, so you might be able to use one to “see” if the LED is emitting IR light.

After you’ve checked that your sensors work, it’s time to mount them in your bird box.

Mounting the Sensors

You could build your own bird box from plans (such as those that are available online) or buy a ready-made one from a garden centre or elsewhere. Either way, you will need to mount the emitters and detectors near the entrance hole.

It is important not to create a perch for predators near the entrance to the nest box, or they could attack the birds in your box. With this in mind, so that the front face around the entrance remains flat, and to provide some protection to the electronics, it’s best to sandwich the sensors in layers of plywood.

An example layout of the three different layers of plywood is shown in Figure 17.5. You will need one front piece and a set of beam A and beam B pieces for each beam.

Figure 17-5: Cutting plans for plywood sheets.

image

image

Ensure that you use exterior- or marine-grade plywood, as these grades are suitable for use outside. Other grades may disintegrate, potentially while birds are nesting! Also if you use glue, ensure that it’s waterproof, and nontoxic!

To hold the sensors, cut five pieces of plywood the same size as the front of your nest box. Cut a hole in all five, the same size and in the same place as the nest box opening. Check that they all align with the front of your box, and then put one piece of plywood aside. This will become the new front of the box.

You need to cut a slot for the sensors in the four remaining pieces of plywood. Draw a horizontal line across the middle of the entrance hole where the beam will be. Mark out 3 mm above and below the line to define a slot where the light beams will be.

image

It’s better to have the slots horizontal so that they don’t fill up with feathers, dirt, muck and such as the birds enter and leave the nest.

With the four pieces of plywood together, cut outward from the entrance hole, 30 mm along the marked-out slots, as shown in Figure 17.6. Check that the IR emitter and IR receiver will fit at the end of the slots. If necessary, enlarge the end of the slots so that they fit.

Figure 17-6: A closeup of the IR LED emitter and phototransistor receiver in the slot near the hole for the nest entrance.

image

Decide how you will route your wires and where they will come out from the plywood sandwich. It is usually best that they come out from the bottom to avoid water getting in. If you cut slots going to the emitter and receiver on the same piece of wood, you will end up with a separate piece, which is tricky to align. Instead, cut a narrow slot for cables from the outside to the emitter on two pieces of wood (refer to Figure 17.5, beam part A), and on the other piece of wood cut a slot to the receiver (refer to Figure 17.5, beam part B).

Glue beam part A and beam part B together with the sensors fitted and the wires running down the slots. Repeat this for the second beam. Test that both the beams work and then fasten the two beams together and attach the front piece of plywood.

Check that the entrance to the nest box remains unrestricted by the five pieces of plywood. Also check that there is nothing sharp sticking out that could injure the birds, and then mount the pieces of plywood in place on the front of the nest box with either glue or screws.

Protecting Your Raspberry Pi from the Elements

Now is a good time to think where you will house the Raspberry Pi. Although the Raspberry Pi is fairly robust, it’s better to protect it from extremes of temperature or humidity. You have a couple of options:

• Mount the Raspberry Pi near the bird box and run a long wire to supply it with power and transmit data back via Wi-Fi or Ethernet.

• Have your Raspberry Pi inside a building and run long wires from it to the LEDs and phototransistors in your bird box.

You may find a compromise is best, perhaps with your Raspberry Pi in a shed or outbuilding, with medium-length wires running to your nest, and a data connection via Wi-Fi to your broadband router.

If you mount your Raspberry Pi outside, you should use a waterproof box. You can buy these boxes from most electrical distributors or hardware stores. These enclosures typically have a rubber gasket to make a waterproof seal with the lid. If you buy a box that has an IP rating of IP66, you can be pretty sure that your Raspberry Pi will remain dry from water from the outside.

image

Often electronic product packaging contains sachets of silica gel to protect the item from humidity during transit. Put a couple of these sachets in with your Raspberry Pi to keep the humidity in the enclosure under control. You can revitalise silica gel by gently warming it to dry it out.

IP Rating

Enclosures may be given an IP rating that describes how much protection they give to their contents. The IP rating consists of two numbers: The first digit indicates the protection against solid objects, such as tools, fingers and dust, and the second digit describes the protection against liquids. The higher the numbers, the greater the protection offered and the less that can enter the enclosure. For liquids, 1 ranges from protection from vertically dripping water, 5 for protection from jets and 8 for continuous immersion in water.

If the manufacturer wants to provide a rating for only one digit, X is used for the other. For example, an enclosure may be rated IPX6, meaning that it is protected against powerful water jets, but nothing is claimed about the protection from solids.

If you’ve built a project with your Raspberry Pi and need to protect it, the IP rating is a good way of knowing if a manufacturer’s box will be up to the job.

Recording Activity to a File

Now that you have your Raspberry Pi able to detect when the light beams are broken and unbroken, it is time to write a program to record this information. Ultimately you are interested in the direction that the birds are traveling in, which is deduced from the order the beams are broken and unbroken.

Real Time or Post-Process?

The Raspberry Pi is more than capable of running a program that can process the beam break information to determine bird direction in real time – in other words, as it happens. However, for this application it is better to record the raw sensor readings and process the data later. This approach means that information is not thrown away.

If you processed the beam break information and just recorded if a bird left or entered the nest box, you would limit your options for future analysis. For example, what if you later decided that you were interested in the speed the bird entered and left the nest? If you store the time of every instance of a beam’s breaking and unbreaking, you can process it later to determine direction, and you can calculate the speed. If you stored only direction information, this information about speed could not be calculated later.

Furthermore, as you will see later in the “Dealing with Sensor Noise” section, you will need to filter out noise from the sensors, and at a later stage you might develop a more sophisticated filter to process the raw data.

If you wrote a program to process the beam break data in real time and there was an error in it, you would have lost valuable information about your birds. Before deciding to store the unprocessed data, it’s worth doing a quick calculation to predict how much data the bird-logging program would produce. If a bird visited every minute, for 18 hours a day there would be 1080 (60*18) events per day. Assuming the nesting period is at most 60 days, this is 64800 (60*1080) events. If each visit results in 10 sensor events (due to noise) and each takes 30 bytes to store, the total storage required is 19440000 (64800*30*10) bytes, or about 20 megabytes. Because there is plenty of low-cost storage available with a Raspberry Pi on a cheap SD card, it is better to write a very simple program to store the raw beam break data that can be analysed offline later.

Information and Data

When you are considering logging events it’s worth thinking about the information you are storing. Sometimes storage space is limited, or the cost of remotely transmitting data is high. The power available to the computer might be limited, or the computer might be in a hostile environment, so the data is at risk of corruption.

When logging data you may need to consider compression, encryption and error detection and correction.

How much information does a piece of data contain? How do you measure information, for example, and how much information does a web page contain? Is there more information on the Internet than contained in the DNA that describes you? How much can you compress a file? Information theory is a branch of computer science concerned with storing and processing information and data and provides an answer for many of these questions. It can lead to some deep philosophical discussions and mind-blowing concepts. Do you need to read every page of this book to get all the information from it?

Computing is all about taking data in, processing it and outputting it. As such it follows that information theory, which is all about how data is stored and processed, is important to computer science.

Thinking about information theory leads to some fascinating questions – how much information is contained in an English sentence? Text speak shows that it is possible to communicate without needing all the words and letters in a sentence.

When you are collecting data with computers, maybe in a bird box in your garden, maybe in the middle of Africa or maybe on a satellite in a distant part of the solar system, information theory provides tools and reasoning to ensure that the valuable research information gets back safely.

Compression and Checksums

Think of the information stored on a CD. If the CD is slightly scratched, it still plays with no loss of sound quality. If it becomes more scratched, there comes a point when it will not play. Effectively, there is an amount of data that can be lost from a CD without mattering; so this data must be redundant in terms of the information the CD contains. Information is useful in calculating the amount of extra data needed for error correction on CDs and in communication links, such as radio, which may be unreliable.

Now think about converting an audio file from a CD to an MP3 file. Most people cannot tell the difference between playing a CD and an MP3 file, yet the MP3 file size (the amount of data) will be typically a tenth of the size. Nine tenths of the data has apparently been thrown away! This is an example of lossy compression. It takes advantage of assumptions of how the audio data will be interpreted in our brains – for example, if a quiet and a loud sound are played together, the quiet sound isn’t heard, and therefore there is no value to the ear in the data representing it.

If you wanted to record birdsong in your bird box, then without audio compression you would quickly run out of storage space on the SD card. Lossy compression isn’t suitable for all applications; for example, you wouldn’t want to throw away data about bird visits. Instead, lossless compression would be more appropriate.

Lossless compression represents data in a more efficient format. For example, if you had a list of numbers 2,0,0,0,0,0,1, instead of sending each 0 you could encode the list as 2,5x0,1. Zip compression is an example of this form of compression (and is used to make the Raspbian OS download smaller).

If you needed to transfer your bird data remotely, over a mobile phone connection, then you might use zip to make the file smaller, or you might consider doing some other processing (to get rid of noisy readings) to send just the information you need.

In some cases you want to know that the data hasn’t been corrupted. In this case you might add redundant data so that you can detect or even correct any errors. MD5 checksums that represent the contents of a long file with a short sequence of characters are an example of this. You came across MD5 checksums in Chapter 1, “Getting Your Raspberry Pi Up and Running”, to detect if the Raspbian OS download had been corrupted.

The Complete Logging Program

The program in Listing 17.1 records the time, which sensor triggered and whether it was broken or unbroken in file.

Listing 17.1 recordBird.py

#!/usr/bin/env python

import piface.pfio as p

import datetime

p.init()

#Define the inputs/output pin number that

#the beams are connected to.

INNER_BEAM = 0

OUTER_BEAM = 1

#keeps track of the last state the beam was in

#used to know if a new event has occurred

innerBeamStateWasBroken = False

outerBeamStateWasBroken = False

#function to return the current time, formatted as

# e.g. 13 Jun 2013 16:07:30 572

def getFormattedTime():

now = datetime.datetime.now()

return now.strftime("%d %b %Y %H:%M:%S. ") + image

str(int(round(now.microsecond/1000.0)))

#generate and record an event to file

def logEvent(sensor, state):

logFile.write(str(sensor) + "," + str(state) + image

"," + getFormattedTime() + "\n")

#turn on IR LED emitters

p.digital_write(INNER_BEAM_EMITTER,1)

p.digital_write(OUTER_BEAM_EMITTER,1)

#open a file for appending, do not buffer

logFile = open('birdlog.txt', 'a', 0)

#indicate the point the program started in the log

logFile.write("###starting up at:" + getFormattedTime() + "\n")

#main loop of the code

while (True):

#read the current state of the beam

innerBeamIsBroken = (p.digital_read(INNER_BEAM) == 0)

outerBeamIsBroken = (p.digital_read(OUTER_BEAM) == 0)

##handle Inner Beam

# if the beam has become broken, that is if the beam

# was not broken before but is now,

# then record that the beam was broken and log to file

if (not innerBeamStateWasBroken and innerBeamIsBroken):

innerBeamStateWasBroken = True

logEvent(0,1)

#print "inner beam has been broken"

# this detects when the beam has become un-broken again.

# That is when the beam was broken and it is not broken

# any longer. When this occurs, record the new state and

# log it to file

if (innerBeamStateWasBroken and not innerBeamIsBroken):

innerBeamStateWasBroken = False

logEvent(0,0)

#print "inner beam has been un-broken"

##handle Outer Beam, with same structure as inner

if (not outerBeamStateWasBroken and outerBeamIsBroken):

outerBeamStateWasBroken = True

logEvent(1,1)

#print "outer beam has been broken"

if (outerBeamStateWasBroken and not outerBeamIsBroken):

outerBeamStateWasBroken = False

logEvent(1,0)

#print "outer beam has been un-broken"

The structure of Python programs should be familiar to you by now, but it is worth considering new concepts introduced and reinforcing others.

Constants

Some languages (such as C) have constants to hold values that do not change over the lifetime of a program. They allow programmers to define the value of a constant in one place and then refer to it throughout the program. If the programmer updates the program, the value needs to be changed in only one place. This is easier than searching through the code looking for all instances of the value to change. In Listing 17.1 the pin number of the PiFace interface that is connected to the beams is defined in INNER_BEAM and OUTER_BEAM. As such, if the circuit is changed, the value needs to be updated in only one place in the program. This is part of program design for the future. It takes into consideration that the beams may be connected to other pins and so makes it easy for them to be changed in one place at the top of the program.

image

Unlike other programming languages, Python does not really have constants. However, the convention is to use variables with all uppercase names instead.

Detecting Changes of State

The variables innerBeamStateWasBroken and outerBeamStateWasBroken are initialised to False. These variables are used to detect if the state of the input pin has changed, so the event of a beam changing can be recorded.

Formatting the Current Time

The function getFormattedTime() is used to get the current time and format it. Looking inside the function, you’ll see that the variable now gets a datetime.datetime object containing the current date and time:

now = datetime.datetime.now()

The method strftime is called with an argument that specifies how the current time is formatted:

return now.strftime("%d %b %Y %H:%M:%S. ") + image

str(int(round(now.microsecond/1000.0)))

The format argument provides a template of codes in a string. When the program runs, the codes in Table 17.1 are replaced by different parts of the date and time stored by the object.

Table 17-1 Date-Formatting Codes

!

Code

Meaning

English Example

%a

Shortened weekday name

Mon

%A

Full weekday name

Monday

%b

Shortened month name

Mar

%B

Full month name

March

%d

Day of the month

14

%H

Hour (24-hour clock) as a decimal number

18

%I

Hour (12-hour clock) as a decimal number

6

%m

Month as decimal number

03

%M

Minute

15

%p

AM or PM (or local equivalent description)

PM

%S

Second

12

%y

Two-digit year

13

%Y

Four-digit year

2013

%%

Use to display a single % sign

%

image

Different places around the world have different conventions; for example, the 14th of March in the U.K. would be written as 14/03, but in the U.S. as 03/14. Linux locale sets the various settings like these that are unique to a particular place in the world. For example, it also defines the currency used, default paper size and keyboard layout. strftime uses your locale setting to determine what %p displays. It also uses it to make %c format the date and time appropriately for you.

Why not try it yourself in an interactive Python session? Type the following

import datetime

datetime.datetime.now().strftime("Today is: %A %d %B %Y. image

The time is: %I:%M:%S %p")

image

Create a format string to format the time and date so that if now was 6:15 p.m. on the 14th of March, it would be displayed as 1815 2013-03-14.

There are other codes too. Find out what %j does.

You will notice that there is no code to display the time in milliseconds. As the time between beams breaking and unbreaking can be less than a second, a way to format the time in milliseconds needs to be created by the programmer.

The datetime.datetime object has the attribute microsecond. Because there are 1000 microseconds in a millisecond, this number is divided by 1000, rounded to the nearest whole number by the int and round functions and then converted to a string and concatenated to the end of the formatted date/time.

Writing to a File

The logEvent function is called to record an event to a file. You will see later that in the program initialisation a file object called logFile is created:

#generate and record an event to file

def logEvent(sensor, state):

logFile.write(str(sensor) + "," + str(state) + image

"," + getFormattedTime() + "\n")

The logEvent function builds the string to be written to the file and then passes it to the write function of the logFile object. Which beam and how it changed are passed as arguments to the function and stored separated by commas. The end of the string written is an “\n”, which is a special character code to create a new line in Linux.

image

The code for a new line varies across operating systems. Files in Microsoft Windows require the code “\r\n” instead of just “\n”.

Initialisation

After the function definitions come the statements that set things up for the main part of the program. The IR LED emitters are controlled by the outputs of PiFace Digital, so they need to be turned on. This is done with the digital_write function that sets a pin to value 1, that is, turn on.INNER_BEAM and OUTER_BEAM specify the pin numbers the IR LEDs are connected to:

#turn on IR LED emitters

p.digital_write(INNER_BEAM,1)

p.digital_write(OUTER_BEAM,1)

Working with Files

Before a Python program (and programs written in most other languages) can write data to a file it needs to open it first. Python does this with the open function, which takes a filename, mode and buffer length as arguments and returns a file object. The file object provides an easy way of keeping track of the file that has been opened.

It is worth studying the open function in more detail, looking at the example in the program:

#open a file for appending, do not buffer

logFile = open('birdlog.txt', 'a', 0)

The function returns a file object that is stored in the logFile variable. This variable is used in your logEvent function to identify which file the data about the event is to be written to.

Opening Files – Filename

The first argument of the function, ‘birdlog.txt’, is the name of the file that will be opened. It is relative to where the program was run from. This means that birdlog.txt will be opened in the same directory where the command was issued to run your program. If you wanted to create the file somewhere else in the filesystem, you would need to give the appropriate file path.

Linux File Paths

Linux, like most operating systems, has a hierarchical filesystem; that is, it has a tree type structure with files in directories (sometimes called folders), directories within other directories and so on. It provides a means to describe the location of a file within the tree of directories as a file path. The top of the tree is the root, where it is not possible to have directories above it.

The path to a particular file may be given relative to another location in the filesystem or the full path from the root of the tree.

For example, in a terminal type pwd to show the current directory. By default this is usually your home directory. Type ls to list the files and directories you have in this directory. To get to know how file paths work in Linux, it’s time for a quick tour:

Linux refers to the root of the tree by starting paths with a forward slash (/). Change to the root of the tree by typing cd /.

Type ls to show the contents.

Type cd etc to move into the etc subdirectory that contains the configuration files for Linux. Type pwd to show the path. Linux will print /etc, indicating that you are in the etc directory under the root of the tree. Now move into the network settings subdirectory by typing cd network. Type pwd again. Linux will print /etc/network, indicating that you are in the network subdirectory of the etc subdirectory of root.

From this you should be able to see that directories are separated with slashes, and that a path starting with / is relative to the root.

Linux users get their own directory to store their files in, which Linux calls a user’s home directory.

Type cd ~ to change to your home directory. Type ls and you will see your files. Type pwd and you will see that your home directory’s full path is /home/pi.

Type cd .. and you will move up into the parent directory. Type pwd and you will see you are now at /home. Type cd ../ and you will now be at the root. Type pwd and you will see you are at /. Type cd ../ again and you will still be at the root as you can’t go any higher!

Here is a summary of what you’ve just seen:

• / – refers to the root, the topmost point of the filesystem, independent of your current position.

• ./ – refers to the current directory, that is the directory you are in.

• ../ – refers to the current parent directory, that is the directory that is containing your current directory.

• ~/ – refers to your home directory.

• ~username – refers to the home directory of username.

After you’ve mastered file paths you can use them within your programs to specify files anywhere in the filesystem. Remember, pwd, cd and ls are useful commands to run in a terminal when you are moving around the filesystem and to check file paths.

Opening Files – Mode

The second argument, ‘a’, specifies the mode the file is opened with. In this program you want to append to the file – in other words, add to the contents already there. (If the file doesn’t exist in the first place, it is created.) If you wanted to replace a file, then you should use the write mode with an argument of ‘w’. In other programs you might want to read from a file without changing it, and in this case you should open a file with a mode of ‘r’.

In summary, files can be opened in three different modes:

• r – read. The file is read only, so you know you will not change the contents.

• w – write. The file is cleared and started from the beginning.

• a – append. The file can be added to.

Opening Files – Buffer Size

The third argument of the open function is the size of the file buffer – that is, the number of characters that will be grouped together before transferring them from memory onto the computer’s permanent storage. For logging birds, it is sensible to have a buffer size of 0 so that the data is written to the SD card as soon as possible. That way, if the power fails before the buffer has been written out to the SD card, the least amount of data is lost.

Buffering

Buffering is a way to work more efficiently. For example, most people do not put the washing machine on for individual items of clothing; instead they collect their clothes together in batches. This is because it takes about the same amount of time and energy to do a batch of washing as it does for an individual item.

In the case of computers, writing characters to disk is similar; writing a few characters to a file takes about the same time as writing a single character. As such, the computer collects characters together in a buffer in memory, and then writes them out (calledflushing) when it is full. Of course, if the power is interrupted before the computer has flushed the buffer, the data is lost.

Writing an Initial Timestamp

The first thing written to the file when the program starts up is a timestamp:

#indicate the point the program started in the log

logFile.write("###starting up at:" + getFormattedTime() + "\n")

This makes it easier to look at the logs to check when a program started running. If the clock is set incorrectly on the Raspberry Pi and you know the real time you started the program logging, you can use the date recorded in the file to calculate the offset to correct the times recorded for the bird activity.

The Main Loop

The main part of the program is a while loop that repeats forever. On each loop, it looks at the current state of the light beams by reading the PiFace Digital inputs as shown in the following:

while (True):

#read the current state of the beam

innerBeamIsBroken = (p.digital_read(INNER_BEAM) == 0)

outerBeamIsBroken = (p.digital_read(OUTER_BEAM) == 0)

Next are two pairs of if statements, one for each beam. The purpose of these statements is to see if the state of the beam is different from the last time it was checked. This is done by comparing the state of the beam that has been read into the variable innerBeamIsBroken with the state previously recorded in the variable innerBeamStateWasBroken. If there is a change, innerBeamStateWasBroken is updated, and the logEvent function is called to record it to the file:

if (not innerBeamStateWasBroken and innerBeamIsBroken):

innerBeamStateWasBroken = True

logEvent(0,1)

if (innerBeamStateWasBroken and not innerBeamIsBroken):

innerBeamStateWasBroken = False

logEvent(0,0)

#print "inner beam has been un-broken"

Testing the Program

You should be familiar with the notion of testing your programs as you write them. Uncomment the print statements in the code to indicate when the beams are broken and unbroken and run the program. Use a Ping-Pong ball or other object to break and unbreak the light beams to check that your program prints the correct corresponding messages.

Press Ctrl + C to exit the program and check the events have been logged to the birdlog.txt file. You can quickly look at the contents of a file in Linux by using the more command. From the command line, type

more birdlog.txt

###starting up at:13 Jun 2013 16:07:29.425

0,1,13 Jun 2013 16:07:30.496

1,1,13 Jun 2013 16:07:30.572

0,0,13 Jun 2013 16:07:30.792

1,0,13 Jun 2013 16:07:30.961

Check that your file looks similar to entries shown here. If so, it is time to write another program that will analyse the log file to translate the raw sensor data to bird actions.

You can download a sample log file from www.wiley.com/go/raspberrypiprojects.

Processing the Data

After you have the data of when the sensors break and unbreak it is time to write a program to interpret this to know whether a bird is entering or leaving the nest box. To do so, you will write a program that reads in the log file and processes it with a state machine. You will also see that it is necessary to filter out noise.

Think about a bird entering the box, and how the sensors will record it. Here is the list of events:

1. The outer beam breaks.

2. The inner beam breaks.

3. The outer beam clears.

4. The inner beam clears.

When a bird leaves the nest box, the order will be as follows:

1. The inner beam breaks.

2. The outer beam breaks.

3. The inner beam clears.

4. The outer beam clears.

To interpret the sensor data, you need to write a program that looks for these orders of events. However, birds don’t just fly in and out of their nest boxes; they also might pop just their heads out, have a look around and then pop back into the box.

This bobbing out of heads has sensors in this sequence:

1. The inner beam breaks.

2. The outer beam breaks.

3. The outer beam clears.

4. The inner beam clears.

When a mother bird is tending her young, she may stand in the nest box entrance and pop her head into the box. It may be clearer to represent this number of sequences in a diagram, as shown in Figure 17.7. The next task is to write a program to implement this diagram, which you will see is best done with a state machine.

Building a State Machine

Diagrams similar to Figure 17.7 that show the transition (movement) between sequences of states occur frequently in computer science, and as such there are standard techniques to implement one in code. State machines (sometimes called more fully, finite state machines or FSMs) consist of states and conditions that govern when there is a change from one state to another.

In the diagram in Figure 17.7, each circle is a state, with the transitions (movement) between states represented by arrows and labelled with the event that causes the transition. The arrow from the block dot shows the starting state.

State machines are implemented in programs with a variable to hold the current state and a loop. The loop contains a series of if statements that determine when there should be a change from one state to the next. On each iteration of the loop, all the conditions that allow the leaving of the current state are tested, and if satisfied the current state is updated, together with any necessary actions required for the transition.

Figure 17-7: A state diagram for birds entering, leaving and bobbing their heads.

image

The Basic Analysis Program

To implement the state machine, enter the Python code in Listing 17.2 into a new file, analyseBirdDataBasic.py.

Listing 17.2 analyseBirdDataBasic.py

#!/usr/bin/env python

import sys

import datetime

#print a debug message if debugging is turned on

def debug(msg):

if DEBUG:

print msg

state="IDLE"

#Constants

INNER_BEAM = 0

OUTER_BEAM = 1

BROKEN = 0

UNBROKEN = 1

DEBUG = True

outwardTimes = []

# loop over every line that is being piped into the program

for line in sys.stdin:

#ignore lines beginning#

if line.startswith('#'):continue

# remove the new line character

line = line.rstrip('\n')

#split the text at each comma. This creates

#the event array, with three items

event = line.split(',')

#store the first item in the sensor variable -- this

#is which beam (inner or outer)

sensor = int(event[0])

#store the type of the event in the eventType

#variable -- this is if the beam broke or unbroke

eventType = int(event[1])

# convert the text representation of the time into

#a datetime object in eventTime variable

eventTime = datetime.datetime.strptime(event[2],image

"%d %b %Y %H:%M:%S.%f")

debug ("RawEvent:"+line+", Current STATE:"+state)

#Main state machine

#The idle state is waiting for either the outside

#or inside beam to be broken.

if (state == "IDLE"):

# the inside sensor has been broken

if ((sensor==INNER_BEAM) and (eventType==BROKEN)):

debug ("on way out")

state = "OUTWARD_BOUND"

if ((sensor==OUTER_BEAM) and (eventType==BROKEN)):

debug ("on way in")

state = "INWARD_BOUND"

# the bird has started leaving. Check to see if

# it bobs back or breaks the next sensor

elif (state == "OUTWARD_BOUND"):

# if bird puts head back in box return to IDLE

if ((sensor==INNER_BEAM) and (eventType==UNBROKEN)):

debug ("inside bob back")

state = "IDLE"

# bird continues and breaks the OUTER_BEAM

elif ((sensor==OUTER_BEAM) and (eventType==BROKEN)):

debug ("outward")

# bird is still going out, wait for the inner

# beam to clear

state = "WAITING_OUTWARD_I_CLEAR"

##the bird is still in the entrance hole, blocking

##both beams

elif (state == "WAITING_OUTWARD_I_CLEAR"):

if (diffTime.seconds < 5):

# bird is very nearly out, waiting for the

# outside beam to clear now

if ((sensor==INNER_BEAM) and (eventType==UNBROKEN)):

debug ("inside cleared, waiting for outside image

to clear")

state = "WAITING_OUTWARD_O_CLEAR"

elif (state == "WAITING_OUTWARD_O_CLEAR"):

if ((sensor==OUTER_BEAM) and (eventType==UNBROKEN)):

debug (str(eventTime)+" BIRD has left")

outwardTimes.append(date2num(eventTime))

state = "IDLE"

#set of states for bird coming in

elif (state == "INWARD_BOUND"):

if ((sensor==OUTER_BEAM) and (eventType==UNBROKEN)):

debug ("outside bob out")

state = "IDLE"

elif ((sensor==INNER_BEAM) and (eventType==BREAK)):

debug ("inward")

state = "WAITING_INWARD_O_CLEAR"

elif (state == "WAITING_INWARD_O_CLEAR"):

if ((sensor==OUTER_BEAM) and (eventType==UNBROKEN)):

debug ("outside cleared, waiting for inside to clear")

state = "WAITING_INWARD_I_CLEAR"

elif (state == "WAITING_INWARD_I_CLEAR"):

if ((sensor==INNER_BEAM) and (eventType==UNBROKEN)):

state == "IDLE"

debug (str(eventTime)+" BIRD has returned")

# if we end up in any other state, then we generate an error

else:

debug ("error")

raise ValueError, "unexpected input block state: "+state

The main part of the program in Listing 17.2 is performed by the for loop which reads stdin, line by line:

for line in sys.stdin

Each loop iteration processes another line of the file. First the line is split at each comma and placed into separate variables. The datetime.datetime.strptime function is the opposite of the strftime function, converting a string-formatted time into a date object that Python can process.

The variables are tested by the if statements in the state machine to determine if a state transition should be made. If it is detected that a bird has left, then the time it left is appended into the outwardTimes array. You will use this array to draw a graph of activity.

Standard Streams

Linux supports standard streams, a mechanism to allow the output of one program to be fed into another one. This allows very powerful combinations of programs to be connected together on the command line to process data. There are three standard streams:

• Standard in (stdin) – Used for input.

• Standard out (stdout) – Used for output.

• Standard error (stderr) – Another sort of output, reserved for reporting error messages.

Essentially standard streams can be thought of as special files. One program will write to its stdout stream, which can be piped (think of the commands as stages in a pipeline) to the stdin stream of another. This program reads from its stdin to receive the data. If no other program is specified, by default stdin will be taken from the keyboard, and stdout will be displayed on the screen in the terminal.

The | character is used to indicate that the output of one program should be piped into another. For an example of how pipes work, imagine that you want to sort the list of the files in your home directory in reverse order. The command ls lists the contents of a directory to stdout. The sort command takes input from stdin, orders it and sends it to ­stdout. The -r argument tells sort to reverse the ordering. So, to reverse sort a list of contents of a directory, in a terminal type

ls | sort -r

****sort uniq wget cat grep >

To test the analysis program, use the cat program to pipe the contents of your birddata.txt file into your Python program by typing the following in a terminal:

cat birddata.txt | python analyseBirdDataBasic.py

With DEBUG=1 set, the program will go through the state machine, process the sensor log entries and print a description of what it thinks the bird is doing.

Dealing with Sensor Noise

In everyday life, someone may say that an environment is too noisy for him or her to hear properly. Noise is the sound that is unwanted and getting in the way of what the listener wants to hear. In computing the term noise is used to describe data that is not wanted and gets in the way. Your light beams are noisy sensors, not in that they make a sound, but that they generate data that’s not meaningful information. The noise occurs because an object passing through the beam may cause it to break and unbreak a few times. Think of a bird passing through the nest box entrance – as well as its body breaking the beam, its tail may wag up and down, additionally breaking the beam as it leaves.

You could run the program again and test it with your finger. As you move it away from the central beam in the hole it may trigger multiple beam break and unbreak events. If the software considered every time a beam broke to be a bird leaving the nest, it would record a higher number than the actual number of bird visits.

Filtering Out Noise

You may be familiar with the reduction of audio noise through the use of filters; you might turn the treble down to remove a high-pitched hiss. Similarly, in computing, data can be filtered to remove noise.

Looking at logs, you should notice that the additional beam break events are usually very short. In comparison, the time taken for a bird to leave the nest box, turn around and come back is a lot slower. Because the time that events occurred is recorded, this information can be used to filter out breaks and unbreaks that happen in quick succession.

The world is full of unknowns that computers have to try and make sense of: Did a bird really leave, or did its tail cause a second beam break to be recorded? Has some dirt been caught in the sensor? You can make your analysis program very complicated by modeling as many possible explanations of sensor readings. In industry sensor models may consider probability to best determine what is happening.

Filtering Contact Bounce

Filtering is used more often than you might think in computers. Inputs from mechanical switches, such as key presses on the keyboard, are filtered to remove contact bounce. Contact bounce happens when a switch is opened or closed and the metal contacts of the switch bounce off each other as they leave or come together. This bouncing action causes a number of pulses to be sent to the computer. Because, in the case of a keyboard, a user wouldn’t want multiple key presses to be registered, the signal is filtered. Typically, this filtering creates a short period when the signal is ignored, which gives the contacts enough time to settle.

The Analysis Program with Noise Filtering

Listing 17.3 shows the modified basic analysis program to include the concept of time. It is shown in full here.

Listing 17.3 analyseBirdDataFiltered.py

#!/usr/bin/env python

import sys

import datetime

#print a debug message on if debugging is turned on

def debug(msg):

if DEBUG:

print msg

state="INIT"

#Constants

INNER_BEAM = 0

OUTER_BEAM = 1

BROKEN = 0

UNBROKEN = 1

DEBUG = True

# timestamp of event from previously processed event

lastEventTime = 0

outwardTimes=[]

# loop over every line that is being piped into the program

for line in sys.stdin:

#ignore lines beginning #

if line.startswith('#'):continue

# remove the new line character

line = line.rstrip('\n')

#split the text at each comma. This creates the event

#array, with three items

event = line.split(',')

#store the first item in the sensor variable – this

#is which beam (inner or outer)

sensor = int(event[0])

#store the type of the event in the eventType

#variable -- this is if the beam broke or unbroke

eventType = int(event[1])

# convert the text representation of the time into

#a datetime object in eventTime variable

eventTime = datetime.datetime.strptime(event[2],image

"%d %b %Y %H:%M:%S.%f")

debug ("RawEvent:"+line+", Current STATE:"+state)

#Main state machine

#INITIALISATION, runs the first time around the loop

if (state == "INIT"):

lastEventTime = eventTime

state = "IDLE"

#calculate the time since the last event took place

diffTime = eventTime - lastEventTime

#to recover state, if the last event was greater than

#30 seconds ago, treat it as independent and reset the

#state machine to IDLE.

#since the idle state of the box is both sensors

#unbroken, waiting for an entry or exit to start

if (diffTime.seconds > 30):

if (state != "IDLE"):

debug ("TIMEOUT - statemachine reset")

state = "IDLE"

#The idle state is waiting for either the outside or

#inside beam to be broken.

if (state == "IDLE"):

# the inside sensor has been broken

if ((sensor==INNER_BEAM) and (eventType==BROKEN)):

debug ("on way out")

state = "OUTWARD_BOUND"

#record when the inner beam was broken

outTime = eventTime

if ((sensor==OUTER_BEAM) and (eventType==BROKEN)):

debug ("on way in")

state = "INWARD_BOUND"

## the bird has started leaving. Check to see if it

##bobs back or breaks the next sensor

elif (state == "OUTWARD_BOUND"):

# check that this event is within 5 seconds of the last

if (diffTime.seconds < 5):

# if bird puts head back in box return to IDLE

if ((sensor==INNER_BEAM) and (eventType==UNBROKEN)):

debug ("inside bob back")

state = "IDLE"

# bird continues and breaks the OUTER_BEAM

elif ((sensor==OUTER_BEAM) and (eventType==BROKEN)):

debug ("outward")

# bird is still going out, wait for the inner

# beam to clear

state = "WAITING_OUTWARD_I_CLEAR"

##the bird is still in the entrance hole, blocking

##both beams

elif (state == "WAITING_OUTWARD_I_CLEAR"):

if (diffTime.seconds < 5):

# bird is very nearly out, waiting for the

# outside beam to clear now

if ((sensor==INNER_BEAM) and (eventType==UNBROKEN)):

debug ("inside cleared, waiting for outsideimage

to clear")

state = "WAITING_OUTWARD_O_CLEAR"

else:

debug ("timeout on waiting outward I Clear")

state= "IDLE"

elif (state == "WAITING_OUTWARD_O_CLEAR"):

if (diffTime.seconds < 5):

if ((sensor==OUTER_BEAM) and (eventType==UNBROKEN)):

debug (str(eventTime)+" BIRD has left")

outwardTimes.append(date2num(eventTime))

state = "IDLE"

if ((sensor==INNER_BEAM) and (eventType==BROKEN)):

debug ("inside has broken again -- image

waiting for it to clear again")

state = "WAITING_OUTWARD_NOISE_CLEAR"

else:

debug ("timeout on waiting outward O Clear")

state= "IDLE"

elif (state == "WAITING_OUTWARD_NOISE_CLEAR"):

if (diffTime.seconds < 5):

if ((sensor==INNER_BEAM) and (eventType==UNBROKEN)):

debug ("inside cleared again, waiting forimage

outside to clear")

state = "WAITING_OUTWARD_O_CLEAR"

else:

debug ("timeout on waiting outward noise Clear")

state= "IDLE"

#set of states for bird coming in

elif (state == "INWARD_BOUND"):

if (diffTime.seconds < 5):

if ((sensor==OUTER_BEAM) and (eventType==UNBROKEN)):

debug ("outside bob out")

state = "IDLE"

elif ((sensor==INNER_BEAM) and (eventType==BROKEN)):

debug ("inward")

state = "WAITING_INWARD_O_CLEAR"

elif (state == "WAITING_INWARD_O_CLEAR"):

if (diffTime.seconds < 5):

if ((sensor==OUTER_BEAM) and (eventType==UNBROKEN)):

debug ("outside cleared, waiting for insideimage

to clear")

state = "WAITING_INWARD_I_CLEAR"

else:

debug ("timeout on waiting inward O Clear")

state= "IDLE"

elif (state == "WAITING_INWARD_I_CLEAR"):

if (diffTime.seconds < 5):

if ((sensor==INNER_BEAM) and (eventType==UNBROKEN)):

state == "IDLE"

debug (str(eventTime)+" BIRD has returned")

state = "IDLE"

else:

debug ("timeout on waiting inward I Clear")

state= "IDLE"

# if we end up in any other state, then we generate

# an error

else:

debug ("error")

raise ValueError, "unexpected input block state: "+state

#independent of state

#update the lastEventTime to be the value from this loop

#for next iteration of loop

lastEventTime = eventTime

Ensure that debugging is turned on (check that DEBUG=1 is set) and run the program by typing the following:

cat birddata.txt | python analyseBirdDataFiltered.py

Python will print the states of the state machine as it processes your bird data. The next step is to visualise this data so that you can see trends about your bird behaviour.

Drawing a Graph

You may be familiar with using a spreadsheet to draw charts or graphs. LibreOffice is a free office suite that runs on the Raspberry Pi. It contains the spreadsheet program Calc, which is largely compatible with Microsoft Excel and Google Docs Spreadsheets. The most appropriate representation of bird activity is a histogram showing the frequency of bird visits. Although it is possible to produce a histogram with Microsoft Excel using the Data Analysis add-in, it is a manual process with multiple steps.

Python can programmatically create graphs with a module called matplotlib. The numpy module is also useful for mathematical and statistical operations. To install the modules, on the command line type the following:

sudo apt-get install python-matplotlib python-numpy

Create a new Python file called drawgraph.py in the same directory as analyseBirdDataFiltered.py and enter the code in Listing 17.4.

Listing 17.4 drawgraph.py

import numpy

import matplotlib

import math

matplotlib.use('PDF')

from matplotlib import pyplot

from matplotlib.dates import DateFormatter, DayLocator,image

HourLocator

from matplotlib.dates import date2num, num2date

def plotDatehist(dates, binCount, title=None,image

intervalSize=None ):

#create histogram

(hist, bin_edges) = numpy.histogram(dates, binCount)

#calculate width of each bin

width = bin_edges[1] - bin_edges[0]

#initialise chart drawing

fig = pyplot.figure()

#create the object for the chart

ax = fig.add_subplot(111)

#draw a bar chart, starting at the first data point, data

#for bars will be held in hist variable.

#the width of the bars is the width you calculated of the

#binss. The colour of the bar will be orange

ax.bar(bin_edges[:-1], hist, width=width,image

facecolor='orange')

#label the y axis

ax.set_ylabel('visit rate per ' + str(intervalSize/60)image

+ "mins")

#label x axis

ax.set_xlabel('Time')

#add title if one specified

if title:

ax.set_title(title)

# format the x axis

# see: http://matplotlib.org/examples/api/date_demo.html

# for more info

ax.xaxis.set_major_locator(DayLocator())

# format major tick boxes as Day of month/Month Hour:Min

ax.xaxis.set_major_formatter(DateFormatter('%d/%m %H:%M'))

ax.xaxis.set_minor_formatter(DateFormatter('%H'))

#uncomment the set_minor_locator function to put minor

#lines for every hour

#if the logging has run for some time, then there are

#too many to display!

#ax.xaxis.set_minor_locator(HourLocator())

# format the coords box, which is displayed when mouse

# is over graph

ax.format_xdata = DateFormatter('%d %H:%M')

ax.grid(True)

fig.autofmt_xdate()

return fig

def drawGraph(events,filename):

#determine the number of bins (bars) on the graph by

#splitting the time the data spans by a time interval.

#calclulate the time spanned by the data

latestReading = num2date(max(events))

earliestReading = num2date(min(events))

dateRange = latestReading - earliestReading

numberOfSeconds = dateRange.seconds + dateRange.days *image

24 * 3600

#chop the data up into roughly 20 min intervals (in seconds)

intervalSize = 20*60

#calculate how many intervals are there in numberOfSeconds

#round up so there is always at least one

histogramBins = math.ceil(float(numberOfSeconds)/image

float(intervalSize))

#draw the graph

fig = plotDatehist(events, bins=histogramBins, title=image

"Bird Box Activity", intervalSize=intervalSize)

#save the graph to a file

pyplot.savefig(filename)

The code contains two functions, drawGraph and plotDatehist. drawGraph sets up the parameters for drawing the chart. It calculates the number of bins (bars) for the histogram. It then calls the plotDatehist function, which actually creates the graph.

plotDatehist uses the histogram function from the numpy library to gather the visits together into time periods which correspond to the bins for the histogram. The histogram function takes the array of data (in this case the times when the nest box was visited) and the number of bins it should be split into. It returns both the number of items in each bin and the highest and lowest points for each bin. The function sets up the layout of where the charts will be drawn. In this case subplot(111) states that one chart will be drawn. The line

ax.bar(bin_edges[:-1], hist, width=width, facecolor='orange')

draws the bar chart.

The remainder of plotDatehist function sets up the axes – setting labels and grid lines. Finally, the graph is saved to file as a PDF.

To draw graphs from the analysis program, edit the file analyseBirdDataFiltered.py and make the following additions:

1. At the start of the file, add

import drawgraph

2. On the last line add the following line, which will call your graph-drawing function:

drawgraph.drawGraph(outwardTimes, “birdGraph.pdf”)

3. Finally, test that your program produces a graph by running it:

cat birddata.txt | python analyseBirdDataFiltered.py

Your program should produce a PDF with a chart showing bird activity. You can view the PDF on the Raspberry Pi with the command xpdf. If xpdf is not installed, install it with apt-get as follows:

apt-get install xpdf

To view the chart on the Raspberry Pi with the graphical environment running (type startx if it is not), into a terminal type

xpdf birdGraph.pdf

Putting the Nest Box into Service

With all the code written and tested, it is time to get your Raspberry Pi in service gathering real data on wildlife. Before deploying you need to remove the test data by deleting the birddata.txt file. To delete a file from the command line, you can type rm filename.

Mount the Raspberry Pi and nest box securely and start the recordBird.py logging program running. You can check for bird activity by looking for entries in the birddata.txt log file with the more command.

Hopefully you’ll see some activity, after which you can analyse the data by running the analyseBirdDataFiltered.py program.

Over to You

With your nest box gathering valuable information about the habits of the birds in your garden, it is worth considering how you can take things further. Some of these suggestions are simple to implement; you can reuse code from other chapters of the book. Others are more involved and will require further research.

You could extend the logging program to send a tweet when one of the sensors was broken so that you knew it was worth watching the box. A good starting point would be to modify the code from Chapter 10, “The Twittering Toy”. Look online for further documentation about the Twitter API – the PostUpdate function can help. Here’s a hint:

api.PostUpdate('Bird Activity detected!')

A development of this would be to take a photo or short sequence of video when the beam is broken. You can execute raspistill or raspivid from Python to control the Raspberry Pi camera. Chapter 10 shows how to call an external process. Look on the Raspberry Pi forums or search Google for more information about controlling the camera.

If you don’t have a bird nest box, you could fit the sensors to a bird feeder, or to the entrance of a box on the floor containing bait to capture rodents coming in and going out.

Sharing Your Data with Others

How does your bird data compare with the data collected by other people?

You could share your photos or activity graphs by uploading them to a web server, perhaps over FTP or ssh if you are familiar with these. If you use Google Sites or WordPress, there are APIs available to transfer images. You could look at Chapter 15, “Facebook-Enabled Roto-Sketch” and use that as a basis to publish to Facebook.

You could set your Raspberry Pi up to be a wireless access point, run its own web server and check on your birds from your smartphone.

If you are really feeling adventurous, why not write a web application that stores bird activity data and plots it on a graph, or as an overlay on Google Maps? You would have to design your own API that would allow Raspberry Pis to submit their data.

Adding More Sensors

You could interface your Raspberry Pi to more sensors. The Raspberry Pi bird box used on Springwatch also recorded data from a weather station. This allowed trends in bird activity to be linked to changes in the weather conditions. You could also add more instrumentation to the nest box, perhaps recording the temperature and humidity inside. Search online for information about interfacing analogue sensors to the Raspberry Pi.

The Possibilities Are Endless

As you can see, the possibilities for extending this, like the other projects, are endless. The aim of this book is to have put you on the road to discover how exciting computing can be, while building really fun projects. You may not have understood every piece of code at first, but in computing this is not uncommon for experts! Sometimes you may need to spend some time playing with the code, copying bits out, changing it, experimenting to find out how it works. The background information in the chapters will hopefully give an introduction to the basics, which you can use to know how to modify the projects to make them your own. Treat it a bit like a cookbook where you reuse techniques from recipes in your own dishes.

You never stop learning with computing; technology will continue to change, but the underlying principles remain more constant. If you learn these principles, you can apply them to the latest technology and know where to find reference documentation. Sometimes part of the challenge is knowing what the jargon means, hopefully some of which this book has demystified.

Computing touches nearly every part of modern life. You can use it as a springboard into virtually any field. Computing changes the world. And often it starts with an idea and some code written by one or two people. Think how mobile phones, digital cameras, MP3 players, Facebook and Twitter affect millions of people. Anita Borg, Steve Furber, Bill Gates, Steve Jobs, Martha Lane Fox, Sophie Wilson and Mark Zuckerberg – contemporary famous names in the field of computing – all started with an idea. You and your invention can be part of it. It’s over to you, what are you going to create? How will you change the world?