Parallax's New Altimeter/Barometer Module
Phil Pilgrim (PhiPi)
Posts: 23,514
For the past couple weeks I've been privileged to work with Parallax's Kevin Cook on the Propeller software for Parallax's new altimeter/barometer module (#29124). Ken suggested that I share my experiences with this module and the object I wrote for it here. So I shall try to describe Parallax's module and what I did with the software (and why) in the paragraphs that follow.
Here's a sneak peek at the module itself:
It is based on the Measurement Specialties MS5607 temperature and pressure sensor chip (the little white thing with the two holes in it). Basically, the chip provides raw temperature and pressure data through either an SPI or I2C interface. It also provides chip-specific factory calibration data from an internal PROM in order to facilitate the computation of accurate temperatures and temperature-compensated pressures. Beyond that, it's the programmer's responsibility to convert the raw data into calibrated temperature and pressure readings and, from there, to usable altimeter and barometer data.
When presented with this task, my first question was, "If I were using this module, what services would I expect from a Propeller object that supports it?" In order to answer this question, I needed to acquaint myself with basic barometry and altimetry.
The Basics
The sensor in this module measures temperature and absolute pressure, and outputs data in digital form from its built-in 24-bit ADC. Due to process variations, the gain and offset of these readings will vary from part to part. These variances are measured at the factory, and correction factors are programmed into each sensor's PROM, which the user must apply to obtain correct readings. Also, because pressure increases with temperature, the temperature reading has to be used to adjust the pressure reading to obtain a temperature-compensated pressure value. These calculations can all be accomplished using multiplies and shifts in 64-bit integer math -- something that, with some effort, can be accommodated by Spin. So far, so good.
The pressure measured by the sensor in free air is atmospheric pressure, which varies with changes in the weather and with changes in altitude. Pressure decreases with altitude, so a pressure sensor at sea level will indicate a higher value than the same sensor at, say, 1000 feet. When you hear a weather report that includes a barometer reading in millibars or inches of mercury, the reading is always corrected to sea level. In other words, the local pressure in Denver, say, at a one-mile elevation, will be adjusted to reflect what the pressure would be if measured at the bottom of a one-mile-deep hole.
So how does pressure vary with altitude? Or, more usefully, if you know the local air pressure, how do you compute your altitude from that? The following formula yields altitude in terms of pressure for the troposphere (i.e. from below sea level to 11 km):
where Altitude is in meters. P is the local air pressure, and P0 is the pressure at sea level, both expressed in the same units, whether millibars, Pascals, or inches of mercury. When P0 is set equal to the standard sea level pressure of 1013.25 mb, the altitude yielded by the formula is called the "pressure altitude." The actual altitude may be computed by substituting the local air pressure, corrected to sea level, for P0.
Implementation
Clearly, the above is an unfriendly formula for the Propeller's native integer math. My first reaction was that I would have to use the Float32 object to pull this off. But that comes at a cost of one more cog and additional heft, memory-wise. The other option was to pre-compute the function at equally-spaced points to produce a table and use interpolation to get the intervening values. Unfortunately, for any table of a reasonable size, linear interpolation does not produce adequately-accurate results. So I reached back to some derivations I did about thirty years ago for CNC milling smooth curves in molds for fishing lures via cubic interpolation.
Whereas linear interpolation involves just the two tabular values on either side of the independent variable and the straight line between them, cubic interpolation takes advantage of four tabular values (two to the left and two to the right) and the third-order polynomial that intersects all of them. In this way the actual curve's convexities and concavities can be approximated more closely. The basic formula is this:
where y(x) is the interpolated value at a = (x - xi) / (xi+1 - xi), assuming the y's in the table are all calculated at equally-spaced x values; and i is chosen such that x is between xi and xi+1.
This formula can be computed with integer math. By spacing the x values in the table at power-of-two multiples of the real x values, i can be computed by shifting x to the right. a, then, is just the least-significant bits that got shifted out.
The table was computed in Perl using standard pressure in hundredths of a millibar (i.e. 101325) for P0 to produce altitude values in whole centimeters (i.e. hundredths of a meter). This was done using 65 equally-spaced values for P (also in hundredths of a millibar), yielding an altitude range of -2086 meters to +23230 meters.
In order to use the table with P0 values other than standard sea-level pressure, the P value must first be corrected, as follows:
Then the interpolation can proceed as described above.
Use as a Barometer
To use the module as a barometer to obtain the local air pressure, one need only read the data and apply the calibration and temperature corrections to obtain a value in hundredths of a millibar. However, to get the air pressure corrected to sea level (as a real weather station would do), you have to know your altitude (i.e. elevation if on land). The question then becomes, "Given the local pressure reading, at what sea-level pressure does the altitude formula produce my current altitude?" Rather than producing yet another table that inverts the altitude formula, I decided to use a binary search, with the presumptive sea-level pressure being the independent variable whose value we want to zero in on. This method turned out to yield an accurate result very quickly.
Use as an Altimeter
To use the module as an altimeter, you have to know both the local pressure (which the sensor gives you) and the corrected sea-level pressure for your location. The latter can be a bit tricky to obtain. For one thing, there may not be a weather station at your x,y coordinates to provide this information. For another, the sea-level pressure can change, due to changing weather conditions. One solution is to start from a known altitude and compute the sea-level pressure as described above for a barometer, then use this value with the changing local pressure to compute your current altitude. Of course, if the weather changes during your travel, the altitude readings become inaccurate again. This is why pilots, when approaching an airport, will radio ahead for its sea-level-corrected barometric reading, so they can adjust their altimeters accordingly, using the knob and little window circled here:
The Spin object provides the same facilities: you can adjust the sea-level pressure either directly by providing the correct value, or indirectly by providing your current altitude and local air pressure.
Foreground and Background Operation
The MS5607 datasheet suggests averaging multiple temperature and pressure readings to get a stable value for each. Depending on the chosen resolution -- and there are five to choose from -- acquiring and averaging a bunch (33 in this case) of readings can take a bit of time. For this reason, I decided to include an optional background mode, wherein a separate cog is started that continuously reads the sensor, performs the temperature and pressure corrections, and averages the last 33 readings. As a bonus, it also keeps a running tab of the median value of the last 33 readings, in case it's preferred over the mean (average). The median can be handy for eliminating spurious readings when, for example, the device is in your car and you slam the door, causing an instantaneous increment in air pressure. With the median, that errant value is simply shunted to one end or the other of the range of values and ignored. With the mean, the errant value would be averaged into the result, skewing it a bit.
In order to ensure that all values requested at a certain time come from the same batch of 33 readings, I've included pause and resume methods for the background process. These can also be used when the sensor shares its I2C port with other devices, in order to avoid conflicts when those devices are accessed.
Documentation
The object was created with Gold Standard autodoc tags and comments. Here's a link to the autodoc file:
Test Results
I'm not going to release the actual object just yet, since it requires more testing first. However, my confidence has been bolstered by the results I've seen so far. I borrowed a Garmin GPS that has a built-in barometric altimeter from a friend of mine to obtain comparative readings between it and the module. I attached the module to a Proto-DB, plugged into a battery-powered Spinneret, which was programmed to record altitude values on a micro-SD card via Kye's file driver object. Along with each module value was also recorded the altitude value from the Garmin (which was running with its GPS disabled). At the outset of each test, both the Garmin GPS and the Parallax module were initialized to the same known altitude. Here are two graphs which plot the Garmin and Parallax altitude readings superimposed on each other:
Readings taken while driving around town.
Readings taken between Quilcene, WA, and the summit of Mt. Walker and back.
So far, I'm pretty happy with the results, and I think the Parallax barometer/altimeter module is going to be an excellent product!
-Phil
Here's a sneak peek at the module itself:
It is based on the Measurement Specialties MS5607 temperature and pressure sensor chip (the little white thing with the two holes in it). Basically, the chip provides raw temperature and pressure data through either an SPI or I2C interface. It also provides chip-specific factory calibration data from an internal PROM in order to facilitate the computation of accurate temperatures and temperature-compensated pressures. Beyond that, it's the programmer's responsibility to convert the raw data into calibrated temperature and pressure readings and, from there, to usable altimeter and barometer data.
When presented with this task, my first question was, "If I were using this module, what services would I expect from a Propeller object that supports it?" In order to answer this question, I needed to acquaint myself with basic barometry and altimetry.
The Basics
The sensor in this module measures temperature and absolute pressure, and outputs data in digital form from its built-in 24-bit ADC. Due to process variations, the gain and offset of these readings will vary from part to part. These variances are measured at the factory, and correction factors are programmed into each sensor's PROM, which the user must apply to obtain correct readings. Also, because pressure increases with temperature, the temperature reading has to be used to adjust the pressure reading to obtain a temperature-compensated pressure value. These calculations can all be accomplished using multiplies and shifts in 64-bit integer math -- something that, with some effort, can be accommodated by Spin. So far, so good.
The pressure measured by the sensor in free air is atmospheric pressure, which varies with changes in the weather and with changes in altitude. Pressure decreases with altitude, so a pressure sensor at sea level will indicate a higher value than the same sensor at, say, 1000 feet. When you hear a weather report that includes a barometer reading in millibars or inches of mercury, the reading is always corrected to sea level. In other words, the local pressure in Denver, say, at a one-mile elevation, will be adjusted to reflect what the pressure would be if measured at the bottom of a one-mile-deep hole.
So how does pressure vary with altitude? Or, more usefully, if you know the local air pressure, how do you compute your altitude from that? The following formula yields altitude in terms of pressure for the troposphere (i.e. from below sea level to 11 km):
where Altitude is in meters. P is the local air pressure, and P0 is the pressure at sea level, both expressed in the same units, whether millibars, Pascals, or inches of mercury. When P0 is set equal to the standard sea level pressure of 1013.25 mb, the altitude yielded by the formula is called the "pressure altitude." The actual altitude may be computed by substituting the local air pressure, corrected to sea level, for P0.
Implementation
Clearly, the above is an unfriendly formula for the Propeller's native integer math. My first reaction was that I would have to use the Float32 object to pull this off. But that comes at a cost of one more cog and additional heft, memory-wise. The other option was to pre-compute the function at equally-spaced points to produce a table and use interpolation to get the intervening values. Unfortunately, for any table of a reasonable size, linear interpolation does not produce adequately-accurate results. So I reached back to some derivations I did about thirty years ago for CNC milling smooth curves in molds for fishing lures via cubic interpolation.
Whereas linear interpolation involves just the two tabular values on either side of the independent variable and the straight line between them, cubic interpolation takes advantage of four tabular values (two to the left and two to the right) and the third-order polynomial that intersects all of them. In this way the actual curve's convexities and concavities can be approximated more closely. The basic formula is this:
y(x) = yi-1(-a3/2 + a2 - a/2) + yi(3a3/2 - 5a2/ 2 + 1) + yi+1(-3a3/2 + 2a2 + a/2) + yi+2(a3/2 - a2/ 2)
where y(x) is the interpolated value at a = (x - xi) / (xi+1 - xi), assuming the y's in the table are all calculated at equally-spaced x values; and i is chosen such that x is between xi and xi+1.
This formula can be computed with integer math. By spacing the x values in the table at power-of-two multiples of the real x values, i can be computed by shifting x to the right. a, then, is just the least-significant bits that got shifted out.
The table was computed in Perl using standard pressure in hundredths of a millibar (i.e. 101325) for P0 to produce altitude values in whole centimeters (i.e. hundredths of a meter). This was done using 65 equally-spaced values for P (also in hundredths of a millibar), yielding an altitude range of -2086 meters to +23230 meters.
In order to use the table with P0 values other than standard sea-level pressure, the P value must first be corrected, as follows:
P := P * P0(standard) / P0(actual)
Then the interpolation can proceed as described above.
Use as a Barometer
To use the module as a barometer to obtain the local air pressure, one need only read the data and apply the calibration and temperature corrections to obtain a value in hundredths of a millibar. However, to get the air pressure corrected to sea level (as a real weather station would do), you have to know your altitude (i.e. elevation if on land). The question then becomes, "Given the local pressure reading, at what sea-level pressure does the altitude formula produce my current altitude?" Rather than producing yet another table that inverts the altitude formula, I decided to use a binary search, with the presumptive sea-level pressure being the independent variable whose value we want to zero in on. This method turned out to yield an accurate result very quickly.
Use as an Altimeter
To use the module as an altimeter, you have to know both the local pressure (which the sensor gives you) and the corrected sea-level pressure for your location. The latter can be a bit tricky to obtain. For one thing, there may not be a weather station at your x,y coordinates to provide this information. For another, the sea-level pressure can change, due to changing weather conditions. One solution is to start from a known altitude and compute the sea-level pressure as described above for a barometer, then use this value with the changing local pressure to compute your current altitude. Of course, if the weather changes during your travel, the altitude readings become inaccurate again. This is why pilots, when approaching an airport, will radio ahead for its sea-level-corrected barometric reading, so they can adjust their altimeters accordingly, using the knob and little window circled here:
The Spin object provides the same facilities: you can adjust the sea-level pressure either directly by providing the correct value, or indirectly by providing your current altitude and local air pressure.
Foreground and Background Operation
The MS5607 datasheet suggests averaging multiple temperature and pressure readings to get a stable value for each. Depending on the chosen resolution -- and there are five to choose from -- acquiring and averaging a bunch (33 in this case) of readings can take a bit of time. For this reason, I decided to include an optional background mode, wherein a separate cog is started that continuously reads the sensor, performs the temperature and pressure corrections, and averages the last 33 readings. As a bonus, it also keeps a running tab of the median value of the last 33 readings, in case it's preferred over the mean (average). The median can be handy for eliminating spurious readings when, for example, the device is in your car and you slam the door, causing an instantaneous increment in air pressure. With the median, that errant value is simply shunted to one end or the other of the range of values and ignored. With the mean, the errant value would be averaged into the result, skewing it a bit.
In order to ensure that all values requested at a certain time come from the same batch of 33 readings, I've included pause and resume methods for the background process. These can also be used when the sensor shares its I2C port with other devices, in order to avoid conflicts when those devices are accessed.
Documentation
The object was created with Gold Standard autodoc tags and comments. Here's a link to the autodoc file:
Test Results
I'm not going to release the actual object just yet, since it requires more testing first. However, my confidence has been bolstered by the results I've seen so far. I borrowed a Garmin GPS that has a built-in barometric altimeter from a friend of mine to obtain comparative readings between it and the module. I attached the module to a Proto-DB, plugged into a battery-powered Spinneret, which was programmed to record altitude values on a micro-SD card via Kye's file driver object. Along with each module value was also recorded the altitude value from the Garmin (which was running with its GPS disabled). At the outset of each test, both the Garmin GPS and the Parallax module were initialized to the same known altitude. Here are two graphs which plot the Garmin and Parallax altitude readings superimposed on each other:
Readings taken while driving around town.
Readings taken between Quilcene, WA, and the summit of Mt. Walker and back.
So far, I'm pretty happy with the results, and I think the Parallax barometer/altimeter module is going to be an excellent product!
-Phil
Comments
This is very interesting and very cool. Thanks for all this information. I look forward to seeing the object.
I foresee altimeters in my RC airplanes and helicopter in the near future.
With the right tubing and enclosure I could use a second sensor as an airspeed indicator.
Duane
A most excellent pre document report, (as always). Looking forward to the product, makes the WiFi EOL seem a little bit less of a blow
I've used previous versions of the MSI (formerly Intersema) sensors, MS5534 and MS5540, and this new one is similar in terms of the parsing and calculations. The basics of the interface take quite a bit of code space, and I've put all of those basics in one slot of a BS2p_, kind of like a separate spin object if not cog, and then the application gets the data or increments the accumulators/filters with a cross-slot call. I'm not sure how I'll handle it with a vanilla BS2. I have a couple of demos written that show data in tabular or ascii graphic format, like a variometer. An accurate full-range altimeter may be out of the question. Linearization around a datum altitude/pressure is the key for most practical applications anyway, I believe.
This harks back to Ken's request that led to this product offering... Pressure-sensors-as-altimeters-seeking-your-important-customer-input!
-Phil
I bet the rocket and balloon peeps are gonna love this thing.
I took this module up to Wrights Lake (7000 feet) over the weekend. I started it off at home (120 feet) and monitored the modules output the entire 3.5 hour trip. As we made our way up the mountain, I watched out for every altitude marker on the side of the road. All in all the accuracy was amazing! By the time I was at my destination I was reading 6898 feet, this was a little over 70 off what it should be reading. Some may think thats a problem, but this was over 3.5 hours and a noticeable weather difference (clouds at the mountain and clear sky at home). I made no adjustments or corrections during that time to compensate for pressure changes due to weather conditions. I made the same test on the way home and results where nearly the same, with only about 50 off what I expected.
Also, something to mention here, during my trip the sunlight would occasionally directly hit the sensor and when it did, it would cause an altitude shift of about 200, something I will note in the documentation. Once the sun was off the sensor it was back to normal operation.
-Phil
In case you happened to use Garmin Rino 530HCX when doing the comparison - here's a picture of what's inside.
Their pressure sensor looks quite similar to SCP1000
We rocket people are paying close attention.
-Phil
-Phil
Looks QuadCopter ready
Jim
Just to know how I can use the Altimeter/Barometer Module with the
Basic Stamp 2P.
Thanks, Mark
Yes, you can read this sensor with the BS2p. I've been working up code for that, a work in progress. I had to put it aside for a while, but I'm attaching two BS2 programs that could get you started. Always, the first question is, "can I read the values from the chip?" These go through the basics of resetting the chip, reading out the calibration parameters and the raw data. This chip can operate in either I2C mode or in SPI mode, so there is one program for each. The SPI program will run on any stamp using the SHIFTIN and SHIFTOUT commands, while the I2C program will run only on '2p series, which support the I2CIN and I2COUT commands.
If you use the SPI version, it is essential to include a 4.7K ohm pulldown resistor from the sdo pin to ground. (It took me a while to figure that out!) For I2C mode, the pullup resistors on sda and scl are built into the module.
Now, these programs go only so far as to extract the raw data from the chip. I takes some math to convert to real pressure and temperature units, and it's pretty knarly math for a BASIC Stamp. Not in principal, however the way they have it parsed in the data sheet it involves multiple precision signed integers, up to 41 bits. I've been cogitating about other ways to arrange it and will post more code in a followup message. Once we have the pressure, then it's on to altitude, which I've done before with the MS5534 an earlier pressure/altimeter sensor from MSI. I am sure it can be effective with a BS2p series Stamp, but I'm not so sure about the original BS2 where the ram is more limiting.
-Phil
Graham
I picked up one of the Altimeter modules from Radio Shack and started playing with it today. I'm using your 29124_Altimeter_Demo straight out of the .zip file. I'm not seeing the results expected and I'm not sure if it's me or the module at this point.
According to my iPhone, the GPS altitude of my desk is 1190ft (+/-6ft).
The local barometric pressure is about 29.55
The desktop temperature where the Quickstart and altimeter module are has stabilized at about 79.8F
If I enter i2955 to the demo program, I get this: If I enter f119000 to the program, I get this:
The temperature and local pressure aren't at all what I was expecting to see. That air pressure put me at about 19,000 ft in altitude...not bad for Ohio!
Any thoughts? Misunderstanding the demo? Did I miss something in the code I was supposed to set? bad module? Calibration issues?
Thanks for your help!
I've spent a lot of time in Ohio, so I know that 19,000 ft is a "bit" off. Take a look at the little white sensor chip on the module, and tell me what the part number is (first four digits). There are two possible parts for which demo code was written, and it looks from your results as if your sensor and demo code do not match.
-Phil
Okay, I see the problem. The demo is set up for the 5611 chip, and it needs to be for the 5607 chip. You can modify the code as follows (assuming you're using it with the QuickStart board):
-Phil
<Insert Wilder/Feldman Young Frankenstein dialog here>
It would have helped if I had read your excellently documented code BEFORE calling 9-1-1, too.
Thank you.
Thanks for calling it to my attention!
-Phil
I'm using this Altimeter with a Basic Stamp 2 and board of education USB. (SPI)
This is the raw output I'm getting, is this correct?
Does anyone have the math code to convert the pressure and temperature to usable numbers? The pressure is the one I need for my project.
Any help will be greatly appreciated.
Thank you,
Andy
I did get a chance to work on the math required to take the next step, and I'll will post what I came up with shortly. As you can see from the data sheet or from Phil's spin code, the raw readings and the intermediate results involve very large numbers. It has been a challenge to squeeze it into the limited RAM and 16 bit integers of the BASIC Stamp and PBASIC .
The demo program merely displays on the debug screen current values of the temperature and pressure as they are reported by the module, crunched to units of degrees Celsius and millibar. Functions of barometer (with altitude correction) and altimeter are yet to come, but this program has the essentials to get you started on your own. In the vanilla BASIC Stamp there is not much RAM memory left to work with, but some things can be done, we'll see. The situation is much better if you have more RAM available, such as the scratchpad RAM on the multi-slot Stamps, or external RAM in something like a real time clock chip.
Please report bugs or issues you have with the programs, or success too, of course.
Edit: Updated programs, now include code for 2nd order temperature compensation at low (< 20 °C) temperatures.
Edit: New photo, eliminated kluge pulldown resistor on sdo, see discussion in followup post below.
My temp is reading 20 which matches my wall unit.
My pressure seems off though. It's reading 655.36 and giving a "overpressure error!"