LoginSignup
2
1

More than 1 year has passed since last update.

SCD41 を ESP32 で 動かすスニペット

Last updated at Posted at 2022-11-11

性能の良い CO2 センサである SCD41 を、ライブラリなどを使わずに動かしてみて様々な機能をベア動作で確認してみます。

環境

  • Arduino 1.8.19 portable 化済
  • Linux Ubuntu 22.04 LTS Desktop Ja

接続

GND,VCC,SDA,SCLをそれぞれ接続しました。

image.png

光⾳響 NDIR センサー技術

sensirion には見た目の同じような SCD40 , SCD41, SCD42 の3つのセンサーがあります。
何が違うのかな・・・調べてみたら、以下のような違いだそうです。

測定範囲 測定精度 低消費電力
SCD40 400-2000ppm ±50 ppm
SCD41 400-5000ppm ±40 ppm 対応
SCD42 400-1000ppm ±75 ppm

今回は低消費電力がどのようになるか調べてみます。

コマンドリスト

22個しかないので、簡単♪

コマンド種類 コマンド コード 送受信 実行時間(ミリ秒) 定期測定中のコマンド実行
基本コマンド start_periodic_measurement 0x21b1 送信 - 不可
^ read_measurement 0xec05 受信 1
^ stop_periodic_measurement 0x3f86 送信 500
補償コマンド set_temperature_offset 0x241d 送信 1 不可
^ get_temperature_offset 0x2318 受信 1 不可
^ set_sensor_altitude 0x2427 送信 1 不可
^ get_sensor_altitude 0x2322 受信 1 不可
^ set_ambient_pressure 0xe000 送信 1
キャリブレーション perform_forced_recalibration 0x362f 送信+結果取得 400 不可
^ set_automatic_self_calibration_enabled 0x2416 送信 1 不可
^ get_automatic_self_calibration_enabled 0x2313 受信 1 不可
省電力 start_low_power_periodic_measurement 0x21ac 送信 - 不可
^ get_data_ready_status 0xe4b8 受信 1
高度なコマンド persist_settings 0x3615 送信 800 不可
^ get_serial_number 0x3682 受信 1 不可
^ perform_self_test 0x3639 受信 10000 不可
^ perform_factry_reset 0x3632 送信 1200 不可
^ reinit 0x3646 送信 20 不可
省電力シングルショット mesure_signle_shot 0x219d 送信 5000 不可
^ mesure_signle_shot_rht_only 0x2196 送信 50 不可
^ power_down 0x36e0 送信 1 不可
^ wake_up 0x36f6 送信 20 不可

動作確認

まずは、get_serial_number 0x3682 を試してみます。

#include <Wire.h>

uint8_t crc(const uint8_t data0, uint8_t data1) {
  uint8_t crc=0xFF ,  crc_bit;

  crc ^= data0;
  for (crc_bit = 8 ; crc_bit > 0 ; --crc_bit) {
    if (crc & 0x80){
      crc = (crc<<1) ^ 0x31;
    } else {
      crc = (crc<<1);
    }
  }
  crc ^= data1;
  for (crc_bit = 8 ; crc_bit > 0 ; --crc_bit) {
    if (crc & 0x80){
      crc = (crc<<1) ^ 0x31;
    } else {
      crc = (crc<<1);
    }
  }
  return(crc);
}

void setup() {
    Serial.begin(115200);
    delay(100);
    Serial.println("start");
}
void loop() {
    int size,j;
    byte data[9];
    Wire.begin();
    Wire.beginTransmission(0x62);
    data[0]=0x36;data[1]=0x82;
    Wire.write(data,2);
    Wire.endTransmission();
    Wire.requestFrom(0x62,9);
    
    for (size =0; Wire.available(); data[size++] = Wire.read())
    {}
    Serial.print("Serial ");
    for ( j=0; j<(size/3); j++ ){
//    Serial.printf(" %02X",data[j]);
      if ( data[j*3+2]== crc(data[j*3],data[j*3+1]))
      {
        Serial.printf("%02X%02X",data[j*3],data[j*3+1]);   
      } else {
        Serial.print( "  CRC error! ");
      }
    }
    Serial.println("");
    delay(5000);
}

I2Cアドレスは 0x62 です。0x3682コマンド"get_serial_number" を送ると9バイトの結果が帰ってきますが、3バイトごとに、2バイトのデータ+1バイトの CRC というフォーマットになっています。CRC チェックを通したシリアル番号を表示するようにしています。

実行結果

start
Serial 9C6EB7073BFF
Serial 9C6EB7073BFF
Serial 9C6EB7073BFF
...

同様に、 perform_self_test 0x3639 を試してみます。
上記のコードの一部を以下のように変更しましたが・・・うまくいきませんでした。

    Wire.beginTransmission(0x62);
    data[0]=0x36;data[1]=0x39;
    Wire.write(data,2);
    Wire.endTransmission();
    Wire.requestFrom(0x62,3);
    for (size =0; Wire.available(); data[size++] = Wire.read())
    {}
    Serial.print("self test ");

基本的な測定を試してみる

まず、start_periodic_measurement 0x21b1 を送ります。
そのあと、 read_measurement 0xec05 を送ります。
測定が終わると、stop_periodic_measurement 0x3f86 を送ります。

上記のコードの一部を更に以下のように変更します。

.
.
.
byte data[9];
.
.
.
void setup() {
    Serial.begin(115200);
    delay(100);
    Wire.begin();
    Wire.beginTransmission(0x62);
    data[0]=0x21;data[1]=0xb1;
    Wire.write(data,2);
    Wire.endTransmission();
    Serial.println("start ");
}
void loop() {
    int size,j;
    Wire.begin();
    Wire.beginTransmission(0x62);
    data[0]=0xec;data[1]=0x05;
    Wire.write(data,2);
    Wire.endTransmission();
    Wire.requestFrom(0x62,9);
    for (size =0; Wire.available(); data[size++] = Wire.read())
    {}
    for ( j=0; j<(size/3); j++ ){
      if ( data[j*3+2]== crc(data[j*3],data[j*3+1]))
      {
        Serial.printf(" %02X %02X  ",data[j*3],data[j*3+1]);   
      } else {
        Serial.print( "  CRC error! ");
      }
    }
    float temp,rh;
    Serial.printf("CO2 %02d ppm",data[0]*256+data[1]);
    temp = ((int) data[3]*256 + (int) data[4] )*175/65535-45;
    Serial.printf(" Temp %3.1f C", temp);
    Serial.printf(" RH %02d %\n",((int)data[6]*256+(int)data[7])*100/65535);   
    delay(5000);
}

測定結果

start 
 02 0D   65 7C   6C 26  CO2 525 ppm Temp 24.0 C RH 42 
 02 0E   65 7B   6C 0D  CO2 526 ppm Temp 24.0 C RH 42 
 02 0F   65 7A   6C 3A  CO2 527 ppm Temp 24.0 C RH 42 
 02 0F   65 72   6C 07  CO2 527 ppm Temp 24.0 C RH 42 
 02 10   65 78   6C 19  CO2 528 ppm Temp 24.0 C RH 42 
 02 14   65 80   6B FF  CO2 532 ppm Temp 24.0 C RH 42 
.
.
.

省電力機能を試してみる

データシートによると、

  • 測定間のアイドル消費 0.15 mA (標準)、0.2 mA (最⼤)
  • 1ショットに費やすエネルギー 243 mJ(標準) 296 mJ(最大)

試してみました。

#include <Wire.h>

byte data[100];
uint8_t crc(const uint8_t data0, uint8_t data1) {
  uint8_t crc=0xFF ,  crc_bit;

  crc ^= data0;
  for (crc_bit = 8 ; crc_bit > 0 ; --crc_bit) {
    if (crc & 0x80){
      crc = (crc<<1) ^ 0x31;
    } else {
      crc = (crc<<1);
    }
  }
  crc ^= data1;
  for (crc_bit = 8 ; crc_bit > 0 ; --crc_bit) {
    if (crc & 0x80){
      crc = (crc<<1) ^ 0x31;
    } else {
      crc = (crc<<1);
    }
  }
  return(crc);
}

void writeSCD4X(int command )
{
  Wire.beginTransmission(0x62);
  data[0]=(byte) ( command / 256 );data[1]= (byte) ( command % 256 );
  //Serial.printf("write: %02X %02X  ",data[0],data[1]);   
  Wire.write(data,2);
  Wire.endTransmission();
}
int readSCD4X(int command,int size )
{
  int count,j;
  writeSCD4X(command);
  delay(10);
  Wire.requestFrom(0x62,size);
  delay(10);
  for ( count = 0; Wire.available(); data[count++] = Wire.read())
  {}
  for ( j=0; j<(count/3); j++ ){
    if ( data[j*3+2]== crc(data[j*3],data[j*3+1]))
    {
      // Serial.printf(" %02X %02X  ",data[j*3],data[j*3+1]);   
    } else {
      Serial.print( "  CRC error! ");
      return(-1);
    }
  }
  //Serial.printf("return byte count: %d\n ",count );
  return(size);
}
void printCO2TEMPRH()
{
    float temp,rh;
    Serial.printf("CO2 %02d ppm",data[0]*256+data[1]);
    temp = ((int) data[3]*256 + (int) data[4] )*175/65535-45;
    Serial.printf(" Temp %3.1f C", temp);
    Serial.printf(" RH %02d %\n",((int)data[6]*256+(int)data[7])*100/65535);   
}

void measuresingleshot(){

    // measure single shot
    writeSCD4X(0x219d );
    delay(5000); 

    // get_data_ready_status
    for ( data[0] = data[1] = 0;
          0 == (0b11111111111 & (data[0]*256+data[1]));
          readSCD4X(0xe4b8,3) )
    {
      // Serial.printf( "ready_status:%02X%02X\n",data[0],data[1]);
    }
    //*/

    // read_measurement
    if ( 9 != readSCD4X(0xec05,9)) 
    {
       Serial.print("error measurement");
       return;
    }
    printCO2TEMPRH();
}

void setup() {
    Serial.begin(115200);
    delay(100);
    Wire.begin();
    // measure single shot
  //  writeSCD4X(0x219d );  
    delay(5000);
    Serial.println("start ");
}
void loop() {
  //wake_up
  writeSCD4X(0x36f6 );
  Serial.println("wake up");
    
  //get serial number    Serial: 0x9C6EB7073BFF
  if ( 9 != readSCD4X(0x3682,9) )
  {
    Serial.print("error read serial number");
    delay(10000);  
  }
  // Serial.printf( "Serial:%02X%02X%02X%02X%02X%02X\n",data[0],data[1],data[3],data[4],data[6],data[7]);

  // measure
  for ( int  i = 0; i< 5; i++){
    measuresingleshot();
  }    
    
  // powerdown SCD41
  writeSCD4X(0x36e0 );
  Serial.println("power down");
  delay(1000*300);
}

データシート 3.10.4 wake_up

Description: Wake up the sensor from sleep mode into idle mode. Note that the SCD4x does not acknowledge the wake_up
command. To verify that the sensor is in the idle state after issuing the wake_up command, the serial number can be read out
(chapter 3.9.2). Note that

とあるので、シリアルナンバーを読んでいます。

測定結果



rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:1184
load:0x40078000,len:13160
load:0x40080400,len:3036
entry 0x400805e4
start 
wake up
CO2 810 ppm Temp 24.0 C RH 44 
CO2 881 ppm Temp 24.0 C RH 44 
CO2 864 ppm Temp 24.0 C RH 44 
CO2 922 ppm Temp 24.0 C RH 44 
CO2 905 ppm Temp 24.0 C RH 44 
power down
wake up
CO2 916 ppm Temp 21.0 C RH 48 
CO2 1005 ppm Temp 21.0 C RH 48 
CO2 977 ppm Temp 22.0 C RH 48 
CO2 1000 ppm Temp 22.0 C RH 48 
CO2 943 ppm Temp 22.0 C RH 47 
power down
wake up
CO2 1091 ppm Temp 21.0 C RH 49 
CO2 1058 ppm Temp 21.0 C RH 49 
CO2 832 ppm Temp 21.0 C RH 49 
CO2 879 ppm Temp 22.0 C RH 49 
CO2 968 ppm Temp 22.0 C RH 48 
power down
wake up
CO2 1096 ppm Temp 21.0 C RH 49 
CO2 1097 ppm Temp 21.0 C RH 49 
CO2 1097 ppm Temp 21.0 C RH 49 
CO2 1089 ppm Temp 22.0 C RH 49 
CO2 1072 ppm Temp 22.0 C RH 49 
power down
wake up
CO2 1092 ppm Temp 21.0 C RH 50 
CO2 1079 ppm Temp 21.0 C RH 50 
CO2 1070 ppm Temp 21.0 C RH 49 
CO2 1072 ppm Temp 21.0 C RH 49 
CO2 1067 ppm Temp 22.0 C RH 49 
power down

データシート 3.10 Low power single shot (SCD41)

To reduce noise levels, the I2C master can perform several single shot measurements in a row and average the CO2 output values. After a power cycle, the initial single shot reading should be discarded to maximize accuracy.

とあるので、 wake up と power down との間に5回読んでいます。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1