Bosch の気圧センサ BMP280は、絶対圧(センサ単体で気圧が測れる)の測れるセンサで測定補正用の温度センサを内蔵しています。Seeed Studio の Grove Beginner Kit for Arduino (以下キット)のボード上に載っているので今回はそれを使って高度を測ってみました。 高度が上がると気圧が下がるのを利用します。同じ標高でも気圧は天候で変わるので絶対的な高さではなく基準からの高さを測ります。 キットには OLED のマトリクスディスプレイ (コントローラ SSD1306, 解像度128x64) が含まれているので表示にはそれを使います。
スケッチには Seeed Studio の Grove Barometer Sensor library BMP280 ライブラリ (BMP280用) と、 oliver さんの U8g2 ライブラリ (OLED 表示用)を使います。気圧から高度への変換は Seeed_BMP280 ライブラリの関数を使います。どちらのライブラリも Arduino IDE のライブラリマネージャからインストールできます。
U8g2 ライブラリには直線等の描画ができるライブラリと、文字表示のみのライブラリが含まれます。キットのマイコンが Arduino Uno 相当でメモリが不足するので文字表示のみのライブラリを使って、グラフ表示の部分は直接 SSD1306 へのコマンド等を書き込んで実現しています。
室内で床に置いた状態から胸の高さぐらいまで持ち上げても気圧がはっきり変化します。測定最小単位のばらつきは大きいので何らかのフィルタ処理は必要だと思いますが、持ち上げの検出などもできそうです。
次にだいたい 60m + α程度の標高差のあるところで使ってみました。 登り切ったところで基準から -7.70hPa で 64.34m 上昇になっているのでまずまずの精度なのだと思います。
完成したスケッチは以下の通りです。 数値の表示は1秒に1回更新、グラフは10秒毎に横1ドットずれます。 キット上のスイッチを押した時点での気圧を 0m とした高度を表示します。 bmp280.getPressure()の内部(ライブラリ内)で温度測定を実行しているので bmp280.getTemparature() なしでも気圧計算は狂いません。bmp280.calcAltitude() の引数は換算したい気圧です。
// // 気圧と温度が高度で変わることを確かめる // // Grove Beginner Kit for Arduino (Seeed Studio) を使う // // To run this sketch you need to install libraries below; // - Grove Barometer Sensor library BMP280 by Seeed Studio, and // - U8g2 library by oliver. // You can install these libraries through Arduino's library manager. // #include// Grove Barometer Sensor library BMP280 by Seeed Studio #include // U8g2 library by oliver const uint8_t OLED_ADDR = 0x3C; // Declarations BMP280 bmp280; U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE); // Global variables unsigned long prev = 0; unsigned long prev2 = 0; bool prevSW = LOW; int t = 0; uint8_t ymin = 63; uint8_t ymax = 0; float temp0 = .0; float pr0 = .0; float alt0 = .0; // Function declarations void disp2(const char *p, int val, uint8_t y); void dispACC_X(); void dispADC(const char *p, int pin); void dispValues(); void oledPutByte(uint8_t x, uint8_t yy, uint8_t c); inline void oledPutPixel(uint8_t x, uint8_t y) { oledPutByte(x, y / 8, 1 << (y % 8)); } void oledVLine(uint8_t x, uint8_t y1, uint8_t y2); void setup(void) { pinMode(4, OUTPUT); pinMode(13, OUTPUT); bmp280.init(); u8x8.setBusClock(100000); u8x8.begin(); u8x8.setFlipMode(1); // Comment out this line if you want to display upside down u8x8.setFont(u8x8_font_chroma48medium8_r); u8x8.clearDisplay(); } void loop(void) { // Show sensor values and/or a graph if (millis() - prev2 > 1000) { prev2 = millis(); disp(); } // Handle the switch if (digitalRead(6) == HIGH) { if (prevSW == LOW) { temp0 = bmp280.getTemperature(); pr0 = bmp280.getPressure(); alt0 = bmp280.calcAltitude(pr0); t = 0; ymin = 63; ymax = 0; u8x8.clearDisplay(); } prevSW = HIGH; digitalWrite(4, HIGH); digitalWrite(13, HIGH); tone(5, 1000); } else { prevSW = LOW; digitalWrite(4, LOW); digitalWrite(13, LOW); noTone(5); } delay(20); } void oledPutByte(uint8_t x, uint8_t yy, uint8_t c) { Wire.beginTransmission(OLED_ADDR); Wire.write(0b10000000); //control byte, Co bit = 1 (1byte only), D/C# = 0 (command) Wire.write(0xB0 | (yy & 0x7)); Wire.write(0b00000000); //control byte, Co bit = 0, D/C# = 0 (command) Wire.write(x & 0xf); //set lower column address Wire.write(0x10 | (x >> 4)); // set higher column address Wire.endTransmission(); Wire.beginTransmission(OLED_ADDR); Wire.write(0b01000000); //control byte, Co bit = 0 (continue), D/C# = 1 (data) Wire.write(c); //SSD1306のGDRAM にデータ書き込み Wire.endTransmission(); } void oledVLine(uint8_t x, uint8_t y1, uint8_t y2) { if (y1 >= y2) { uint8_t tmp = y1; y1 = y2; y2 = tmp; } uint8_t buf[8] = {0}; for (int i = y1; i <= y2; i ++) { buf[i / 8] |= (1 << (i % 8)); } for (int i = 0; i < 8; i ++) { if (buf[i] != 0) { oledPutByte(x, i, buf[i]); } } } void disp2(uint8_t y) { ymin = min(y, ymin); ymax = max(y, ymax); oledPutPixel(t, y); if (millis() - prev > 10000) { oledVLine(t, ymin, ymax); ymin = ymax = y; prev += 10000; t ++; if (t >= 128) { t = 0; u8x8.clearDisplay(); } } } void disp() { float t = bmp280.getTemperature(); float p = bmp280.getPressure(); float a = bmp280.calcAltitude(p) - alt0; u8x8.setCursor(0, 0); u8x8.print(t); u8x8.print("C "); u8x8.print(t-temp0); u8x8.print(" "); u8x8.setCursor(0, 1); u8x8.print(p / 100.0); u8x8.print("hPa"); u8x8.print((p-pr0)/100.0); u8x8.print(" "); u8x8.setCursor(0, 2); u8x8.print(a); u8x8.print("m "); disp2( - a*20.0/250.0 + 44.0 +.5); }