Sensirion社のSCD41を搭載したCO2・温湿度センサモジュールとXIAO ESP32C3とをI2C接続して動作確認した際の作業メモです。
Seeed StudioのWiki よくあるご質問(FAQ):
What to do when upload fails/the program runs abnormally/the device port is not found? | Seeed Studio Wiki
デバイスが認識されない問題やアップロードに失敗する問題の解決
準備したパーツ
ネット通販(秋月電子通商、マルツオンライン など)でパーツを集めてブレッドボードでクイン建てました。
今回使ったCO2センサモジュール(二酸化炭素+温度/湿度センサ)【FSNS-SCD41-X00】は、SCD41センサの中では比較的低価格なモジュールでした。このモジュールには基板上にI2Cプルアップ抵抗が内蔵(有効化が必要)されています。
# | パーツ | 個数 | 備考 |
1 | Seeed Studio XIAO ESP32C3 | 1 | 【M-17454】 |
2 | ILI9431搭載2.8インチ SPI制御タッチパネル付TFT液晶MSP2807 | 1 | 【M-16265】 |
3 | BME280 気圧、温湿度センサ モジュール | 1 | ※手持ち |
4 | CO2センサモジュール(二酸化炭素+温度/湿度センサ)【SCD41】 | 1 | 【FSNS-SCD41-X00】 |
5 | プルアップ抵抗 | – | ※SCD41モジュール内蔵 ※有効化が必要 |
6 | ブレッドボード 6穴版 EIC-3901 | 1 | 【P-12366】 |
7 | 細ピンヘッダ 1×40(黒) | 1 | 【C-06631】 |
8 | ジャンパーワイヤ オス-オス 10cmセット | 適量 | 【C-05371】 |
結線図
XIAO ESP32C3は、Espressif ESP32-C3 WiFi / Bluetoothデュアルモードチップをベースにした超小型のマイコンボードです。小型モジュールのためピン数が限られますが独立したI2C、SPIピンがあるので、これらの接続インターフェースを持ったセンサモジュールや液晶モジュールを利用できます。
I2C接続のCO2センサモジュールをSCD30からSCD41に変更したこと以外は下記の記事と同じ構成です。
SCD41センサモジュール
SENSIRION社のSCD41はPASens技術により、SCD30のNDIR方式にある光学ビーム経路を必要としないため、構造もシンプルで小型です。測定範囲はSCD30が400~10000ppm、SCD41が400~5000ppmです。
今回購入したSCD41モジュール【FSNS-SCD41-X00】は、SCD30モジュールの基板寸法に合わせてあり、7ピンヘッダーの他に、I2CやGrove(I2C)コネクタ取り付けパターンが設けられています。
I2C(SCL、SDA)のプルアップ抵抗(4.7KΩ)を内蔵しています。購入時は有効化されていないので、I2C内でプルアップされていない場合は基板裏面のランドパターンを半田でショートします。
XIAO ESP32C3と2.8インチ液晶モジュールを4線式SPIで接続
2.8インチ液晶モジュールにはILI9341を使った240×320の液晶モジュールに加えて、XPT2046を使ったタッチパネル、SDカードスロットが実装されています。今回は表示用として240×320の液晶モジュールのみを利用しています。
電源電圧は3.3Vで使用するので、秋月電子通商サイトを参考に、J1のランドパターンをはんだ付けして三端子レギュレータ(U1)をバイパスしています。
XIAO ESP32C3と2.8インチ液晶モジュールはSPIで接続、この液晶モジュールのロジックレベルは3.3Vなので、同じ3.3V系のXIAO ESP32C3と直結できます。
J2コネクタ(タッチパネルを除く液晶への接続箇所)とXIAO ESP32C3のGPIOへのピンアサインです。
# | 2.8インチTFT液晶の シルク印刷 | XIAO ESP32C3に 接続したGPIO番号 |
1 | VCC ※共通、3.3V | 3V3 |
2 | GND ※共通 | GND |
3 | CS | GPIO2 |
4 | RESET | GPIO3 |
5 | DC | GPIO4 |
6 | SDI(MOSI) | GPIO10 |
7 | SCK | GPIO8 |
8 | LED ※バックライト | 3V3 |
9 | SDO(MISO) | GPIO9 |
結線図
動作確認できた現在の結線図です。通信線には短めの長さ10cmのジャンパーワイヤを使っています。
SCD41(最大:205mA、平均:3mA)、SCD30(最大:75mA、平均:19mA)と2.8インチTFT液晶(約90mA)は供給電流が比較的大きいモジュールです。ブレッドボードとジャンパーワイヤの接触が悪いと安定動作しない時がありました。SCD41センサと2.8インチTFT液晶は供給電流が大きいのでXIAO ESP32C3の3V3端子から個別にジャンパーワイヤで3.3Vを給電しています。
XIAO ESP32C3サイトの power-pins には「3V3 – This is the regulated output from the onboard regulator. You can draw 700mA」の記載があったので外部電源モジュールは使っていません。数日試しましたが、XIAO ESP32C3のUSB Type-Cコネクタからの給電で動作に支障は無いようです。
SCD41とSCD30のI2Cアドレスが異なっていたので、両センサをI2C接続して測定しています。
下記の回路図では、XIAO ESP32C3のピン番号はGPIO番号を記載しています。詳細なピン配置は「Getting Started with Seeed Studio XIAO ESP32C3(Pinout diagram、power-pins)」を参照ください。
参考:
・https://sensirion.com/jp/products/product-catalog/SCD41/
・https://sensirion.com/jp/products/product-catalog/SCD30/
・Getting Started with Seeed Studio XIAO ESP32C3(Pinout diagram、power-pins)
・ILI9341搭載2.8インチSPI制御タッチパネル付TFT液晶(akizukidenshi.com)
・2.8inch SPI Module ILI9341 SKU-MSP2807 – LCD wiki
+ Product Documentation
+ 2.8 inch SPI Module Schematic(回路図)
開発ツール arduino-esp32のインストール
ESP32の開発ツール arduino-esp32を準備してスケッチを作っていきます。XIAO ESP32C3サイトにarduino-esp32のインストール手順の記載があります。
Getting Started with Seeed Studio XIAO ESP32C3
https://wiki.seeedstudio.com/XIAO_ESP32C3_Getting_Started/#software-setup
+ Software setup
当サイトでもESP32-DevKitCを例として、arduino-esp32 をArduino IDE 2.0.x版にインストールする手順メモを纏めました。
ボード(XIAO_ESP32C3を選択)とポート(当サイトのPCではCOM6)を切り替えることでXIAO ESP32C3でもそのまま使えます。
バージョン2.0.5へのダウングレードはバージョン3.0.3を削除(アンインストール)し、プルダウンから2.0.5を選んでインストール下さい。その後、Arduino IDEを再起動して下さい。
書き込み中にシリアルエラーが出た際は、Seeed Studio XIAO ESP32C3基板上のBボタンを押しながら電源を入れることで回避できることもあります。
Seeed StudioのWiki よくあるご質問(FAQ):
What to do when upload fails/the program runs abnormally/the device port is not found? | Seeed Studio Wiki
デバイスが認識されない問題やアップロードに失敗する問題の解決
また、書き込み後にLeaving… Hard resetting via RTS pin…の後も白画面のままになった場合、電源を入れ直すことで正常に液晶表示されることもありました。の後も白画面のままになった場合、電源を入れ直すことで正常に液晶表示されることもありました。
ライブラリのインクルード
先達の方々が開発されたライブラリをインクルードすることでスケッチ作成が容易になります。
TFT_eSPIライブラリのインクルード、User_Setup.hの編集
2.8インチ液晶モジュールを利用するにあたりTFT_eSPIライブラリのインクルードと初期設定を行います。
TFT_eSPIライブラリのインクルード
GitHubサイトからTFT_eSPIライブラリのzipファイル( TFT_eSPI-master.zip )をPCにダウンロードして保存(「Code」ボタンをクリックして「Download ZIP」)。Arduino IDEのメニューの「スケッチ」→「ライブラリをインクルード」→「.ZIP形式の ライブラリを インストール」で ダウンロードしたZIPファイルを追加します。
https://github.com/Bodmer/TFT_eSPI
User_Setup.hのGPIO番号を結線した環境に合わせて設定変更
Arduino IDEのメニューからTFT_eSPIライブラリのzipファイルをインクルードすると「User_Setup.h」はWinsowsでは下記フォルダ配下にありました。
C:\Users\[ユーザー名]\Documents\Arduino\libraries\TFT_eSPI-master
または、ライブラリのアップデートを行った際などに下記のフォルダ名になっている場合もあります。
C:\Users\[ユーザー名]\Documents\Arduino\libraries\TFT_eSPI
「User_Setup.h」の200行目付近の「// For ESP32 Dev board・・・」の下にある#defineを今回の環境用にピン番号を変更します。
左側がデフォルトのUser_Setup.h 設定、右側の黄色点線枠が環境に合わせてGPIO番号を変更した箇所です。左端の // を削除して上書き保存します。
SCD41センサ と SCD30センサ
SCD41は sparkfun/SparkFun_SCD4x_Arduino_Library サイトのライブラリを利用させていただきました。 サイトの「Code」プルダウンから「SparkFun_SCD4x_Arduino_Library-main.zip」をダウンロード。Arduino IDEのメニュー「スケッチ」–>「ライブラリをインクルード」–>「zip形式のライブラリをインクルード」でダウンロードしたzipファイルを指定します。
SCD30はsparkfun/SparkFun_SCD30_Arduino_Library サイトのライブラリを利用させていただきました。 サイトの「Code」プルダウンから「SparkFun_SCD30_Arduino_Library-main .zip」をダウンロードしてzip形式のライブラリをインクルード」でダウンロードしたzipファイルを指定します。
BME280センサ
BME280センサから測定データを取得するライブラリとして adafruit/Adafruit_BME280_Library と adafruit/Adafruit_Sensor を利用させていただきました。
TimeLib.h
Time.h、TimeLib.hライブラリは PaulStoffregen/Time を利用させていただきました。サイトの「Code」プルダウンから「Download ZIP」でPCに「Time-master.zip」をダウンロード。Arduino IDEのメニュー「スケッチ」–>「ライブラリをインクルード」–>「zip形式のライブラリをインクルード」で「Time-master.zip」を指定します。
esp_sntp.h
「esp_sntp.h」は arduino-esp32同梱モジュールです。詳細は Official develooment framework for ESP32 (ESP-IDF) に記述があります。mulong.meサイトを参考に内蔵時計の時刻が NTPサーバから取得した時刻に一致しているかの判定に使っています。
https://github.com/espressif/esp-idf/blob/4a011f3/components/lwip/include/apps/esp_sntp.h
スケッチ:2.8インチTFT液晶に日時、CO2濃度、気圧、気温、湿度を表示、WiFi接続してJSTに時刻合わせ
電源起動(スケッチ実行)時にWiFi接続して、情報通信研究機構(NICT)のNTPサーバに接続してXIAO ESP32C3の内蔵時計をJSTに時刻合わせします。加えて、1日1回(下記スケッチでは12時30分0秒)、NTPサーバに接続して時刻合わせを実行して精度を維持します。
時刻合わせ実行時には、WiFI接続、NTPサーバ接続、JST同期の進捗状況を表示しています。240×320の解像度があるので文字の描画も精細です。
SCD30センサのサンプリング期間は2秒、SCD41センサは5秒なので、現在のスケッチでは測定サイクルを10秒としています(起動時、測定タイミングになるまで2.8インチTFT液晶には測定値の表示なし)。
※SCD41センサと2.8インチTFT液晶は供給電流が大きいのでXIAO ESP32C3の3V3端子から個別にジャンパーワイヤで3.3Vを給電
SCD30とSCD41の2つのモジュールをI2Cに同時接続してみました。ストーブを焚いている部屋のCO2濃度は、両センサ共に2000ppm程度に上昇しました。
窓を全開にして換気すると徐々に500ppm前後まで下がっていきます。
SensirionサイトのDatasheetには、キャリブレーションとしてAutomatic Self Calibration (ASC)とForced recalibration (FRC)の説明があります。ASCではCO2濃度 400 ppmにさらすことを推奨していますがなかなかそのような環境は無いので、まずは自宅ベランダの外気で自動キャリブレーション中です。
参考:
・SCD41 | sensirion.com
Technical downloads
Datasheet SCD40/41 | sensirion.com (Field Calibration / FRC, ASC)
Low power operation SCD4x (Operation Modes / Sampling Period)
・ Sensirion/arduino-snippets | github.com
Examples by sensor
SCD4x_I2C_minimal_example (Minimal example to get started)
SCD4x_I2C_FRC_Forced_Recalibration_Example (Show forced recalibration)
・二酸化炭素濃度の経年変化 – 気象庁
スケッチ1:SCD41センサを接続
xiao-esp32c3_2.8TFT-LCD_SCD41_BME280.ino
※ここをクリックするとコード表示を開閉できます。
#include <Wire.h>
#include <SPI.h>
#include <WiFi.h>
#include <esp_sntp.h>
#include <TimeLib.h> // https://github.com/PaulStoffregen/Time
#include <TFT_eSPI.h> // https://github.com/Bodmer/TFT_eSPI
#include "SparkFun_SCD4x_Arduino_Library.h" // https://github.com/sparkfun/SparkFun_SCD4x_Arduino_Library
#include <Adafruit_Sensor.h> // https://github.com/adafruit/Adafruit_Sensor
#include <Adafruit_BME280.h> // https://github.com/adafruit/Adafruit_BME280_Library
TFT_eSPI tft = TFT_eSPI();
SCD4x mySensor;
float co2_41x;
Adafruit_BME280 bme;
bool status;
float pressure;
float temp;
float humid;
const char* ssid = "your ssid";
const char* password = "your password";
const char* ntpServer = "ntp.nict.jp";
const long gmtOffset_sec = 32400;
const int daylightOffset_sec = 0;
const char* weekStr[7] = {"(Sun)","(Mon)","(Tue)","(Wed)","(Thu)","(Fri)","(Sat)"};
char d_mes[12] ;
char t_mes[12] ;
struct tm *tm;
int d_year ;
int d_mon ;
int d_mday ;
int d_hour ;
int d_min ;
int d_sec ;
int d_wday ;
const unsigned char kii_bmp[] PROGMEM = {0x80, 0x01, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x1F, 0xF0, 0xFF, 0xFF, 0x1F, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x8E, 0xFF, 0xFF, 0x03, 0x86, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x01, 0xFC, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0x83, 0x01, 0xE0, 0xC0, 0x81, 0x01, 0xC0, 0xC3, 0x80, 0x01, 0x00, 0x67, 0x80, 0x01, 0x00, 0x7E, 0x80, 0x01, 0x00, 0x3C, 0x80, 0x01, 0x00, 0x7C, 0x80, 0x03, 0x00, 0xEF, 0x00, 0x43, 0x80, 0xC3, 0x01, 0x63, 0xE0, 0x81, 0x03, 0x63, 0xF8, 0x00, 0x07, 0x66, 0x3E, 0x00, 0x06, 0x7E, 0x0E, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x18, };
const unsigned char atu_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0xFF, 0x7F, 0xF0, 0xFF, 0xFF, 0x7F, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x0E, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0xFE, 0xFF, 0x1F, 0x30, 0xFE, 0xFF, 0x1F, 0x30, 0x00, 0x0E, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x38, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x0C, 0x00, 0x1C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0E, 0x00, 0x0C, 0x00, 0xCE, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
const unsigned char ccc_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0xF8, 0xC0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x78, 0xC0, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
const unsigned char ooo_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0x1E, 0x78, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x80, 0x07, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x01, 0x00, 0x03, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x03, 0x80, 0x03, 0x80, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x1E, 0x78, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
const unsigned char onn_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x38, 0xF8, 0xFF, 0x07, 0x70, 0xF8, 0xFF, 0x07, 0xE0, 0x18, 0x00, 0x06, 0xC0, 0x18, 0x00, 0x06, 0x00, 0x18, 0x00, 0x06, 0x00, 0xF8, 0xFF, 0x07, 0x00, 0xF8, 0xFF, 0x07, 0x02, 0x18, 0x00, 0x06, 0x07, 0x18, 0x00, 0x06, 0x1E, 0x18, 0x00, 0x06, 0x38, 0x38, 0x00, 0x06, 0x70, 0xF8, 0xFF, 0x07, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x3F, 0x00, 0x8C, 0xE3, 0x18, 0x60, 0x0C, 0x63, 0x18, 0x60, 0x0C, 0x63, 0x18, 0x70, 0x0C, 0x63, 0x18, 0x30, 0x0C, 0x63, 0x18, 0x30, 0x0C, 0x63, 0x18, 0x18, 0x0C, 0x63, 0x18, 0x18, 0x0C, 0x63, 0x18, 0x1C, 0x0C, 0x63, 0x18, 0x8C, 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
const unsigned char doo_bmp[] PROGMEM = {0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0xF0, 0xFF, 0xFF, 0x7F, 0xF0, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x00, 0x00, 0x30, 0x60, 0xC0, 0x00, 0x30, 0x60, 0xC0, 0x00, 0x30, 0x60, 0xC0, 0x00, 0x30, 0x60, 0xC0, 0x00, 0xB0, 0xFF, 0xFF, 0x7F, 0x30, 0xE7, 0xE0, 0x70, 0x30, 0x60, 0xC0, 0x00, 0x30, 0x60, 0xC0, 0x00, 0x30, 0x60, 0xC0, 0x00, 0x30, 0xE0, 0xFF, 0x00, 0x30, 0xE0, 0xFF, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0xFF, 0xFF, 0x03, 0x38, 0xFF, 0xFF, 0x07, 0x18, 0x30, 0x00, 0x03, 0x18, 0x60, 0x80, 0x01, 0x18, 0xE0, 0xC0, 0x01, 0x18, 0xC0, 0xE1, 0x00, 0x1C, 0x80, 0x7B, 0x00, 0x0C, 0x00, 0x1F, 0x00, 0x0E, 0x00, 0x3F, 0x00, 0x0E, 0xE0, 0xFB, 0x01, 0x06, 0xFE, 0xC0, 0x3F, 0xC6, 0x1F, 0x00, 0xFE, 0xC0, 0x01, 0x00, 0x60, };
const unsigned char deg_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x04, 0x10, 0x3C, 0xC0, 0x07, 0x08, 0x18, 0x24, 0xF0, 0x1F, 0x10, 0x0C, 0x42, 0x38, 0x18, 0x30, 0x04, 0x42, 0x18, 0x30, 0x20, 0x06, 0x24, 0x0C, 0x70, 0x60, 0x06, 0x18, 0x0C, 0x00, 0x40, 0x02, 0x00, 0x0C, 0x00, 0x40, 0x02, 0x00, 0x0E, 0x00, 0xC0, 0x02, 0x00, 0x06, 0x00, 0xC0, 0x02, 0x00, 0x06, 0x00, 0xC0, 0x02, 0x00, 0x0E, 0x00, 0xC0, 0x02, 0x00, 0x0E, 0x00, 0xC0, 0x02, 0x00, 0x0C, 0x00, 0x40, 0x06, 0x00, 0x0C, 0x60, 0x40, 0x04, 0x00, 0x1C, 0x30, 0x60, 0x0C, 0x00, 0x38, 0x38, 0x20, 0x08, 0x00, 0xF0, 0x1F, 0x30, 0x18, 0x00, 0xC0, 0x0F, 0x18, 0x30, 0x00, 0x00, 0x00, 0x08, 0x60, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, };
const unsigned char sit_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0xF8, 0xFF, 0x1F, 0x70, 0xF8, 0xFF, 0x1F, 0xE0, 0x18, 0x00, 0x18, 0xC0, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0xF8, 0xFF, 0x1F, 0x06, 0x18, 0x00, 0x18, 0x1E, 0x18, 0x00, 0x18, 0x3C, 0x18, 0x00, 0x18, 0x70, 0x18, 0x00, 0x18, 0x20, 0xF8, 0xFF, 0x1F, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x0C, 0x63, 0x30, 0x60, 0x0C, 0x43, 0x30, 0x60, 0x18, 0x43, 0x18, 0x70, 0x18, 0x43, 0x18, 0x30, 0x30, 0x43, 0x0C, 0x30, 0x30, 0x43, 0x0C, 0x38, 0x30, 0x43, 0x06, 0x18, 0x00, 0x63, 0x00, 0x1C, 0x00, 0x63, 0x00, 0x8C, 0xFF, 0xFF, 0xFF, 0x8E, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
void setup(void) {
Wire.begin(); // I2C初期化
tft.init(); // TFT液晶初期化
tft.setRotation(3);
tft.setTextSize(1);
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.drawString("Initializing TFT library", 0, 20, 4);
delay(1000);
//---------内蔵時計のJST同期(起動時)--------
wifisyncjst();
// BME280初期化
status = bme.begin(0x76);
while (!status) {
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.drawString("BME280 connection failed", 0, 110, 4);
}
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.drawString("BME280 connected", 0, 110, 4);
delay(1000);
//-------SCD41初期化
if (mySensor.begin() == false) {
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.drawString("SCD41 not detected", 0, 170, 4);
while (1);
}
//The SCD41 has data ready every five seconds
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.drawString("SCD41 detected", 0, 170, 4);
delay(5000);
// 画面クリア
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
// ----- 項目名を日本語で表示 -----
// 気圧(hPa)
tft.drawXBitmap(10, 0, kii_bmp, 32, 32, 0xFFFF);
tft.drawXBitmap(42, 0, atu_bmp, 32, 32, 0xFFFF);
tft.drawString("(hPa)", 250, 7, 4);
// CO2濃度(ppm)
tft.drawXBitmap(5, 47, ccc_bmp, 32, 32, 0xFFFF);
tft.drawXBitmap(30, 47, ooo_bmp, 32, 32, 0xFFFF);
tft.drawString("2", 62, 60, 4);
tft.drawString("(ppm)", 250, 53, 4);
// 温度(℃)
tft.drawXBitmap(10, 95, onn_bmp, 32, 32, 0xFFFF);
tft.drawXBitmap(42, 95, doo_bmp, 32, 32, 0xFFFF);
tft.drawXBitmap(250, 85, deg_bmp, 40, 40, 0xFFFF);
// 湿度(%)
tft.drawXBitmap(10, 142, sit_bmp, 32, 32, 0xFFFF);
tft.drawXBitmap(42, 142, doo_bmp, 32, 32, 0xFFFF);
tft.drawString("(%)", 250, 146, 4);
}
void loop() {
//---------内蔵時計の表示--------
time_t t = time(NULL);
tm = localtime(&t);
d_year = tm->tm_year+1900;
d_mon = tm->tm_mon+1;
d_mday = tm->tm_mday;
d_hour = tm->tm_hour;
d_min = tm->tm_min;
d_sec = tm->tm_sec;
d_wday = tm->tm_wday;
sprintf(d_mes, "%04d/%02d/%02d", d_year, d_mon, d_mday);
sprintf(t_mes, "%02d:%02d:%02d", d_hour, d_min, d_sec);
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(70, 190, 4);
tft.println(d_mes);
tft.setCursor(200, 190, 4);
tft.println(weekStr[d_wday]);
tft.setCursor(110, 215, 4);
tft.println(t_mes);
// -------- 10秒毎に測定してTFT表示 --------
if((String(d_sec) == "0") || (String(d_sec) == "10") ||
(String(d_sec) == "20") || (String(d_sec) == "30") ||
(String(d_sec) == "40") || (String(d_sec) == "50")) {
// ----- SCD41センサが稼働している時の処理 -----
if (mySensor.readMeasurement()) {
// ----- SCD41センサからデータ取得、測定値をTFT表示 -----
tft.setTextColor(TFT_GREEN, TFT_BLACK);
co2_41x=mySensor.getCO2();
tft.fillRect(105, 47, 140, 45, TFT_BLACK); // 残像消去
tft.drawFloat(co2_41x, 0, 105, 47, 6);
}
//----- BME280センサからデータ取得、測定値を表示 -----
pressure=bme.readPressure() / 100.0F;
temp=bme.readTemperature();
humid=bme.readHumidity();
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.fillRect(105, 0, 140, 45, TFT_BLACK); // 気圧の4桁→3桁時の残像消去
tft.drawFloat(pressure, 0, 105, 0, 6);
tft.drawFloat(temp, 1, 105, 93, 6);
tft.drawFloat(humid, 1, 105, 140, 6);
} //10秒毎ここまで
//---------内蔵時計のJST同期(1日1回、12時30分0秒に実行)--------
if ((String(d_hour) == "12") && (String(d_min) == "30") && (String(d_sec) == "0")) {
wifisyncjst();
tft.fillRect(105, 210, 205, 25, TFT_BLACK); // 残像消去
}
delay(200);
}
void wifisyncjst() {
//---------内蔵時計のJST同期--------
// WiFi接続
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED) {
tft.drawString("WiFi bigin", 110, 215, 4);
}
delay(1000);
// WiFi接続の表示
tft.drawString("WiFi connected", 110, 215, 4);
delay(1000);
// NTPサーバからJST取得
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
tft.drawString("JST synchronized", 110, 215, 4);
delay(1000);
// 内蔵RTCの時刻がNTP時刻に合うまで待機
while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET) {
delay(1000);
}
//WiFi切断
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}
スケッチ2:SCD30センサを接続
xiao-esp32c3_2.8TFT-LCD_SCD30_BME280.ino
※ここをクリックするとコード表示を開閉できます。
#include <Wire.h>
#include <SPI.h>
#include <WiFi.h>
#include <esp_sntp.h>
#include <TimeLib.h> // https://github.com/PaulStoffregen/Time
#include <TFT_eSPI.h> // https://github.com/Bodmer/TFT_eSPI
#include "SparkFun_SCD30_Arduino_Library.h" // https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library
#include <Adafruit_Sensor.h> // https://github.com/adafruit/Adafruit_Sensor
#include <Adafruit_BME280.h> // https://github.com/adafruit/Adafruit_BME280_Library
TFT_eSPI tft = TFT_eSPI();
SCD30 airSensor;
float co2_tmp;
Adafruit_BME280 bme;
bool status;
float pressure;
float temp;
float humid;
const char* ssid = "your ssid";
const char* password = "your password";
const char* ntpServer = "ntp.nict.jp";
const long gmtOffset_sec = 32400;
const int daylightOffset_sec = 0;
const char* weekStr[7] = {"(Sun)","(Mon)","(Tue)","(Wed)","(Thu)","(Fri)","(Sat)"};
char d_mes[12] ;
char t_mes[12] ;
struct tm *tm;
int d_year ;
int d_mon ;
int d_mday ;
int d_hour ;
int d_min ;
int d_sec ;
int d_wday ;
const unsigned char kii_bmp[] PROGMEM = {0x80, 0x01, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x1F, 0xF0, 0xFF, 0xFF, 0x1F, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x8E, 0xFF, 0xFF, 0x03, 0x86, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x01, 0xFC, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0x83, 0x01, 0xE0, 0xC0, 0x81, 0x01, 0xC0, 0xC3, 0x80, 0x01, 0x00, 0x67, 0x80, 0x01, 0x00, 0x7E, 0x80, 0x01, 0x00, 0x3C, 0x80, 0x01, 0x00, 0x7C, 0x80, 0x03, 0x00, 0xEF, 0x00, 0x43, 0x80, 0xC3, 0x01, 0x63, 0xE0, 0x81, 0x03, 0x63, 0xF8, 0x00, 0x07, 0x66, 0x3E, 0x00, 0x06, 0x7E, 0x0E, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x18, };
const unsigned char atu_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0xFF, 0x7F, 0xF0, 0xFF, 0xFF, 0x7F, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x0E, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0xFE, 0xFF, 0x1F, 0x30, 0xFE, 0xFF, 0x1F, 0x30, 0x00, 0x0E, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x38, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x0C, 0x00, 0x1C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0E, 0x00, 0x0C, 0x00, 0xCE, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
const unsigned char ccc_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0xF8, 0xC0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x78, 0xC0, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
const unsigned char ooo_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0x1E, 0x78, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x80, 0x07, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x01, 0x00, 0x03, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x03, 0x80, 0x03, 0x80, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x1E, 0x78, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
const unsigned char onn_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x38, 0xF8, 0xFF, 0x07, 0x70, 0xF8, 0xFF, 0x07, 0xE0, 0x18, 0x00, 0x06, 0xC0, 0x18, 0x00, 0x06, 0x00, 0x18, 0x00, 0x06, 0x00, 0xF8, 0xFF, 0x07, 0x00, 0xF8, 0xFF, 0x07, 0x02, 0x18, 0x00, 0x06, 0x07, 0x18, 0x00, 0x06, 0x1E, 0x18, 0x00, 0x06, 0x38, 0x38, 0x00, 0x06, 0x70, 0xF8, 0xFF, 0x07, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x3F, 0x00, 0x8C, 0xE3, 0x18, 0x60, 0x0C, 0x63, 0x18, 0x60, 0x0C, 0x63, 0x18, 0x70, 0x0C, 0x63, 0x18, 0x30, 0x0C, 0x63, 0x18, 0x30, 0x0C, 0x63, 0x18, 0x18, 0x0C, 0x63, 0x18, 0x18, 0x0C, 0x63, 0x18, 0x1C, 0x0C, 0x63, 0x18, 0x8C, 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
const unsigned char doo_bmp[] PROGMEM = {0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0xF0, 0xFF, 0xFF, 0x7F, 0xF0, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x00, 0x00, 0x30, 0x60, 0xC0, 0x00, 0x30, 0x60, 0xC0, 0x00, 0x30, 0x60, 0xC0, 0x00, 0x30, 0x60, 0xC0, 0x00, 0xB0, 0xFF, 0xFF, 0x7F, 0x30, 0xE7, 0xE0, 0x70, 0x30, 0x60, 0xC0, 0x00, 0x30, 0x60, 0xC0, 0x00, 0x30, 0x60, 0xC0, 0x00, 0x30, 0xE0, 0xFF, 0x00, 0x30, 0xE0, 0xFF, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0xFF, 0xFF, 0x03, 0x38, 0xFF, 0xFF, 0x07, 0x18, 0x30, 0x00, 0x03, 0x18, 0x60, 0x80, 0x01, 0x18, 0xE0, 0xC0, 0x01, 0x18, 0xC0, 0xE1, 0x00, 0x1C, 0x80, 0x7B, 0x00, 0x0C, 0x00, 0x1F, 0x00, 0x0E, 0x00, 0x3F, 0x00, 0x0E, 0xE0, 0xFB, 0x01, 0x06, 0xFE, 0xC0, 0x3F, 0xC6, 0x1F, 0x00, 0xFE, 0xC0, 0x01, 0x00, 0x60, };
const unsigned char deg_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x04, 0x10, 0x3C, 0xC0, 0x07, 0x08, 0x18, 0x24, 0xF0, 0x1F, 0x10, 0x0C, 0x42, 0x38, 0x18, 0x30, 0x04, 0x42, 0x18, 0x30, 0x20, 0x06, 0x24, 0x0C, 0x70, 0x60, 0x06, 0x18, 0x0C, 0x00, 0x40, 0x02, 0x00, 0x0C, 0x00, 0x40, 0x02, 0x00, 0x0E, 0x00, 0xC0, 0x02, 0x00, 0x06, 0x00, 0xC0, 0x02, 0x00, 0x06, 0x00, 0xC0, 0x02, 0x00, 0x0E, 0x00, 0xC0, 0x02, 0x00, 0x0E, 0x00, 0xC0, 0x02, 0x00, 0x0C, 0x00, 0x40, 0x06, 0x00, 0x0C, 0x60, 0x40, 0x04, 0x00, 0x1C, 0x30, 0x60, 0x0C, 0x00, 0x38, 0x38, 0x20, 0x08, 0x00, 0xF0, 0x1F, 0x30, 0x18, 0x00, 0xC0, 0x0F, 0x18, 0x30, 0x00, 0x00, 0x00, 0x08, 0x60, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, };
const unsigned char sit_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0xF8, 0xFF, 0x1F, 0x70, 0xF8, 0xFF, 0x1F, 0xE0, 0x18, 0x00, 0x18, 0xC0, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0xF8, 0xFF, 0x1F, 0x06, 0x18, 0x00, 0x18, 0x1E, 0x18, 0x00, 0x18, 0x3C, 0x18, 0x00, 0x18, 0x70, 0x18, 0x00, 0x18, 0x20, 0xF8, 0xFF, 0x1F, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x0C, 0x63, 0x30, 0x60, 0x0C, 0x43, 0x30, 0x60, 0x18, 0x43, 0x18, 0x70, 0x18, 0x43, 0x18, 0x30, 0x30, 0x43, 0x0C, 0x30, 0x30, 0x43, 0x0C, 0x38, 0x30, 0x43, 0x06, 0x18, 0x00, 0x63, 0x00, 0x1C, 0x00, 0x63, 0x00, 0x8C, 0xFF, 0xFF, 0xFF, 0x8E, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
void setup(void) {
// TFT液晶初期化
tft.init();
tft.setRotation(3);
tft.setTextSize(1);
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.drawString("Initializing TFT library", 0, 20, 4);
delay(1000);
//---------内蔵時計のJST同期(起動時)--------
wifisyncjst();
// BME280初期化
status = bme.begin(0x76);
while (!status) {
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.drawString("BME280 connection failed", 0, 140, 4);
}
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.drawString("BME280 connected", 0, 140, 4);
delay(1000);
// SCD30初期化
Wire.begin();
if (airSensor.begin() == false) {
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.drawString("SCD30 not detected", 0, 170, 4);
while (1);
}
//The SCD30 has data ready every two seconds
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.drawString("SCD30 detected", 0, 170, 4);
delay(2000);
// 画面クリア
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
// ----- 項目名を日本語で表示 -----
// 気圧(hPa)
tft.drawXBitmap(10, 0, kii_bmp, 32, 32, 0xFFFF);
tft.drawXBitmap(42, 0, atu_bmp, 32, 32, 0xFFFF);
tft.drawString("(hPa)", 250, 7, 4);
// CO2濃度(ppm)
tft.drawXBitmap(5, 47, ccc_bmp, 32, 32, 0xFFFF);
tft.drawXBitmap(30, 47, ooo_bmp, 32, 32, 0xFFFF);
tft.drawString("2", 62, 60, 4);
tft.drawString("(ppm)", 250, 53, 4);
// 温度(℃)
tft.drawXBitmap(10, 95, onn_bmp, 32, 32, 0xFFFF);
tft.drawXBitmap(42, 95, doo_bmp, 32, 32, 0xFFFF);
tft.drawXBitmap(250, 85, deg_bmp, 40, 40, 0xFFFF);
// 湿度(%)
tft.drawXBitmap(10, 142, sit_bmp, 32, 32, 0xFFFF);
tft.drawXBitmap(42, 142, doo_bmp, 32, 32, 0xFFFF);
tft.drawString("(%)", 250, 146, 4);
}
void loop() {
//---------内蔵時計の表示--------
time_t t = time(NULL);
tm = localtime(&t);
d_year = tm->tm_year+1900;
d_mon = tm->tm_mon+1;
d_mday = tm->tm_mday;
d_hour = tm->tm_hour;
d_min = tm->tm_min;
d_sec = tm->tm_sec;
d_wday = tm->tm_wday;
sprintf(d_mes, "%04d/%02d/%02d", d_year, d_mon, d_mday);
sprintf(t_mes, "%02d:%02d:%02d", d_hour, d_min, d_sec);
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(70, 190, 4);
tft.println(d_mes);
tft.setCursor(200, 190, 4);
tft.println(weekStr[d_wday]);
tft.setCursor(110, 215, 4);
tft.println(t_mes);
// -------- 10秒毎に測定してTFT表示 --------
if((String(d_sec) == "0") || (String(d_sec) == "10") ||
(String(d_sec) == "20") || (String(d_sec) == "30") ||
(String(d_sec) == "40") || (String(d_sec) == "50")) {
// ----- SCD30センサが稼働している時の処理 -----
if (airSensor.dataAvailable()) {
// ----- 液晶画面に測定値を表示 -----
tft.setTextColor(TFT_WHITE, TFT_BLACK);
// ----- SCD30センサからデータ取得、測定値を表示 -----
co2_tmp=airSensor.getCO2();
tft.fillRect(105, 47, 140, 45, TFT_BLACK); // 残像消去
tft.drawFloat(co2_tmp, 0, 105, 47, 6);
// ----- BME280センサからデータ取得、測定値を表示 -----
pressure=bme.readPressure() / 100.0F;
temp=bme.readTemperature();
humid=bme.readHumidity();
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.fillRect(105, 0, 140, 45, TFT_BLACK); // 残像消去
tft.drawFloat(pressure, 0, 105, 0, 6);
tft.drawFloat(temp, 1, 105, 93, 6);
tft.drawFloat(humid, 1, 105, 140, 6);
} //dataAvailable_SCD30ここまで
} //10秒毎ここまで
//---------内蔵時計のJST同期(1日1回、12時30分0秒に実行)--------
if ((String(d_hour) == "12") && (String(d_min) == "30") && (String(d_sec) == "0")) {
wifisyncjst();
tft.fillRect(105, 210, 205, 25, TFT_BLACK); // 残像消去
}
delay(200);
}
void wifisyncjst() {
//---------内蔵時計のJST同期--------
// WiFi接続
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED) {
tft.drawString("WiFi bigin", 110, 215, 4);
}
delay(1000);
// WiFi接続の表示
tft.drawString("WiFi connected", 110, 215, 4);
delay(1000);
// NTPサーバからJST取得
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
tft.drawString("JST synchronized", 110, 215, 4);
delay(1000);
// 内蔵時計の時刻がNTP時刻に合うまで待機
while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET) {
delay(1000);
}
//WiFi切断
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}
スケッチ3:SCD30とSCD41を同時接続、表示するセンサをdisp_setで指定
スケッチ中の disp_set の設定値を変えることで表示するセンサを切り替えます( 1:SCD30のみ表示(白文字)、2:SCD41のみ表示(緑文字)、3:SCD30とSCD41の両方を表示)。
xiao-esp32c3_2.8TFT-LCD_SCD30_SCD41_BME280.ino
※ここをクリックするとコード表示を開閉できます。
#include <Wire.h>
#include <SPI.h>
#include <WiFi.h>
#include <esp_sntp.h>
#include <TimeLib.h> // https://github.com/PaulStoffregen/Time
#include <TFT_eSPI.h> // https://github.com/Bodmer/TFT_eSPI
#include "SparkFun_SCD30_Arduino_Library.h" // https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library
#include "SparkFun_SCD4x_Arduino_Library.h" // https://github.com/sparkfun/SparkFun_SCD4x_Arduino_Library
#include <Adafruit_Sensor.h> // https://github.com/adafruit/Adafruit_Sensor
#include <Adafruit_BME280.h> // https://github.com/adafruit/Adafruit_BME280_Library
TFT_eSPI tft = TFT_eSPI();
SCD30 airSensor;
float co2_tmp;
SCD4x mySensor;
float co2_41x;
int disp_set = 3 ; // 1:SCD30のみ表示(白文字)、2:SCD41のみ表示(緑文字)、3:SCD30とSCD41の両方を表示
Adafruit_BME280 bme;
bool status;
float pressure;
float temp;
float humid;
const char* ssid = "your ssid";
const char* password = "your password";
const char* ntpServer = "ntp.nict.jp";
const long gmtOffset_sec = 32400;
const int daylightOffset_sec = 0;
const char* weekStr[7] = {"(Sun)","(Mon)","(Tue)","(Wed)","(Thu)","(Fri)","(Sat)"};
char d_mes[12] ;
char t_mes[12] ;
struct tm *tm;
int d_year ;
int d_mon ;
int d_mday ;
int d_hour ;
int d_min ;
int d_sec ;
int d_wday ;
const unsigned char kii_bmp[] PROGMEM = {0x80, 0x01, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x1F, 0xF0, 0xFF, 0xFF, 0x1F, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x8E, 0xFF, 0xFF, 0x03, 0x86, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x01, 0xFC, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0x83, 0x01, 0xE0, 0xC0, 0x81, 0x01, 0xC0, 0xC3, 0x80, 0x01, 0x00, 0x67, 0x80, 0x01, 0x00, 0x7E, 0x80, 0x01, 0x00, 0x3C, 0x80, 0x01, 0x00, 0x7C, 0x80, 0x03, 0x00, 0xEF, 0x00, 0x43, 0x80, 0xC3, 0x01, 0x63, 0xE0, 0x81, 0x03, 0x63, 0xF8, 0x00, 0x07, 0x66, 0x3E, 0x00, 0x06, 0x7E, 0x0E, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x18, };
const unsigned char atu_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0xFF, 0x7F, 0xF0, 0xFF, 0xFF, 0x7F, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x0E, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0xFE, 0xFF, 0x1F, 0x30, 0xFE, 0xFF, 0x1F, 0x30, 0x00, 0x0E, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x38, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x0C, 0x00, 0x1C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0E, 0x00, 0x0C, 0x00, 0xCE, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
const unsigned char ccc_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0xF8, 0xC0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x78, 0xC0, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
const unsigned char ooo_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0x1E, 0x78, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x80, 0x07, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x01, 0x00, 0x03, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x03, 0x80, 0x03, 0x80, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x1E, 0x78, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
const unsigned char onn_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x38, 0xF8, 0xFF, 0x07, 0x70, 0xF8, 0xFF, 0x07, 0xE0, 0x18, 0x00, 0x06, 0xC0, 0x18, 0x00, 0x06, 0x00, 0x18, 0x00, 0x06, 0x00, 0xF8, 0xFF, 0x07, 0x00, 0xF8, 0xFF, 0x07, 0x02, 0x18, 0x00, 0x06, 0x07, 0x18, 0x00, 0x06, 0x1E, 0x18, 0x00, 0x06, 0x38, 0x38, 0x00, 0x06, 0x70, 0xF8, 0xFF, 0x07, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x3F, 0x00, 0x8C, 0xE3, 0x18, 0x60, 0x0C, 0x63, 0x18, 0x60, 0x0C, 0x63, 0x18, 0x70, 0x0C, 0x63, 0x18, 0x30, 0x0C, 0x63, 0x18, 0x30, 0x0C, 0x63, 0x18, 0x18, 0x0C, 0x63, 0x18, 0x18, 0x0C, 0x63, 0x18, 0x1C, 0x0C, 0x63, 0x18, 0x8C, 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
const unsigned char doo_bmp[] PROGMEM = {0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0xF0, 0xFF, 0xFF, 0x7F, 0xF0, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x00, 0x00, 0x30, 0x60, 0xC0, 0x00, 0x30, 0x60, 0xC0, 0x00, 0x30, 0x60, 0xC0, 0x00, 0x30, 0x60, 0xC0, 0x00, 0xB0, 0xFF, 0xFF, 0x7F, 0x30, 0xE7, 0xE0, 0x70, 0x30, 0x60, 0xC0, 0x00, 0x30, 0x60, 0xC0, 0x00, 0x30, 0x60, 0xC0, 0x00, 0x30, 0xE0, 0xFF, 0x00, 0x30, 0xE0, 0xFF, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0xFF, 0xFF, 0x03, 0x38, 0xFF, 0xFF, 0x07, 0x18, 0x30, 0x00, 0x03, 0x18, 0x60, 0x80, 0x01, 0x18, 0xE0, 0xC0, 0x01, 0x18, 0xC0, 0xE1, 0x00, 0x1C, 0x80, 0x7B, 0x00, 0x0C, 0x00, 0x1F, 0x00, 0x0E, 0x00, 0x3F, 0x00, 0x0E, 0xE0, 0xFB, 0x01, 0x06, 0xFE, 0xC0, 0x3F, 0xC6, 0x1F, 0x00, 0xFE, 0xC0, 0x01, 0x00, 0x60, };
const unsigned char deg_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x04, 0x10, 0x3C, 0xC0, 0x07, 0x08, 0x18, 0x24, 0xF0, 0x1F, 0x10, 0x0C, 0x42, 0x38, 0x18, 0x30, 0x04, 0x42, 0x18, 0x30, 0x20, 0x06, 0x24, 0x0C, 0x70, 0x60, 0x06, 0x18, 0x0C, 0x00, 0x40, 0x02, 0x00, 0x0C, 0x00, 0x40, 0x02, 0x00, 0x0E, 0x00, 0xC0, 0x02, 0x00, 0x06, 0x00, 0xC0, 0x02, 0x00, 0x06, 0x00, 0xC0, 0x02, 0x00, 0x0E, 0x00, 0xC0, 0x02, 0x00, 0x0E, 0x00, 0xC0, 0x02, 0x00, 0x0C, 0x00, 0x40, 0x06, 0x00, 0x0C, 0x60, 0x40, 0x04, 0x00, 0x1C, 0x30, 0x60, 0x0C, 0x00, 0x38, 0x38, 0x20, 0x08, 0x00, 0xF0, 0x1F, 0x30, 0x18, 0x00, 0xC0, 0x0F, 0x18, 0x30, 0x00, 0x00, 0x00, 0x08, 0x60, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, };
const unsigned char sit_bmp[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0xF8, 0xFF, 0x1F, 0x70, 0xF8, 0xFF, 0x1F, 0xE0, 0x18, 0x00, 0x18, 0xC0, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0xF8, 0xFF, 0x1F, 0x06, 0x18, 0x00, 0x18, 0x1E, 0x18, 0x00, 0x18, 0x3C, 0x18, 0x00, 0x18, 0x70, 0x18, 0x00, 0x18, 0x20, 0xF8, 0xFF, 0x1F, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x0C, 0x63, 0x30, 0x60, 0x0C, 0x43, 0x30, 0x60, 0x18, 0x43, 0x18, 0x70, 0x18, 0x43, 0x18, 0x30, 0x30, 0x43, 0x0C, 0x30, 0x30, 0x43, 0x0C, 0x38, 0x30, 0x43, 0x06, 0x18, 0x00, 0x63, 0x00, 0x1C, 0x00, 0x63, 0x00, 0x8C, 0xFF, 0xFF, 0xFF, 0x8E, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
void setup(void) {
Wire.begin(); // I2C初期化
tft.init(); // TFT液晶初期化
tft.setRotation(3);
tft.setTextSize(1);
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.drawString("Initializing TFT library", 0, 20, 4);
delay(1000);
//---------内蔵時計のJST同期(起動時)--------
wifisyncjst();
// BME280初期化
status = bme.begin(0x76);
while (!status) {
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.drawString("BME280 connection failed", 0, 110, 4);
}
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.drawString("BME280 connected", 0, 110, 4);
delay(1000);
if ( String(disp_set) == "1" || String(disp_set) == "3" ) { //SCD30のみ or 両方表示
//-------SCD30初期化
if (airSensor.begin() == false) {
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.drawString("SCD30 not detected", 0, 140, 4);
while (1);
}
//The SCD30 has data ready every two seconds
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.drawString("SCD30 detected", 0, 140, 4);
delay(2000);
}
if ( String(disp_set) == "2" || String(disp_set) == "3" ) { //SCD41のみ or 両方表示
//-------SCD41初期化
if (mySensor.begin() == false) {
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.drawString("SCD41 not detected", 0, 170, 4);
while (1);
}
//The SCD41 has data ready every five seconds
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.drawString("SCD41 detected", 0, 170, 4);
delay(5000);
}
// 画面クリア
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
// ----- 項目名を日本語で表示 -----
// 気圧(hPa)
tft.drawXBitmap(10, 0, kii_bmp, 32, 32, 0xFFFF);
tft.drawXBitmap(42, 0, atu_bmp, 32, 32, 0xFFFF);
tft.drawString("(hPa)", 250, 7, 4);
// CO2濃度(ppm)
tft.drawXBitmap(5, 47, ccc_bmp, 32, 32, 0xFFFF);
tft.drawXBitmap(30, 47, ooo_bmp, 32, 32, 0xFFFF);
tft.drawString("2", 62, 60, 4);
tft.drawString("(ppm)", 250, 53, 4);
// 温度(℃)
tft.drawXBitmap(10, 95, onn_bmp, 32, 32, 0xFFFF);
tft.drawXBitmap(42, 95, doo_bmp, 32, 32, 0xFFFF);
tft.drawXBitmap(250, 85, deg_bmp, 40, 40, 0xFFFF);
// 湿度(%)
tft.drawXBitmap(10, 142, sit_bmp, 32, 32, 0xFFFF);
tft.drawXBitmap(42, 142, doo_bmp, 32, 32, 0xFFFF);
tft.drawString("(%)", 250, 146, 4);
}
void loop() {
//---------内蔵時計の表示--------
time_t t = time(NULL);
tm = localtime(&t);
d_year = tm->tm_year+1900;
d_mon = tm->tm_mon+1;
d_mday = tm->tm_mday;
d_hour = tm->tm_hour;
d_min = tm->tm_min;
d_sec = tm->tm_sec;
d_wday = tm->tm_wday;
sprintf(d_mes, "%04d/%02d/%02d", d_year, d_mon, d_mday);
sprintf(t_mes, "%02d:%02d:%02d", d_hour, d_min, d_sec);
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(70, 190, 4);
tft.println(d_mes);
tft.setCursor(200, 190, 4);
tft.println(weekStr[d_wday]);
tft.setCursor(110, 215, 4);
tft.println(t_mes);
// -------- 10秒毎に測定してTFT表示 --------
if((String(d_sec) == "0") || (String(d_sec) == "10") ||
(String(d_sec) == "20") || (String(d_sec) == "30") ||
(String(d_sec) == "40") || (String(d_sec) == "50")) {
// ----- 2つのCO2センサの切り替え表示 :disp_setで分岐
if((String(disp_set)) == "1") { //SCD30のみ表示
// ----- SCD30センサが稼働している時の処理 -----
if (airSensor.dataAvailable()) {
// ----- SCD30センサからデータ取得、測定値を表示 -----
tft.setTextColor(TFT_WHITE, TFT_BLACK);
co2_tmp=airSensor.getCO2();
tft.fillRect(105, 47, 140, 45, TFT_BLACK); // 残像消去
tft.drawFloat(co2_tmp, 0, 105, 47, 6);
}
} else if ((String(disp_set)) == "2") { //SCD41のみ表示
// ----- SCD41センサが稼働している時の処理 -----
if (mySensor.readMeasurement()) {
// ----- SCD41センサからデータ取得、測定値をTFT表示 -----
tft.setTextColor(TFT_GREEN, TFT_BLACK);
co2_41x=mySensor.getCO2();
tft.fillRect(105, 47, 140, 45, TFT_BLACK); // 残像消去
tft.drawFloat(co2_41x, 0, 105, 47, 6);
}
} else if ((String(disp_set)) == "3") { //SCD30とSCD41の両方を表示
// ----- SCD30センサは2秒毎、SCD41センサは5秒毎のため、両センサのandで稼働確認 -----
if (airSensor.dataAvailable() && mySensor.readMeasurement()) {
tft.fillRect(80, 47, 240, 45, TFT_BLACK); // 残像消去
// ----- SCD30センサからデータ取得、測定値を表示 -----
tft.setTextColor(TFT_WHITE, TFT_BLACK);
co2_tmp=airSensor.getCO2();
tft.drawFloat(co2_tmp, 0, 85, 47, 6);
// ----- SCD41センサからデータ取得、測定値をTFT表示 -----
tft.setTextColor(TFT_GREEN, TFT_BLACK);
co2_41x=mySensor.getCO2();
tft.drawFloat(co2_41x, 0, 210, 47, 6);
}
} //SCDセンサ判定終了
//----- BME280センサからデータ取得、測定値を表示 -----
pressure=bme.readPressure() / 100.0F;
temp=bme.readTemperature();
humid=bme.readHumidity();
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.fillRect(105, 0, 140, 45, TFT_BLACK); // 気圧の4桁→3桁時の残像消去
tft.drawFloat(pressure, 0, 105, 0, 6);
tft.drawFloat(temp, 1, 105, 93, 6);
tft.drawFloat(humid, 1, 105, 140, 6);
} //10秒毎ここまで
//---------内蔵時計のJST同期(1日1回、12時30分0秒に実行)--------
if ((String(d_hour) == "12") && (String(d_min) == "30") && (String(d_sec) == "0")) {
wifisyncjst();
tft.fillRect(105, 210, 205, 25, TFT_BLACK); // 残像消去
}
delay(200);
}
void wifisyncjst() {
//---------内蔵時計のJST同期--------
// WiFi接続
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED) {
tft.drawString("WiFi bigin", 110, 215, 4);
}
delay(1000);
// WiFi接続の表示
tft.drawString("WiFi connected", 110, 215, 4);
delay(1000);
// NTPサーバからJST取得
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
tft.drawString("JST synchronized", 110, 215, 4);
delay(1000);
// 内蔵RTCの時刻がNTP時刻に合うまで待機
while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET) {
delay(1000);
}
//WiFi切断
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}