Sniff is a "Scratch-like" programming language that's designed to help Scratchers move gently from Scratch to more conventional languages. They can start writing programs, without having to learn a new language because Sniff is based on Scratch. They learn a little more about variables, compiling, syntax errors (!), and they can have fun controlling real hardware while they're doing it.

Monday, 7 July 2014

Are you high? (calculating altitude!!!)

Last week I was at a demo of the Engduino. It's an arduino comparable with a whole bunch of extra sensors built in - LEDS, accelerometer, magnetometer, temperature, light sensor, and IR. The big thing they've got going for them is that they have all of that plugged in and working "out of the box". That's a big win  if you're working with inexperienced students, and have a limited time - they don't have to plug anything in except the USB cable! The flip side is that they're pretty expensive, and while they are expandable, hooking up extra hardware is going to be more tricky. As such they're probably great for teaching one off workshops, but for longer term project based teaching, a regular Arduino is probably better...

Anyway - the reason I mention it is that at they explained how at a previous workshop students had been measuring temperature, and logging it/plotting graphs, but then for extra fun they'd gone to the foyer of the building which was 4 stories high, and used a helium balloon to lift one of the boards up, and measure how the temperature changed with height - being indoors, it was (as they predicted) hotter near the top of the building.

I thought that was a fun thing to do, but immediately wanted to take things further - if we want to record the change of temperature in any meaningful way, then we need to know the height. There are two ways to do that - measure the string on the balloon (accurate but slow, and hard to record along with the temperature data), or add an altimeter to the arduino...

It turns out that it's pretty easy to figure out your altitude, within certain limitations. All we need is a BMP180, which will cost you about £1 from our favourite online car boot sale and flea market website. These can measure atmospheric pressure with very high relative accuracy, so they can measure changes in pressure accurate to about 5 Pascals  (1Atmosphere is about 100,000 pascals so that's pretty good percentage error!). At sea level the air pressure drops about 10 Pascals per meter, so if we know the change in pressure we can figure out the change in height. As luck would have it they also include a thermometer so if you want to repeat the Engduino balloon experiment you're good to go.

Hooking up i2c

Hooking up the BMP180 is actually really easy, but can be a bit daunting of you're new to dealing with hardware. It uses i2c (pronounced eye-squared-see) which is simply a way for different electronic components to talk to each other - think of it as USB for chips. The great thing about it is that it makes hooking up lots of hardware really easy once you know what you're doing, but it can be confusing at first.

I2C uses 4 wires. Two of them are power and ground, and the other two  are Data (usually labeled SDA), and Clock (labeled SCL). The clever thing is that you can connect up as many devices as you are likely to need all sharing the same two signal pins (data and clock), which is why I2C is sometimes referred to as TWI (two wire interface). It's able to do this sharing, as most of the devices disconnect themselves from the wires when they're not communicating. The arduino sends a message out to a specific device, then that device turns on, and sends a message back. This is all handled automatically, so you don't really have to worry about it.

What you do need to worry about are voltages, and what happens when all the devices are turned off. This is where it gets confusing, as there are at least 3 ways of doing things - in increasing order of correctness (and complexity). The problem is that the BMP180 is a 3v device, while are Arduino is 5V. Normally we shouldn't mix these, but it turns out that with I2C its OK (ish).

Option 1: wing it hope for the best

BMP Vin to 3V (THIS IS IMPORTANT)
BMP SDA to Uno  A4 (check spec for different Arduino)
BMP SCL to Uno A5 (check spec for different Arduino)
BMP Gnd to Gnd

If we were using a 5V device this would probably be fine (though not ideal). For 3V devices this will work. I've done it. Nothing bad happens. The worst that could happen is that you damage a £1 chip, but you won't. Even then it probably won't blow up or anything. It just might stop working in a few years time. There are a lot of blogs discussion this, and my summary of them would be this is technically incorrect (don't to it in production hardware or assessed projects), but it'll probably work, and no one has actually seen anything bad happen from doing this.

Option 2: cheap and cheerful

Wire as before but add a 4K7 (or 10K) resistor from SDA to 3V, and SCL to 3V. Because of the way I2C works, it gets its positive voltage from these resistors, so the Arduino never sends 5V to our BMP  - instead it just gets the 3V its expecting. As an added bonus 3V is enough (usually) for 5V devices too, so you can connect 5V I2C devices, and 3V I2C devices like this. This is my preferred solution.


I've got a simple breakout board I made from strip board that connects to the sensor shield. The sensor shield breaks out I2C on a 4 pin dupont as 0v,5V,SDL,SCL so soldering a set of 4pin headers makes it easy to connect any number of devices. The three sets of header pins on the left are connected directly for 5V devices like the i2c LCD display. In the centre of the board I break the 5V line, and replace the power with 3V. There are also two 10K pull-ups to 3.3V. These are jumpered just in case I want to remove them. Using this board I can easily connect multiple I2C devices just by plugging them in. About 60% of I2C devices have a pinout which matches this layout, but in the case of the BMP180 I've cut a dupont cable, and resoldered it back together with a couple of twists in so that the pin out matches my board.

Option 3: Do it properly

Option 2 should work fine, and apparently (according to what I've read) is technically within specification. However for about £1 you can buy an i2c level shifter board. These have a chip on them which does all of this stuff properly. If you're building something that's "important", that's going to be sold, or run for a long period of time you should look into these, but today we don't need them.

Lets get on with it

So you've hooked up your BMP180. Now lets code it:
make i2c device
make atmosphere device
make pressure number
make temperature number

when start
.forever
..tell atmosphere to "read"
..say join "Temperature:" [ temperature ]
..say join "Pressure:" [ pressure*0.01 ]
..wait 1 secs

We start by creating an i2c device. We don't use it directly but its needed by the atmosphere device (and all other i2c devices). Then we make an atmosphere device, and the two variables pressure and temperature that its going to use.

To take a reading we just tell the sensor to "read", and it sets the temperature value in Centigrade and the pressure in Pascals (Pa). For historical reasons atmospheric pressure is usually reported in hectoPascals (=100Pa) so we multiply by 0.01 before displaying, and we expect to get a value of around 1000hPa (it could be worse - some backward countries still record pressure in inches!).

Now to figure out the change in pressure, we just record it when we start up, and then we can calculate the difference whenever we need to:

make seaLevelPressure number
make seaLevelTemperature number

when start
.tell atmosphere to "read"
.set seaLevelPressure to pressure
.set seaLevelTemperature to temperature+273.15
.
.forever
..tell atmosphere to "read"
..say join "Temperature:" [ temperature ]
..say join "Pressure:" [ pressure*0.01 ]
..say join "Delta:" [ (pressure-seaLevelPressure)*0.01 ]
..wait 1 secs

I've called the reference pressure sea level, as that's the standard reference point - we assume we start out on the ground, at sea level, though in practise it doesn't really matter. It's just a point to call zero.

So knowing the change in pressure whats the change in altitude? In an ideal world, given enough time and an army of minions class of school children. I might ask them to measure the height of one step on a flight of stairs, count the steps, and calculate the height of each floor in a building, while they measure the pressure at each height. Then get them to come back, and plot a graph, and figure out a linear approximation...

Then I'd google it, and get this equation:


There are a whole bunch of other equations out there, and they should all work fine (and for short distances, linear should be close enough). There are a lot of assumptions about humidity and the way temperature and air density change with height built into this equation, but it doesn't have to be rocket science (unless you're building a rocket - which would be an ideal application for this! in that case it actually is rocket science). Just adding this in should get you accurate to a meter or so, over small height ranges, and if you're changing altitude by more than 100m then you can probably live with less accuracy.

So lets put that together:

make i2c device
make atmosphere device
make pressure number
make temperature number

make altitude number
make seaLevelPressure number
make seaLevelTemperature number

make lcdi2c device
make message string

when start
.tell atmosphere to "read"
.set seaLevelPressure to pressure
.set seaLevelTemperature to temperature+273.15
.
.forever
..tell atmosphere to "read"
..broadcast calculateAltitude and wait
..set message to join "Temperature:" [ temperature ]
..tell lcdi2c to "show"
..set message to join "Pressure:" [ pressure*0.01 ]
..tell lcdi2c to "show"
..set message to join "Delta:" [ (pressure-seaLevelPressure)*0.01 ]
..tell lcdi2c to "show"
..set message to join "Altitude:" [ altitude ]
..tell lcdi2c to "show"
..wait 1 secs



when calculateAltitude
.set altitude to seaLevelPressure/pressure
.set altitude to ln of altitude
.set altitude to altitude * 0.1903
.set altitude to e^ of altitude
.set altitude to seaLevelTemperature * (altitude-1)
.set altitude to altitude/0.0065

As the whole point is to measure changes in height we're going to need to move around, so I've added an LCD device. More specifically I've added an i2C lcd. Since we've already figured out i2c, adding a second i2c device is just a matter of plugging in the 4 wires and off we go.

And by off we go I mean we actually have to get up and walk around now! Plug the Arduino into a battery and its time to go collect some measurements.

 We start out on the ground floor of the Sniff Mansion, and record a baseline pressure. It's a pleasant 21 degrees. There's a change in altitude recorded (17cm) as the sensor only has limited accuracy. We're also recording very small changes in pressure, so over time the pressure will change anyway.
 Moving down to the dungeon (currently closed for refurbishment) we note that not only is it cooler, but that the air pressure has increased by 26Pa. At 10Pa per meter that means we're about 2.5m underground, and the more complex equation confirms this.
 Moving back to the first floor we notice that again we're in a cooler area (the original recording was by the south facing windows, which does get warm). However pressure has conveniently dropped by 38Pa suggesting we're 3.5m higher - the equation gives 3.24m.
There are 15 steps to the first floor of about 21cm or about 3.15m so that's pretty good!
Sniff Labs are in the South tower, where we note its a little hotter again. Preasure has dropped by 72Pa, which is  calculated as 6.15m.

That's 2.9m higher than the first floor, and there are again 15 steps up to the tower. However they're slightly shallower at only 19cm, making  an estimated 2.85m.

That's pretty close! Measureing a step height is probably accurate to about 5% (nearest cm), while both of the results agreed to within 10cm over 3m!


In practise that may be a little optimistic - leaving the arduino sat on my desk, it drifts by about 50cm. However its seems pretty reliable to within 1m. You could improve the accuracy by averaging the samples, but for now the accuracy we've seen is pretty good!

All of this code should run un-changed on a Raspberry Pi if you're so inclined. However the Pi is a bit more tricky to wire up, harder to run off batteries, and much more expensive so an Arduino is a better choice.

This was a really fun experiment to do, and includes lots of good lessons: aside from understanding air pressure, we have to get to grips with the idea of accuracy and precision. We can cross reference different ways of measuring to provide confidence in the two methods, and we can choose an appropriate approximation. All that before we even start to use what we've built. now we can measure altitude we can measure the heights of things for all sorts of cool experiments...

No comments:

Post a comment