Digital Altimeter
by Jaden Singh
by Jaden Singh
Corner view of the altimeter with power off, battery port visible. Small paper clip for scale.
This project implements a basic barometric altimeter, which uses ambient air pressure to determine altitude. There are two user inputs: an on/off switch on the side and the front dial. Since pressure varies with weather, the dial can be used to fine-tune the altimeter to account for high or low pressure weather systems. Alternatively, in remote settings, known discrepancies in the altitude reading can be used to anticipate good or bad weather (if you have another means of determining your correct altitude).
Close look at how the potentiometer, which implements the pressure dial, is mounted within the enclosure.
View of altimeter as a handheld device. Display is on.
The altitude reading is 921 feet given a sea level pressure of 1005 hPA (this measurement was taken in the basement of Hunt Library).
Top-down view of the altimeter turned on and properly adjusted to local pressure at sea level. 'Readout' and 'adjust baseline hPA' labels are visible.
Top-down view of the altimeter turned on and instead adjusted to the minimum allowed sea level pressure of 1000 hPa. Note the elevation reading is now 784 feet, which is not an accurate reading on campus at Carnegie Mellon
Side view of the 'Dinwoody Glacier Research' label. Dinwoody Glacier is a location that is special to me personally, and I hope one day to use a self-constructed altimeter (perhaps this one!) on the glacier and the peaks surrounding it. 'Dinwoody Glacier Research' is also a play on a accessory brand in Wyoming, 'Teton Gravity Research', that me and a few friends like to joke about.
In this video, I demonstrate turning on the device and using the pressure dial to calibrate the altitude reading. The dial sets a hypothetical 'sea level pressure' at your location, which informs the altitude reading that the pressure sensor gives the display. This 'sea level pressure' accounts for pressure variations at your actual altitude based on weather.
The refresh rate of the display device display made the display appear unintelligible at every available fps I tried to shoot at, so I've included a range of still photos below that demonstrate how the altitude reading changes as the dial is turned. Measurements were taken in the basement of Hunt Library.
At the max setting of 1024 hPA at sea level, the altimeter reads 1435 feet
At a setting of 1017 hPA, the altitude reading is 1247 ft.
At a setting of 1007 hPA, the altitude reading is 975 (quite close to an accurate reading at my location).
At the minimum setting of 1000 hPA at sea level, the altimeter reads 784 ft.
The initial, untrimmed circuitry. At this point I was still trying to include the GPS, which can be seen hanging off the white breadboard at the top left of the image. Ultimately, the Arduino Micro couldn't support the libraries of both the GPS, the OLED display, and the pressure sensor.
The naked enclosure. In this picture, the enclosure is constructed inside-out. I only discovered this after gluing the OLED display to the loose front piece (at the top of this image).
The OLED display glued to fit to the laser-cut whole in the front display piece.
The inside of the enclosure and the popsicle stick used to mount the potentiometer. It is crowded, but only needing three real electronic devices allowed the small space to ultimately work out. The Arduino Pro Micro is partially visible underneath the popsicle stick.
A look at a configuration I scrapped. Here, all the electronics are mounted via glue and tape directly to the front display piece in order to support the potentiometer, which you cannot see here. This setup was not even close to fitting inside the enclosure size I wanted.
The first information set I thought about sending to the OLED display, using only data from the pressure sensor. I later decided that having data from the last few seconds display next to the current reading was sort of useless, unless you are skydiving. You can't easily descend or ascend quickly enough for that to be helpful.
The major roadblock turned out to be memory related. I originally wanted the altimeter to also output a GPS reading, which would complement the altitude reading derived purely from air pressure. What I discovered was that the libraries for the OLED display and for the pressure sensor (strangely) took up almost too much memory just on their own. So I cut the GPS sensor in favor of the more classic, somewhat less accurate pressure-to-altitude process that the pressure sensor uses.
A smaller issue was that I glued on the pressure sensor to the wrong side of the front display panel. The fix was to reassemble the enclosure inside-out. I had already glued together the other panels in the correct configuration, but I was able to pry a few of them back apart to use in the inside-out version. I also took the opportunity to re-cut a few panels with more interesting designs. Originally, they were all plain.
The project satisfied my realistic short-term goals, but there are certainly improvements I would like to continue making. My ideal altimeter is small, resilient, accurate, and has a long battery life. The altimeter I made for this project is reasonably small, not very resilient, fairly accurate, and likely doesn't have a great battery life. So those are the areas I'd work on going forward.
I think the external presentation of the device came out well, especially for having no previous experience with CAD/laser cutting. It also was not a very ambitious design. Swapping out electronics (I wonder if I could implement an altimeter using just the small ATMEGA controller) and being more aggressive with soldering could definitely reduce the size and produce a sleeker product. With a lot of time I might be able to get everything down into a chunky wristwatch.
One comment I received from the written critique was that a 'bigger screen would be beneficial if possible', and with the device sized the way it is now, I definitely agree. I'd probably look around the internet for an alternative to the LED display I used, because the bigger ones in the Phys Comp room can't display the same amount of information. At the moment you need to squint a bit to make out what my display is saying. Another piece of feedback was that 'another [dial] where I could change the weather and have the device give me the pressure would’ve been more intuitive.' This was a common theme in the verbal critique. For a general use product I agree that this might be better, but for my personal use I'd stick with the simple sea level adjustment dial. I want to have to make my own judgements based on the raw data the device gives me.
Which parts of the process went well, and which parts did not? I expected the laser cutting to be the trickiest part. Instead, I got hung up on some weird interactions between hardware and software. I wanted a GPS sensor, which overloaded the memory on the Arduino Micro. I wasted a lot of time trying to see if I could slim down libraries so that I could fit the GPS sensor into the project. I fried a component when hooking up 9V to the wrong power input. On the other hand, the laser cutting was smooth and quite fun!
At the end of the day, the device works and I like how it looks, so I can't complain. Excited to get a chance to make some upgrades!
/***************************************************************************
Digital Altimeter by Jaden Singh
This program sets up the Adafruit BMP280 pressure sensor and the SSD1306 OLED I2C 128x64 display,
and then uses a potentiometer input to calibrate pressure sensor data sent to the display in a loop.
In the loop, which runs every 500ms, the display screen is reset and the bmp_printer() function is called.
bmp_printer() uses the existing settings to make a altitude reading based on the BMP pressure reading, and then
prints it to the display screen. Then the potentiometer input is taken, mapped to the correct values,
and is set to be used in the next loop.
Pin Mappings:
- The potentiometer is mapped to pin A0
- The display and pressure sensor use the I2C SDA and SCL pins (2 and 3, respectively, for the Arduino Pro Micro)
Citations:
This code is structured off of an example Adafruit sketch ('bmp280test') used to set up the BMP280 sensor.
Additional code is borrowed from the SSD1306 OLED I2C 128x64 base demo sketch.
***************************************************************************/
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SoftwareSerial.h>
#define POTPIN A0
#define LOGO_HEIGHT 16
#define LOGO_WIDTH 16
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
static const unsigned char PROGMEM logo_bmp[] = { 0b00000000, 0b11000000,
0b00000001, 0b11000000,
0b00000001, 0b11000000,
0b00000011, 0b11100000,
0b11110011, 0b11100000,
0b11111110, 0b11111000,
0b01111110, 0b11111111,
0b00110011, 0b10011111,
0b00011111, 0b11111100,
0b00001101, 0b01110000,
0b00011011, 0b10100000,
0b00111111, 0b11100000,
0b00111111, 0b11110000,
0b01111100, 0b11110000,
0b01110000, 0b01110000,
0b00000000, 0b00110000 };
#define BMP_SCK (13)
#define BMP_MISO (12)
#define BMP_MOSI (11)
#define BMP_CS (10)
Adafruit_BMP280 bmp; // I2C
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO: A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO: 2(SDA), 3(SCL), ...
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
float input_pressure = 1020;
int time_last_set;
int last_read = 0;
void setup() {
Serial.begin(115200);
//switch
pinMode(POTPIN, INPUT);
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 failed"));
for(;;); // Don't proceed, loop forever
}
time_last_set = 0;
input_pressure = 1023; //default
unsigned status;
status = bmp.begin();
if (!status) {
Serial.println(F("Could not find a valid BMP280 sensor"));
}
/* Default settings from datasheet. */
bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */
Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
Adafruit_BMP280::FILTER_X16, /* Filtering. */
Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
// the library initializes this with an Adafruit splash screen. It's cute.
display.display();
delay(2000); // Pause for 2 seconds
// Clear the buffer
display.clearDisplay();
}
uint32_t timer = millis();
void loop() {
delay(500); // gap measurements half a second
//Reset display
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1);
bmp_printer(); //Call function to do logic with potentiometer + pressure sensor, and print it
display.display();
}
//Print readings to displayand set new sea level pressure (if a new one is provided)
void bmp_printer() {
display.setTextColor(SSD1306_WHITE);
display.println("Altitude Log:");
//input_pressure is the variable that accounts for 'pressure at sea level'
display.print(bmp.readAltitude(input_pressure) * 3.28); //3.28 is to convert meters to feet.
display.println(" ft");
display.print("Pressure:");
display.println(bmp.readPressure());
display.println(" Pa");
int potread = analogRead(POTPIN);
if (abs(potread - last_read) > 30) { //>30 controls for minor shakes in the potentiometer that are not real input changes
time_last_set = millis();
last_read = potread;
}
input_pressure = map(last_read, 0, 1023, 1000, 1025); //maps potentiometer input to a reasonable sea level hPA range
display.print("Set sea level hpa of ");
display.print(input_pressure);
display.print(" hPa ");
}