Wio BG770Aで使うCO2センサ：Sensirion製SCD41の実装方法

# はじめに
今回は、Wio BG770Aを使って、SeeedStudio製の「Grove-CO2/温度/湿度センサー(SCD41)」を動かしていきたいと思います。こちらのモジュールには、Sensirion製SCD41が実装されていて、CO2濃度の他に温度・湿度も取得することができるセンサーとなります。

# 使用するデバイス
* Wio BG770A v1.0
* Grove-CO2/温度/湿度センサー(SCD41)
* SORACOM Air for セルラー

https://soracom.jp/store/30926/

https://www.marutsu.co.jp/pc/i/2229735/

https://soracom.jp/store/13380/



# ハードウェア準備

### Grove-CO2/温度/湿度センサー(SCD41)の接続


Grove-CO2/温度/湿度センサー(SCD41)は、『電源範囲：2.4V～5.5V』となっており、Wio BG770AのI2Cのポートにそのまま接続することが出来ます。基板モジュール自体には、電源周りの回路は無さそうで、SensirionのSCD41自体が電源電圧が『2.4V-5.5V』とデータシートに記載されているので、センサ自体が幅広い電圧に対応しているのでしょう（センサ内部で何かしらの電源回路はあると思いますが）

### SORACOM Air for セルラーの設置

以下の『3-1. LTEアンテナとSIMを取り付ける』を参考にしてください

https://seeedjp.github.io/Wiki/Wio_BG770A/user-manual


# ソフトウェア編
ここでは、サンプルスケッチの中で、『soracom_uptime_psm』をベースに実装を進めます。ソースコードは以下になりますが、Arduino IDEの[Examples]-[WioCellular]-[soracom]-[soracom_uptime_psm]より開いて、[Save As...]で別なスケッチとして保存して、実装を進めればよいです。

https://github.com/SeeedJP/wio_cellular/tree/main/examples/soracom/soracom-uptime-psm

### SCD41用ライブラリ
今回は、Sensirion社のライブラリを使用します。

https://github.com/Sensirion/arduino-i2c-scd4x

『SCD4x』で検索すると出てくるので、こちらのインストールします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21408/8edf4129-116d-4c39-a548-b6b4f04dec20.png)

その他、依存しているライブラリ[Sensirion Core]もインストールされますが、気にせずに全てインストールします。

### ソフトウェアの実装方法

「Include Library」より、SensirionI2cScd4x.hをインクルードします。
Wireライブラリも追加します。

```cpp
#include <SensirionI2cScd4x.h>
#include <Wire.h>

// macro definitions
// make sure that we use the proper definition of NO_ERROR
#ifdef NO_ERROR
#undef NO_ERROR
#endif
#define NO_ERROR 0

SensirionI2cScd4x sensor;

static char errorMessage[64];
static int16_t error;
```

<br>
次のsetup関数内で、「Groveコネクタ用の電源ON」と「SCD41用の初期化処理」を実装します。初期化の実装はサンプルプログラムを流用します。

```cpp
void setup(void) {
//省略

  // 以下、追加
  digitalWrite(PIN_VGROVE_ENABLE, LOW);

  Wire.begin();
  sensor.begin(Wire, SCD41_I2C_ADDR_62);

  uint64_t serialNumber = 0;
  delay(30);
  // Ensure sensor is in clean state
  error = sensor.wakeUp();
  if (error != NO_ERROR) {
      Serial.print("Error trying to execute wakeUp(): ");
      errorToString(error, errorMessage, sizeof errorMessage);
      Serial.println(errorMessage);
  }
  error = sensor.stopPeriodicMeasurement();
  if (error != NO_ERROR) {
      Serial.print("Error trying to execute stopPeriodicMeasurement(): ");
      errorToString(error, errorMessage, sizeof errorMessage);
      Serial.println(errorMessage);
  }
  error = sensor.reinit();
  if (error != NO_ERROR) {
      Serial.print("Error trying to execute reinit(): ");
      errorToString(error, errorMessage, sizeof errorMessage);
      Serial.println(errorMessage);
  }
  // Read out information about the sensor
  error = sensor.getSerialNumber(serialNumber);
  if (error != NO_ERROR) {
      Serial.print("Error trying to execute getSerialNumber(): ");
      errorToString(error, errorMessage, sizeof errorMessage);
      Serial.println(errorMessage);
      return;
  }
  Serial.print("serial number: ");
  PrintUint64(serialNumber);
  Serial.println();
  //
  // If temperature offset and/or sensor altitude compensation
  // is required, you should call the respective functions here.
  // Check out the header file for the function definitions.

}
```


<br>
次に、一定周期で計測する処理の箇所にSCD41へのアクセスを追加します。これにより、定期的にセンサよりデータを取得してSORACOMにデータを送ることが可能になります。

```cpp
static bool measure(JsonDocument &doc) {
  Serial.println("### Measuring");

  doc["uptime"] = millis() / 1000;



  // 以下が追加した処理になります（SCD41用）
  uint16_t co2Concentration = 0;
  float temperature = 0.0;
  float relativeHumidity = 0.0;
  //
  // Wake the sensor up from sleep mode.
  //
  error = sensor.wakeUp();
  if (error != NO_ERROR) {
      Serial.print("Error trying to execute wakeUp(): ");
      errorToString(error, errorMessage, sizeof errorMessage);
      Serial.println(errorMessage);
      //return;
  }
  //
  // Ignore first measurement after wake up.
  //
  error = sensor.measureSingleShot();
  if (error != NO_ERROR) {
      Serial.print("Error trying to execute measureSingleShot(): ");
      errorToString(error, errorMessage, sizeof errorMessage);
      Serial.println(errorMessage);
      //return;
  }
  //
  // Perform single shot measurement and read data.
  //
  error = sensor.measureAndReadSingleShot(co2Concentration, temperature,
                                          relativeHumidity);
  if (error != NO_ERROR) {
      Serial.print("Error trying to execute measureAndReadSingleShot(): ");
      errorToString(error, errorMessage, sizeof errorMessage);
      Serial.println(errorMessage);
      //return;
  }
  //
  // Print results in physical units.
  //
  Serial.print("CO2 concentration [ppm]: ");
  Serial.print(co2Concentration);
  Serial.println();
  Serial.print("Temperature [°C]: ");
  Serial.print(temperature);
  Serial.println();
  Serial.print("Relative Humidity [RH]: ");
  Serial.print(relativeHumidity);
  Serial.println();
  Serial.print("sleep for 5 minutes until next measurement is due");
  Serial.println();


  doc["co2"] = co2Concentration;
  doc["temperature"] = temperature;
  doc["humidity"] = relativeHumidity;




  Serial.println("### Completed");

  return true;
}
```





# SORACOMプラットフォームでの確認
事前に、「SORACOM Harvest Data」でデータを収集できるように設定はしておきます。
まずは、[Table]で送信されてきているデータを確認します。ソースコードで記述した"co2"、"temprerature"、"humidity"などが送信されてきていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21408/a8f95d8b-9d7b-4aac-a2b5-02237a65dc5c.png)

[Line Chart]でもデータを確認してみます。スケールの関係上、"co2"のデータ変化が見やすいグラフにはなっていますが、それらしい値を取得できていることも確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21408/97137f66-5e14-459c-8eda-80dd284d2170.png)


# まとめ
今回は、Wio BG770AとSeeedStudio製の「Grove-CO2/温度/湿度センサー(SCD41)」を接続して、センサからのデータ取得及びSORACOMプラットフォームへのデータ送信を確認しました。


# その他参考リンク
Wio BG770Aについては、以下にもいろいろまとめているので参考にしてください。

https://qiita.com/yukima77/items/e45a44720cd7fffade3d

https://qiita.com/yukima77/items/45793e72b2347ecbb18b

https://qiita.com/yukima77/items/aaf8ff2a3fd9249c8f4b

https://seeedjp.github.io/Wiki/Wio_BG770A/home.html