M5StickC ENV Hat III で気圧を測る ~ 高度計を作る
今までM5StickCで高度計を作成しましたが、センサーにBME280を使い、Proto Hatに組み込んで作ったものでした。
これで機能的には問題ないのですが、Hat部分が大きくて実際に使うと邪魔な感じが拭えませんでした。そこで、正規のHatであるENV Hat IIIを購入しました。
このように、自作Hatの半分の大きさです。ENV Hat IIIをM5StickCに取り付けると
こんな感じでスッキリします。これなら、行動中でも邪魔になることは少なそうです。
さて、ENV Hat IIIですが、ENV Hat IIで使用されていたBMP280がQMP6988に変更されています。また、今まで温度と湿度はBME280から取得していましたが、これもSHT30から取得の変更になります。
これらの変更のため、ライブラリをダウンロードしました。githubにサンプルが用意されています。
srcフォルダにあるソースを、プロジェクトのあるフォルダにコピーしてください。
以下、ENV Hat IIIに対応したソースです。
#include <M5StickC.h>
#include <Wire.h>
#include <math.h>
#include <time.h>
#include <WiFi.h>
#include "Adafruit_Sensor.h"
#include "QMP6988.h"
#include "SHT3X.h"
SHT3X sht30;
QMP6988 qmp6988;
const char* g_ssid = "XXXXXXXXXXXXXX";
const char* g_password = "XXXXXXXXXXXXXX";
const char* g_ntpServer = "ntp.jst.mfeed.ad.jp" ;
const long g_gmtOffset = 9 * 3600 ;
const int g_daylightOffset_sec = 0; // サマータイム設定なし
RTC_TimeTypeDef g_RTC_Time;
RTC_DateTypeDef g_RTC_Date;
bool g_isPowerON = false ;
RTC_DATA_ATTR int g_hosei = 0 ;
int g_editTimeCount = 0 ; // 修正できる時間 (500ms × 20回)
bool g_isDateDisp = true ;
float getAltitude(float wkTemp, float wkPress) {
float wkAltitude = 0;
float seaAltitude = (float)1013.25 ;
float PressJyou = (float)1 / (float)5.257 ;
float wkPressHi = seaAltitude / wkPress ;
float wkPress2 = powf(wkPressHi, PressJyou) ;
wkPress2 = wkPress2 - (float)1 ;
float wkTemp2 = wkTemp + 273.15 ;
wkAltitude = wkPress2 * wkTemp2 / (float)0.0065 ;
return wkAltitude ;
}
void FooterLCD() {
static const char *week[7] = {"Sun","Mon","Tue","Wed","Thr","Fri","Sat"};
M5.Rtc.GetTime(&g_RTC_Time);
M5.Rtc.GetData(&g_RTC_Date);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setCursor(2, 65, 1);
M5.Lcd.printf("%04d.%02d.%02d %s %02d:%02d", g_RTC_Date.Year, g_RTC_Date.Month, g_RTC_Date.Date, week[g_RTC_Date.WeekDay], g_RTC_Time.Hours, g_RTC_Time.Minutes);
}
void DispLCD(float wAltitude) {
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextColor(YELLOW);
M5.Lcd.setCursor(135, 35, 4);
M5.Lcd.printf("m") ;
M5.Lcd.setCursor(0, 10, 7);
if (g_isPowerON == false) {
M5.Lcd.setTextColor(GREEN);
M5.Lcd.printf("%04.0f", wAltitude) ;
if (g_isDateDisp) {
FooterLCD();
}
delay(10000) ;
} else {
M5.Lcd.setTextColor(WHITE);
M5.Lcd.printf("%04.0f", wAltitude) ;
delay(500);
}
}
void setup() {
// put your setup code here, to run once:
M5.begin();
Wire.end();
// Arduino IDE の環境によっては、必要です。
Wire.begin(0,26);
M5.Lcd.setRotation(3);
M5.Lcd.fillScreen(BLACK);
qmp6988.init();
esp_sleep_wakeup_cause_t wakeup ;
wakeup = esp_sleep_get_wakeup_cause();
switch(wakeup){
case ESP_SLEEP_WAKEUP_EXT0 :
Serial.printf("Boot from deep sleep\n");
g_isPowerON = false ;
break; // スリープ復帰時はWiFiで時刻取得しない
default :
Serial.printf("Initial startup\n");
// Wi-Fiから時刻を取得
M5.Lcd.setCursor(0, 0, 1);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(1);
M5.Lcd.print("Wi-Fi Connecting");
WiFi.begin(g_ssid, g_password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
M5.Lcd.print(".");
}
M5.Lcd.println("Wi-Fi Connected");
// Set ntp time to local
configTime(g_gmtOffset, g_daylightOffset_sec, g_ntpServer);
struct tm timeInfo;
if (getLocalTime(&timeInfo)) {
RTC_TimeTypeDef wTime;
wTime.Hours = timeInfo.tm_hour;
wTime.Minutes = timeInfo.tm_min;
wTime.Seconds = timeInfo.tm_sec;
M5.Rtc.SetTime(&wTime);
RTC_DateTypeDef wDate;
wDate.WeekDay = timeInfo.tm_wday;
wDate.Month = timeInfo.tm_mon + 1;
wDate.Date = timeInfo.tm_mday;
wDate.Year = timeInfo.tm_year + 1900;
M5.Rtc.SetData(&wDate);
}
g_isPowerON = true ;
break ;
} // end switch
}
void loop() {
float temp, altitude, pressure, humid;
if (sht30.get() == 0) {
humid = sht30.humidity ;
temp = sht30.cTemp;
} else {
humid = 0.0 ;
temp = 0.0 ;
}
pressure = qmp6988.calcPressure() / 100 ;
Serial.printf("pressure=%f\n", pressure);
altitude = getAltitude(temp, pressure) + g_hosei ;
DispLCD(altitude);
if (g_isPowerON) {
// 高度の補正が可能
if (g_editTimeCount++ > 50) {
// 修正モード終了
g_isPowerON = false ;
Serial.println("Exit Edit Mode\n");
} else {
Serial.println("Edit Mode\n");
if ( digitalRead(M5_BUTTON_HOME) == LOW ) {
// +補正
g_hosei += 10 ;
Serial.print("+");
} else if (digitalRead(M5_BUTTON_RST) == LOW ) {
// -補正
g_hosei -= 10 ;
Serial.print("-");
}
}
} else {
// ボタンAでスリープ復帰
pinMode(GPIO_NUM_37, INPUT_PULLUP);
esp_sleep_enable_ext0_wakeup(GPIO_NUM_37, LOW);
// ディープスリープスタート
M5.Axp.SetSleep();
esp_deep_sleep_start();
}
}
ボタンA(Home)で10m高度を上げる補正をし、ボタンB(Reset)で10m高度を下げる補正をします。また、isModeを変更することで温度、湿度の表示もできますが、プログラムの修正が必要です。
ひとつ問題点があります。高度を計算する時に温度が必要ですが、M5StickCに隣接するセンサーは実際よりも高い温度となってしまいます。また、腕の体温の影響もあります。そのため、高度の計算に狂いが出てきます。しかし、気圧の変化の方が高度に大きく影響するので、補正をすることで十分とも言えそうです。
今回のプログラムは、5秒ごとに気圧を計測しているので、バッテリーが1時間ほどしか持たないでしょう。もし、実用的なプログラムにするのであれば、以下の記事で紹介したソースをENV Hat IIIに対応させてください。バッテリーを1日持たせるために、deep sleepに対応しました。ボタンAを押すと復帰して10秒間高度を表示します。日付時刻も表示できるようにしました。
実際に試してみたいのですが、登山を辞めたのでできません。近くのハイキングか、車での峠越えでテストしてみたいと思います。
紹介動画も作りました。