M5Stack Basic と絶対圧センサー(2SMPB-02E)で作る気圧計

気圧は天候の変化だけでなく、頭痛といった体調にも影響するそうです。気圧は地域や条件で異なるため、自分の場所でどのように変化しているのか計測するのも良いのではないでしょうか。

参考)
- [天気痛予報を作るデータについて(ウェザーニーズ社)](https://weathernews.jp/s/topics/202001/250055/)
- [頭痛ーる](https://zutool.jp/)

## M5Stack Basic と絶対圧センサー(2SMPB-02E)で作る気圧計

#### 完成形
M5Stack Basicの本体に、絶対圧センサーを両面テープで貼り付けただけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/2af53212-ec79-6f01-88bd-2e742e510404.png)
LCDには何も表示してません。M5Stack君の良さが活かせてない感じもしますが、そこは本筋ではないので今回は見送ってます。

#### 蓄積データ
IoTデータ収集・蓄積サービス「SORACOM Harvest Data」を利用しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/3fe1508e-d2c4-210a-7c71-e86707f15e13.png)

### 構成図

![](https://docs.google.com/drawings/d/e/2PACX-1vTkRYQExbh1UHBDKAXmM_tbOc_2eijhAWBsUaMIoGF8qASrMsayHV6DPgnR7e5lyy_pPiQS834NPG62/pub?w=684&h=292)

絶対圧センサーとの接続はI2Cです。M5Stack Basicで定期的に読み取り、データ送信をしています。
通信はWi-Fiを使っています。[WireGuard](https://www.wireguard.com/)を使い「[SORACOM Arc](https://soracom.jp/services/arc/)」に対してVPNを張ることで、Wi-Fi経由でもSORACOMのIoT向けサービスが活用できるようになります。
今回は、その中でもIoTデータ蓄積・収集サービス「[SORACOM Harvest Data](https://soracom.jp/services/harvest/)」に気圧データを保存しています。

## M5Stack のコード

- 絶対圧センサーからの読み取りは、センサー自体が持つ機能でノイズ低減をしています。詳しくは[M5StackにGroveでセンサつないで気圧測定する](https://westgate-lab.hatenablog.com/entry/2020/03/22/115903)で解説されています。
- SORACOM Harvest Dataへのデータ送信には、UDPを利用しています。ペイロードフォーマット自体はJSONです。HTTPよりもコードが簡潔になる上、データ量も削減できます。送り先は `uni.soracom.io` となっていますが、ここから有効化されたサービスにルーティングされる仕組みです([Unified Endpoint](https://users.soracom.io/ja-jp/docs/unified-endpoint/))。
- Wi-Fiの切断を検出したら、再起動して再接続を試みるようになっています。

```c++:m5stack_2SMPB-02E_wireguard_soracom.ino
/*
   Barometer made with M5Stack Basic and pressure sensor (2SMPB-02E)
   Copyright (c) 2022 Kohei "Max" MATSUSHITA (ma2shita+git@ma2shita.jp)
   Released under the MIT license
   https://opensource.org/licenses/mit-license.php
   
   NOTE: Also follow the library's license.
*/

static const char _VERSION_[] = "2SMPB-WG-0.1";

#include <M5Stack.h>
static const char TAG[] = "TAG"; // for ESP_LOG*

#include <WiFi.h>
static const char WIFI_SSID[] = "YOUR_WIFI_SSID";
static const char WIFI_PASSWORD[] = "YOUR_WIFI_PASS";
WiFiUDP ctx;
static const char ENTRYPOINT_HOST[] = "uni.soracom.io";
static const int ENTRYPOINT_PORT = 23080;

#include <WireGuard-ESP32.h> // Install from lib. manager. "WireGuard-ESP32 by Kenta Ida"
static WireGuard wg;
static const char PRIVATE_KEY[] = "VzOe......kq4="; // [Interface] PrivateKey
static const IPAddress LOCAL_IP(999, 999, 999, 999); // [Interface] Address
static const char PUBLIC_KEY[] = "cx/QH.......VA=";  // [Peer] PublicKey
static const char ENDPOINT_ADDRESS[] = "XXXX.soracom.io"; // [Peer] Endpoint(address)
static const uint16_t ENDPOINT_PORT = 99999; // [Peer] Endpoint(port)

#include <ArduinoJson.h> // Install from lib. manager. "ArduinoJson by Benoit Blanchon"

#include "Omron2SMPB02E.h" // Install from GitHub then a tweak fix by your hand. "https://github.com/omron-devhub/2smpb02e-grove-arduino"
Omron2SMPB02E prs;

template <typename T> void connect_to_network(T *wifi) {
  int32_t _enter = millis();
  wifi->mode(WIFI_STA);
  ESP_LOGD(TAG, "begin():");
  wifi->begin(WIFI_SSID, WIFI_PASSWORD);
  while (wifi->status() != WL_CONNECTED) {
    ESP_LOGV(TAG, ".");
    delay(200);
  }
  ESP_LOGD(TAG, "localIP(): %s", wifi->localIP().toString().c_str());
  ESP_LOGD(TAG, "elapsed(ms): %d", millis() - _enter);
}

template <typename T> bool is_network_connected(T *wifi) {
  bool result = (wifi->status() == WL_CONNECTED);
  if (!result) ESP_LOGE(TAG, "status() != WL_CONNECTED");
  return result;
}

void connect_to_wireguard(WireGuard *wg) {
  int32_t _enter = millis();
  ESP_LOGD(TAG, "Adjust system time. (with winting 3 seconds for adjusting.)");
  configTime(9 * 60 * 60, 0, "ntp.jst.mfeed.ad.jp", "ntp.nict.jp", "time.google.com");
  delay(3000); // Wait for adjust
  ESP_LOGD(TAG, "begin():");
  wg->begin(LOCAL_IP, PRIVATE_KEY, ENDPOINT_ADDRESS, PUBLIC_KEY, ENDPOINT_PORT);
  ESP_LOGD(TAG, "elapsed(ms): %d", millis() - _enter);
}

void mcu_restart() {
  ESP_LOGD(TAG, "enter.");
  delay(3000); // waiting for flush of serial buffer
  M5.Power.reset();
}

void setup() {
  delay(1000);
  
  M5.begin();
  dacWrite(25, 0); // Speaker OFF // bad known how for M5Stack ...
  
  Serial.begin(115200); // for log
  ESP_LOGD(TAG, "--- START: %s", _VERSION_);

  M5.Lcd.sleep();
  M5.Lcd.setBrightness(0);
  
  connect_to_network(&WiFi);
  connect_to_wireguard(&wg);

  prs.begin();
  prs.set_average(AVG_1, AVG_8);
  delay(300);
  prs.set_filter(FILTER_4);
  delay(300);
  prs.set_mode(MODE_NORMAL);
  delay(300);
}

static const uint32_t LOOP_INTERVAL_MS_IN_LOOP = 60000; // msec
void loop() {
  if (!is_network_connected(&WiFi)) mcu_restart();
  
  StaticJsonDocument<16> doc;
  doc["hPa"] = prs.read_pressure() / 100.0;
  serializeJson(doc, Serial); // logging => e.g.) `{"hPa": 1014.1980135}`
  Serial.println(); // for CRLF
  
  ctx.beginPacket(ENTRYPOINT_HOST, ENTRYPOINT_PORT);
  serializeJson(doc, ctx);
  ctx.println();
  ctx.endPacket();
  
  delay(LOOP_INTERVAL_MS_IN_LOOP);
}
```

### 絶対圧センサーのライブラリの手直し

絶対圧センサーのライブラリは2022年8月時点のmainブランチでバグがあり、以下のようなエラーが出てコンパイルできません。

```
In file included from \...\Arduino\libraries\2smpb02e-grove-arduino-master\src\Omron2SMPB02E.cpp:5:
\...\Arduino\libraries\2smpb02e-grove-arduino-master\src/Omron2SMPB02E.h:124:3: note: previous specification in 'Omron2SMPB02E::Omron2SMPB02E(uint8_t)' here
   Omron2SMPB02E(uint8_t SDO = 1);
   ^~~~~~~~~~~~~
```

https://github.com/omron-devhub/2smpb02e-grove-arduino/pull/2/files このP-Rで提案されている内容を手動で適用すればコンパイルできます。
具体的には `src/Omron2SMPB02E.cpp` の39行目の変更と、 `src/Omron2SMPB02E.h` の11~13行目の削除です。

## おわりに

計測データを自分自身で使う場合は特に気にすることはありませんが、一般公開をしたい場合は、気象業務法に従う必要があります。詳しくは自分で読んでください。
https://www.jma.go.jp/jma/kishou/shinsei/kentei/index.html

### SORACOM 費用

- SORACOM Arc には、無料利用枠があります (https://soracom.jp/services/arc/)
- SORACOM Harvest には、無料利用枠があります (https://soracom.jp/services/harvest/)

### 参考資料

- 絶対圧センサー自体
    - [スイッチサイエンス / 絶対圧センサ評価モジュール（Groveコネクタ付）](https://www.switch-science.com/catalog/5329/)

- 絶対圧センサーのライブラリ
    - [omron-devhub/2smpb02e-grove-arduino : GitHub](https://github.com/omron-devhub/2smpb02e-grove-arduino)
        - [MODE_NORMALが多重定義されている問題の修正](https://github.com/omron-devhub/2smpb02e-grove-arduino/pull/2)
    - [M5StackにGroveでセンサつないで気圧測定する](https://westgate-lab.hatenablog.com/entry/2020/03/22/115903)
- UDP送信
    - [esp8266/Arduino / WiFiUdp.h](https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/src/WiFiUdp.h)
    - [JsonUdpBeacon.ino / ArduinoJson](https://arduinojson.org/v6/example/udp-beacon/)

EoT