株式会社アイツーの LoRa 無線モジュール LRA1 に、 SCD41 を使ってみました。
「SCD41 を ESP32 で 動かすスニペット」
https://qiita.com/nanbuwks/items/788c8b6eec8e68639751
において、ライブラリなどを使わずに直接I2Cを制御して動かしてみたスケッチがあるので、それを LRA1 に応用します。
Spansion の CO2 センサは精度がいいですが、その中でもSCD41 は以下のように低消費電力に対応しています。
測定範囲 | 測定精度 | 低消費電力 | |
---|---|---|---|
SCD40 | 400-2000ppm | ±50 ppm | |
SCD41 | 400-5000ppm | ±40 ppm | 対応 |
SCD42 | 400-1000ppm | ±75 ppm |
省電力の LRA1 に組み合わせてみましょう。
テストベッド
こんな感じで試してみました。奥が SCD41 モジュールです。ヒゲが出ているのは、実験中にロジアナをつなげていたためです。
試した動作
先のESP32の例では、以下のコマンドを試してました。
- 動作確認として get_serial_number 0x3682
- 基本的な測定として read_measurement 0xec05
- 省電力測定として measure_single_shot 0x219d
これを LRA1 で動かしてみましたがこのうち get_serial_number と measure_single_shot は問題なく動きましたが、 read_measurement はうまくいかななかった? (検証不足)
measure_single_shot のサンプルプログラム
10 _start
15 Print "start"
20 Catch _ERRORHANDLER
30 ' I2cD(-1)=100
40 ' 0x36f6 wake_up
50 I2cD(0)=$36
60 I2cD(1)=$F6
70 I2cW $62,-1,2
80 '0x3682 get_serial_number
90 I2cD(0)=$36
100 I2cD(1)=$82
110 Catch _ERRORHANDLER
120 I2cW $62,-1,2
130 I2cR $62,-1,9
140 ' gosub _PRINT9BYTES
150 Gosub _crccheck
160 ' measure 5 times
170 For K=1 To 5
180 ' 0X219d measure_single_shot
190 I2cD(0)=$21
200 I2cD(1)=$9D
210 I2cW $62,-1,2
220 Delay 5000
230 ' 0xe4b8 get_data_ready_status
240 I2cD(0)=$E4
250 I2cD(1)=$B8
260 ' wait loop
270 I2cW $62,-1,2
280 Delay 10
290 I2cR $62,-1,3
300 If $80=I2cD(0)&&$0=I2cD(1)Then Goto 280:End EndIf
310 'Gosub _PRINT9BYTES
320 Gosub _crccheck
330 ' read_measurement
340 I2cD(0)=$EC
350 I2cD(1)=$5
360 I2cW $62,-1,2
370 I2cR $62,-1,9
380 'gosub _PRINT9BYTES
390 Gosub _PRINTCO2TEMPRH
400 Next
410 I2cD(0)=$36
420 I2cD(1)=$E0
430 I2cW $62,-1,2
440 ' sleep
445 Print "sleep"
450 Sleep 270
460 Goto _start
470 End
480 '==== ERROR HANDLER=====
490 _ERRORHANDLER
500 Print "ERROR NO=";Error;" AT ";Error#
510 Catch _ERRORHANDLER
520 If Error=12 Then Delay (1000):Goto Error#End EndIf
530 End
540 '======subroutine=============
550 _PRINT9BYTES
560 ' print return 9 bytes
570 Print Form("X02",I2cD(0))Form("X02",I2cD(1))Form("X02",I2cD(2))Form("X02",I2cD(3))Form("X02",I2cD(4))Form("X02",I2cD(5))Form("X02",I2cD(6))Form("X02",I2cD(7))Form("X02",I2cD(8))
580 ' 9C6EDCB707293BFF85
590 U=9
600 Gosub _crccheck
610 If U=-1 Then Print "error":End EndIf
620 Print "serial:";:Print Form("X02",I2cD(0))Form("X02",I2cD(1))Form("X02",I2cD(3))Form("X02",I2cD(4))Form("X02",I2cD(6))Form("X02",I2cD(7))
630 Return
650 _crccheck
660 ' use revariant : datacoutnt : U return; -1; error
670 For I=0 To U/3-1
680 C=$FF^I2cD(I*3)
690 For J=0 To 7
700 If (127<C)Then C=((C<<1)&$FF)^$31 Else C=((C<<1)&$FF)EndIf
710 'Print "J:";:Print J;:Print " ";:Print Form("X02",C)
720 Next
730 C=C^I2cD(I*3+1)
740 For J=0 To 7
750 If (127<C)Then C=((C<<1)&$FF)^$31 Else C=((C<<1)&$FF)EndIf
760 Next
770 If I2cD(I*3+2)<>C Then U=-1:Print "CRC ERROR":Return EndIf
780 'print "CRC OK"
790 Next
800 Return
810 _PRINTCO2TEMPRH
820 Print "CO2:";:Print I2cD(0)*256+I2cD(1);
830 T=(I2cD(3)*256+I2cD(4))*175/65535-45
840 Print " TEMP:";:Print T;
850 H=(I2cD(6)*256+I2cD(7))*100/65535
860 Print " RH:";:Print H;:Print "%"
870 Return
やっていること
- writeSCD4X(0x36f6 ); WakeUp
- readSCD4X(0x3682,9) get_serial_number
- 5回繰り返し{
- writeSCD4X(0x219d ); measure_single_shot
- readSCD4X(0xe4b8,3) get_data_ready_status が 0 になるまで繰り返し
- readSCD4X(0xec05,9) read_measurement
- }
- writeSCD4X(0x36e0 ); power down
実行結果
>>run
start
CO2:526 TEMP:14 RH:42%
CO2:526 TEMP:14 RH:42%
CO2:511 TEMP:14 RH:42%
CO2:535 TEMP:14 RH:42%
CO2:516 TEMP:14 RH:42%
sleep
start
ERROR NO=12 AT 70
CO2:565 TEMP:14 RH:43%
CO2:547 TEMP:14 RH:43%
CO2:535 TEMP:14 RH:43%
CO2:552 TEMP:14 RH:43%
CO2:553 TEMP:15 RH:42%
sleep
start
ERROR NO=12 AT 70
CO2:554 TEMP:14 RH:42%
CO2:559 TEMP:14 RH:42%
CO2:529 TEMP:14 RH:42%
CO2:525 TEMP:14 RH:42%
CO2:551 TEMP:14 RH:42%
sleep
うまくいきました・・・
が、途中で ERROR NO=12 AT 70 というのがありますね。
ERROR 12 とは、Device access error となり、 Wake Up のコマンド I2C でが書き込めないエラーです。ESP32 ではエラーが無いのになと思ったら、ロジアナで調べてみると ESP32 でもI2Cバス上でエラーが発生た時に再送して取り直していて、ソフトウェアにエラーが出てきてないだけでした。
LRA1 では、アクセスエラーが起こるとそのままではプログラムが停止してしますので、エラーハンドラを用いてリトライするようにしています。
おまけ うまくいかなかったサンプルプログラム
以下は read_measurement でうまくいかなかったプログラムです。
ちゃんとトラブルシュートしてませんが、後々のために残しておきます。
1 Catch 1000
10 I2cD(-1)=100
20 ' 0x36f6 wake_up
30 I2cD(0)=$36
40 I2cD(1)=$F6
50 I2cW $62,-1,2
60 '0x3682 get_serial_number
70 I2cD(0)=$36
80 I2cD(1)=$82
85 Catch 1000
90 I2cW $62,-1,2
100 I2cR $62,-1,9
110 Gosub 240
120 ' 0x21ac start_low_power_periodic_measurement
130 I2cD(0)=$21
140 I2cD(1)=$B1
150 I2cW $62,-1,2
160 ' 0xec05 read_measurement
170 I2cD(0)=$EC
180 I2cD(1)=$5
190 I2cW $62,-1,2
195 Delay 10
200 I2cR $62,-1,9
210 Gosub 240
220 End
230 '======subroutine=============
240 ' print return 9 bytes
250 Print Form("X02",I2cD(0))Form("X02",I2cD(1))Form("X02",I2cD(2))Form("X02",I2cD(3))Form("X02",I2cD(4))Form("X02",I2cD(5))Form("X02",I2cD(6))Form("X02",I2cD(7))Form("X02",I2cD(8))
260 ' 9C6EDCB707293BFF85
270 U=9
280 Gosub _crccheck
290 If U=-1 Then Print "error":End EndIf
300 Print "serial:";:Print Form("X02",I2cD(0))Form("X02",I2cD(1))Form("X02",I2cD(3))Form("X02",I2cD(4))Form("X02",I2cD(6))Form("X02",I2cD(7))
310 Return
320 '=========subroutine ==============
330 _crccheck
340 ' use revariant : datacoutnt : U return; -1; error
350 For I=0 To U/3-1
360 C=$FF^I2cD(I*3)
370 For J=0 To 7
380 If (127<C)Then C=((C<<1)&$FF)^$31 Else C=((C<<1)&$FF)EndIf
390 'Print "J:";:Print J;:Print " ";:Print Form("X02",C)
400 Next
410 C=C^I2cD(I*3+1)
420 For J=0 To 7
430 If (127<C)Then C=((C<<1)&$FF)^$31 Else C=((C<<1)&$FF)EndIf
440 Next
450 If I2cD(I*3+2)<>C Then U=-1:Print "CRC ERROR":Return EndIf
460 'print "CRC OK"
470 Next
480 Return
1000 Print "ERROR NO=";Error;" AT ";Error#
1005 Catch 1000
1010 If Error=12 Then Goto Error#
これを実行すると、
>run
C5AEBFB707293BE1D9
serial:C5AEB7073BE1
ERROR NO=12 AT 200
ERROR NO=12 AT 200
ERROR NO=12 AT 200
ERROR NO=12 AT 200
ERROR NO=12 AT 200
ERROR NO=12 AT 200
ERROR NO=12 AT 200
ERROR NO=12 AT 200
ERROR NO=12 AT 200
ERROR NO=12 AT 200
ERROR NO=12 AT 200
となりました。