概要
Sony Spresense と ELTRES アドオンボードを使い取得した温湿度をクラウドに送信。
数値の見える化を行いました。
必要機材
ハードウェア
- Spresense メインボード
- ELTRES アドオンボード
- DHT20 温湿度センサー
- ブレッドボード & ジャンパーワイヤー
ソフトウェア
- Arduino IDE(バージョン2.3.4で実施)
- ELTRES アドオンボード用ライブラリ(1.3.0)
- Adafruit AHTX0 ライブラリ
配線図
製作開始
Arduino IDE へのライブラリ追加
使用するライブラリについては、
Arduino IDE の「ツール」->「ライブラリの管理」を開いて入手していきます。
また、ELTRES アドオンボードについては、CLIP Viewer Liteコンテンツページにライブラリの記載がありますのでそこから入手を行いました。
使用するセンサについて
今回はセンサとしてDHT20を使用しました。類似品としてDHT11というセンサもあります。
基本的には同じものですがセンサ精度がDHT20の方が良いものとなっています。
項目 | DHT11 | DHT20 |
---|---|---|
測定範囲(温度) | 0〜50°C | -40〜80°C |
測定範囲(湿度) | 20〜90% RH | 0〜100% RH |
精度(温度) | ±2°C | ±0.3°C |
精度(湿度) | ±5% RH | ±3% RH |
ソースコード
サンプルコードをもとに使用するセンサの情報を追記して組み立てていきました。
センサ情報の取得とデータ送信
#include <EltresAddonBoard.h>
// PIN定義:LED(プログラム状態)
#define LED_RUN PIN_LED0
// PIN定義:LED(GNSS電波状態)
#define LED_GNSS PIN_LED1
// PIN定義:LED(ELTRES状態)
#define LED_SND PIN_LED2
// PIN定義:LED(エラー状態)
#define LED_ERR PIN_LED3
// プログラム内部状態:初期状態
#define PROGRAM_STS_INIT (0)
// プログラム内部状態:起動中
#define PROGRAM_STS_RUNNING (1)
// プログラム内部状態:終了
#define PROGRAM_STS_STOPPED (3)
// DHT20(温湿度センサ)設定
#include <Adafruit_AHTX0.h> // DHT20用ライブラリ
Adafruit_AHTX0 dht20;
//確認用LEDのピン
#define SIGNAL_PIN1 22
// 直前の温度データ
float lastTemperature = 0.0;
// 測定間隔(1分ごと)
#define MEASURE_INTERVAL 60000
unsigned long lastMeasureTime = 0;
// プログラム内部状態
int program_sts = PROGRAM_STS_INIT;
// GNSS電波受信タイムアウト(GNSS受信エラー)発生フラグ
bool gnss_recevie_timeout = false;
// 点滅処理で最後に変更した時間
uint64_t last_change_blink_time = 0;
// イベント通知での送信直前通知(5秒前)受信フラグ
bool event_send_ready = false;
// ペイロードデータ格納場所
uint8_t payload[16];
// 最新のGGA情報
eltres_board_gga_info last_gga_info;
// 初期値の設定
float last_temp = 0; // 最新値(温度)
float last_hum = 0; // 最新値(湿度)
uint16_t last_co2 = 0; // 最新値(CO2濃度)今回は使用なし
/**
* @brief イベント通知受信コールバック
* @param event イベント種別
*/
void eltres_event_cb(eltres_board_event event) {
switch (event) {
case ELTRES_BOARD_EVT_GNSS_TMOUT:
// GNSS電波受信タイムアウト
Serial.println("gnss wait timeout error.");
gnss_recevie_timeout = true;
break;
case ELTRES_BOARD_EVT_IDLE:
// アイドル状態
Serial.println("waiting sending timings.");
digitalWrite(LED_SND, LOW);
break;
case ELTRES_BOARD_EVT_SEND_READY:
// 送信直前通知(5秒前)
Serial.println("Shortly before sending, so setup payload if need.");
event_send_ready = true;
break;
case ELTRES_BOARD_EVT_SENDING:
// 送信開始
Serial.println("start sending.");
digitalWrite(LED_SND, HIGH);
break;
case ELTRES_BOARD_EVT_GNSS_UNRECEIVE:
// GNSS電波未受信
Serial.println("gnss wave has not been received.");
digitalWrite(LED_GNSS, LOW);
break;
case ELTRES_BOARD_EVT_GNSS_RECEIVE:
// GNSS電波受信
Serial.println("gnss wave has been received.");
digitalWrite(LED_GNSS, HIGH);
gnss_recevie_timeout = false;
break;
case ELTRES_BOARD_EVT_FAULT:
// 内部エラー発生
Serial.println("internal error.");
break;
}
}
/**
* @brief GGA情報受信コールバック
* @param gga_info GGA情報のポインタ
*/
void gga_event_cb(const eltres_board_gga_info *gga_info) {
Serial.print("[gga]");
last_gga_info = *gga_info;
if (gga_info->m_pos_status) {
// 測位状態
// GGA情報をシリアルモニタへ出力
Serial.print("utc: ");
Serial.println((const char *)gga_info->m_utc);
Serial.print("lat: ");
Serial.print((const char *)gga_info->m_n_s);
Serial.print((const char *)gga_info->m_lat);
Serial.print(", lon: ");
Serial.print((const char *)gga_info->m_e_w);
Serial.println((const char *)gga_info->m_lon);
Serial.print("pos_status: ");
Serial.print(gga_info->m_pos_status);
Serial.print(", sat_used: ");
Serial.println(gga_info->m_sat_used);
Serial.print("hdop: ");
Serial.print(gga_info->m_hdop);
Serial.print(", height: ");
Serial.print(gga_info->m_height);
Serial.print(" m, geoid: ");
Serial.print(gga_info->m_geoid);
Serial.println(" m");
} else {
// 非測位状態
// "invalid data"をシリアルモニタへ出力
Serial.println("invalid data.");
}
}
/**
* @brief setup()関数
*/
void setup() {
// シリアルモニタ出力設定
Serial.begin(115200);
Serial.println("initialize start.");
Serial.println("Serial OK. -->> NEXT ELTRES init");
Wire.begin();
// LED初期設定
pinMode(LED_RUN, OUTPUT);
digitalWrite(LED_RUN, HIGH);
pinMode(LED_GNSS, OUTPUT);
digitalWrite(LED_GNSS, LOW);
pinMode(LED_SND, OUTPUT);
digitalWrite(LED_SND, LOW);
pinMode(LED_ERR, OUTPUT);
digitalWrite(LED_ERR, LOW);
pinMode(SIGNAL_PIN1, OUTPUT);
digitalWrite(SIGNAL_PIN1, LOW);
delay(30);
// ELTRES起動処理
eltres_board_result ret = EltresAddonBoard.begin(ELTRES_BOARD_SEND_MODE_1MIN, eltres_event_cb, gga_event_cb);
if (ret != ELTRES_BOARD_RESULT_OK) {
// ELTRESエラー発生
digitalWrite(LED_RUN, LOW);
digitalWrite(LED_ERR, HIGH);
program_sts = PROGRAM_STS_STOPPED;
Serial.print("cannot start eltres board (");
Serial.print(ret);
Serial.println(").");
} else {
// 正常
program_sts = PROGRAM_STS_RUNNING;
}
Serial.println("-->> NEXT DHt20 init");
// DHT20の初期化
if (!dht20.begin()) {
Serial.println("DHT20センサが見つかりません。接続を確認してください。");
while (1) delay(10);
}
Serial.println("DHT20センサが見つかりました。");
Serial.println("Initialization completed. Entering main loop.");
}
/**
* @brief loop()関数
*/
void loop() {
switch (program_sts) {
case PROGRAM_STS_RUNNING:
// プログラム内部状態:起動中
if (gnss_recevie_timeout) {
// GNSS電波受信タイムアウト(GNSS受信エラー)時の点滅処理
uint64_t now_time = millis();
if ((now_time - last_change_blink_time) >= 1000) {
last_change_blink_time = now_time;
bool set_value = digitalRead(LED_ERR);
bool next_value = (set_value == LOW) ? HIGH : LOW;
digitalWrite(LED_ERR, next_value);
}
} else {
digitalWrite(LED_ERR, LOW);
}
if (event_send_ready) {
// 送信直前通知時の処理
event_send_ready = false;
setup_payload_gps(); //GPS情報の送信
setup_payload_temp_hum_co2(last_temp, last_hum, (float)last_co2); //温度湿度データの送信
// 送信ペイロードの設定
EltresAddonBoard.set_payload(payload);
}
//センサからのデータの更新
measure_scd41();
break;
case PROGRAM_STS_STOPPED:
// プログラム内部状態:終了
break;
}
// 次のループ処理まで100ミリ秒待機
delay(100);
}
/**各種ペイロード設定**/
/**
* @brief GPSペイロード設定
*/
void setup_payload_gps() {
String lat_string = String((char *)last_gga_info.m_lat);
String lon_string = String((char *)last_gga_info.m_lon);
int index;
uint32_t gnss_time;
uint32_t utc_time;
// GNSS時刻(epoch秒)の取得
EltresAddonBoard.get_gnss_time(&gnss_time);
// UTC時刻を計算(閏秒補正)
utc_time = gnss_time - 18;
// 設定情報をシリアルモニタへ出力
Serial.print("[setup_payload_gps]");
Serial.print("lat:");
Serial.print(lat_string);
Serial.print(",lon:");
Serial.print(lon_string);
Serial.print(",utc:");
Serial.print(utc_time);
Serial.print(",pos:");
Serial.print(last_gga_info.m_pos_status);
Serial.println();
// ペイロード領域初期化
memset(payload, 0x00, sizeof(payload));
// ペイロード種別[GPSペイロード]設定
payload[0] = 0x81;
// 緯度設定
index = 0;
payload[1] = (uint8_t)(((lat_string.substring(index, index + 1).toInt() << 4)
+ lat_string.substring(index + 1, index + 2).toInt())
& 0xff);
index += 2;
payload[2] = (uint8_t)(((lat_string.substring(index, index + 1).toInt() << 4)
+ lat_string.substring(index + 1, index + 2).toInt())
& 0xff);
index += 2;
index += 1; // skip "."
payload[3] = (uint8_t)(((lat_string.substring(index, index + 1).toInt() << 4)
+ lat_string.substring(index + 1, index + 2).toInt())
& 0xff);
index += 2;
payload[4] = (uint8_t)(((lat_string.substring(index, index + 1).toInt() << 4)
+ lat_string.substring(index + 1, index + 2).toInt())
& 0xff);
// 経度設定
index = 0;
payload[5] = (uint8_t)(lon_string.substring(index, index + 1).toInt() & 0xff);
index += 1;
payload[6] = (uint8_t)(((lon_string.substring(index, index + 1).toInt() << 4)
+ lon_string.substring(index + 1, index + 2).toInt())
& 0xff);
index += 2;
payload[7] = (uint8_t)(((lon_string.substring(index, index + 1).toInt() << 4)
+ lon_string.substring(index + 1, index + 2).toInt())
& 0xff);
index += 2;
index += 1; // skip "."
payload[8] = (uint8_t)(((lon_string.substring(index, index + 1).toInt() << 4)
+ lon_string.substring(index + 1, index + 2).toInt())
& 0xff);
index += 2;
payload[9] = (uint8_t)(((lon_string.substring(index, index + 1).toInt() << 4)
+ lon_string.substring(index + 1, index + 2).toInt())
& 0xff);
// 時刻(EPOCH秒)設定
payload[10] = (uint8_t)((utc_time >> 24) & 0xff);
payload[11] = (uint8_t)((utc_time >> 16) & 0xff);
payload[12] = (uint8_t)((utc_time >> 8) & 0xff);
payload[13] = (uint8_t)(utc_time & 0xff);
// 拡張用領域(0固定)設定
payload[14] = 0x00;
// 品質設定
payload[15] = last_gga_info.m_pos_status;
}
/**
* @brief 温度・湿度・CO2 ペイロード設定
* @param temp 温度
* @param hum 湿度
* @param co2 CO2濃度
*/
void setup_payload_temp_hum_co2(float temp, float hum, float co2) {
// 設定情報をシリアルモニタへ出力
Serial.print("[setup_payload_temp_hum_co2]");
Serial.print("tem:");
Serial.print(temp, 6);
Serial.print(",hum:");
Serial.print(hum, 6);
Serial.print(",co2:");
Serial.print(co2);
Serial.println();
// ペイロード領域初期化
memset(payload, 0x00, sizeof(payload));
// ペイロード種別[温度・湿度・CO2ペイロード]設定
payload[0] = 0x82;
// 温度設定
uint32_t raw;
raw = *((uint32_t *)&temp);
payload[1] = (uint8_t)((raw >> 24) & 0xff);
payload[2] = (uint8_t)((raw >> 16) & 0xff);
payload[3] = (uint8_t)((raw >> 8) & 0xff);
payload[4] = (uint8_t)((raw >> 0) & 0xff);
// 湿度設定
raw = *((uint32_t *)&hum);
payload[5] = (uint8_t)((raw >> 24) & 0xff);
payload[6] = (uint8_t)((raw >> 16) & 0xff);
payload[7] = (uint8_t)((raw >> 8) & 0xff);
payload[8] = (uint8_t)((raw >> 0) & 0xff);
// CO2設定
raw = *((uint32_t *)&co2);
payload[9] = (uint8_t)((raw >> 24) & 0xff);
payload[10] = (uint8_t)((raw >> 16) & 0xff);
payload[11] = (uint8_t)((raw >> 8) & 0xff);
payload[12] = (uint8_t)((raw >> 0) & 0xff);
}
/**
* @brief SCD41から温度、湿度、CO2濃度を取得し、最新値を更新
*/
void measure_scd41() {
unsigned long currentTime = millis();
/*uint16_t error_scd4x;
bool data_ready_flag;
uint16_t co2;
float temp;
float hum;
error_scd4x = scd4x.getDataReadyFlag(data_ready_flag);
if (error_scd4x != NoError) {
Serial.print("cannot get data ready status (");
Serial.print(error_scd4x);
Serial.println(")");
return;
}
if (data_ready_flag == false) {
// センサの測定待ち
return;
}
error_scd4x = scd4x.readMeasurement(co2, temp, hum);
if (error_scd4x != NoError) {
Serial.print("cannot read measurement (");
Serial.print(error_scd4x);
Serial.println(")");
return;
}*/
if (currentTime - lastMeasureTime >= MEASURE_INTERVAL) {
lastMeasureTime = currentTime;
// 温度・湿度を取得
sensors_event_t humidity, temp;
dht20.getEvent(&humidity, &temp);
float temperature = temp.temperature;
float humidity_value = humidity.relative_humidity;
// シリアルに出力
Serial.print("温度: ");
Serial.print(temperature);
Serial.println(" °C");
Serial.print("湿度: ");
Serial.print(humidity_value);
Serial.println(" %");
// 最新値の更新
last_co2 = 0;
last_temp = temperature;
last_hum = humidity_value;
// 温度変化に応じてLEDを制御
if (temperature > lastTemperature) {
digitalWrite(SIGNAL_PIN1, HIGH); // 温度上昇時に1を出力
Serial.println("1分前より温度上昇");
} else {
digitalWrite(SIGNAL_PIN1, LOW); // 下降または変化なしで0を出力
Serial.println("1分前より温度下降 or 変化なし)");
}
// **最新の温度を保存**
lastTemperature = temperature;
/* // 最新値をシリアルモニタへ出力
Serial.print("[measure]co2: ");
Serial.print(last_co2);
Serial.print(" ppm, tem: ");
Serial.print(last_temp, 6);
Serial.print(" °C, hum: ");
Serial.print(last_hum, 6);
Serial.print(" %");
Serial.println();*/
}
}
実行結果
写真のようにデータの受信ができ、クラウド上に温湿度の情報が反映。
目標としていた温湿度の可視化を行うことができました。
まとめ
今回はSpresense と ELTRES を用いて温度、湿度を測定しクラウドに送信する方法を行いました。
実際に理解してから形にするまでの時間がかかってしまいましたが、何とか形にできて良かったです。
投稿者の人気記事
-
Fuka
さんが
2025/01/31
に
編集
をしました。
(メッセージ: 初版)
-
Fuka
さんが
2025/01/31
に
編集
をしました。
ログインしてコメントを投稿する