misoのアイコン画像
miso 2022年09月26日作成 (2022年10月20日更新) © MIT
製作品 製作品 閲覧数 643
miso 2022年09月26日作成 (2022年10月20日更新) © MIT 製作品 製作品 閲覧数 643

自宅の池等に設置するSpresenseを使用した環境センサープラットフォーム

自宅の池等に設置するSpresenseを使用した環境センサープラットフォーム

はじめに

実家の庭にある池の水温をリアルタイムで監視したくて、試作したのですが。実家に帰る機会が、シルバーウィーク後半戦だったのでそのタイミングまで眠らせての制作だったのですが、部品を忘れてしまいデータ送信は試せず。

最終日になってデータ送信含めて試作しているのですが、ギリギリまで制作を遅らせてしまい「夏休み最終日に宿題が終ってない小学生状態」です。

簡単に言えば、おわた状態 なのですが・・・😇

すやぁすやぁとサボリ続けて、本日2022年09月06日ついに試作を開始した。
敵前逃亡は許されないと銃口を突きつけられたような状態で挑みます。

コンテスト締切まで残り3時間なんだけど、完成するこが出来るか・・・・・!!! 😄

部品

制作手順

  1. Spresenseメインボードと拡張ボードをガチャんと合体!
  2. マイクロUSBケーブルでバッテリーとSpresenseメインボードを接続!!
  3. DS18B20をの信号線と5Vを4.7kΩ抵抗と接続してプルアップ
  4. DS18B20の5VとGNDをSpresense拡張ボードに接続!
  5. DS18B20の信号線をSpresense拡張ボードのD10に接続!
  6. ELTRESモジュールをSpresenseメインボードと接続!!!
  7. GNSS用のアンテナとLPWAアンテナをELTRESモジュールと接続して適当に設置!

完成時の全体像

キャプションを入力できます

ソースコード

#include <Arduino.h>
#include <OneWire.h>  // OneWire by Jim_Studt
#include <DallasTemperature.h>
#include <EltresAddonBoard.h>

// 温度センサー
OneWire bus(2);
DallasTemperature sensors(&bus);

void printAddress(DeviceAddress addr) {
  for(int i = 0; i < 8; i++) {
    if(i != 0) Serial.print(':');
    if(addr[i] < 0x10) Serial.print('0');
    Serial.print(addr[i], HEX);  
  }
}

// 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_STOPPING (2)
// プログラム内部状態:終了
#define PROGRAM_STS_STOPPED (3)

// プログラム内部状態
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;
// イベント通知でのアイドル状態受信フラグ
bool event_idle = false;

// 送信回数
int send_count = 0;
uint8_t payload[16];

// 最新のGGA情報
eltres_board_gga_info last_gga_info;

/**
 * @brief イベント通知受信コールバック
 */
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);
      event_idle = true;
      break;
    case ELTRES_BOARD_EVT_SEND_READY:
      // 送信直前通知(5秒前)
      Serial.begin(115200);
      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情報受信コールバック
 */
void gga_event_cb(eltres_board_gga_info *gga_info) {
  //  Serial.print("[gga]");
  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.");
  }
}

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    delay(100);
  }

  sensors.begin();
  sensors.setResolution(9);

  // 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);

  // 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;
  }

  uint16_t error;
  char errorMessage[256];

  Serial.println("Waiting for first measurement... (5 sec)");
}

void loop() {
  uint32_t gnss_time;
  int32_t remaining;
  uint16_t error;
  char errorMessage[256];

  // Read Measurement
  float waterTemperature = 0.0f;
  float airTemperature = 0.0f;
  bool isDataReady = false;

  // Start the temperature measurement on all sensors and wait for it
  sensors.requestTemperatures();

  int index = 0;
  DeviceAddress addr;

  while(sensors.getAddress(addr, index)) {
    // Sensor index and address
    Serial.print("Sensor ");
    Serial.println(index);
    Serial.print(" - Address: ");
    printAddress(addr);
    Serial.println("");
    // Read and show the result
    Serial.print(" - Temperature: ");
    Serial.println(sensors.getTempC(addr));

    if(index == 0){
      waterTemperature = sensors.getTempC(addr);
    }
    else{
      airTemperature = sensors.getTempC(addr);
      isDataReady = true;
    }

    // Next index
    index++;
  }
  delay(100);

  if (!isDataReady) {
    return;
  }

  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) {
        Serial.print("'send_ready");

        into_payload(waterTemperature, airTemperature);

        // 送信直前通知時の処理
        event_send_ready = false;
        // 送信ペイロードの設定
        EltresAddonBoard.set_payload(payload);

        Serial.print("waterTemperature:");
        Serial.print(waterTemperature);
        Serial.print("\t");
        Serial.print("airTemperature:");
        Serial.println(airTemperature);
      }

      if (event_idle) {
        // 送信完了時の処理
        event_idle = false;
        // GNSS時刻(epoch秒)の取得
        EltresAddonBoard.get_gnss_time(&gnss_time);
        Serial.print("gnss time: ");
        Serial.print(gnss_time);
        Serial.println(" sec");
        // 次送信までの残り時間の取得
        EltresAddonBoard.get_remaing_time(&remaining);
        Serial.print("remaining time: ");
        Serial.print(remaining);
        Serial.println(" sec");
      }
      break;

    case PROGRAM_STS_STOPPING:
      // プログラム内部状態:終了処理中
      // ELTRES停止処理(注意:この処理を行わないとELTRESが自動送信し続ける)
      EltresAddonBoard.end();
      digitalWrite(LED_RUN, LOW);
      digitalWrite(LED_GNSS, LOW);
      program_sts = PROGRAM_STS_STOPPED;
      break;

    case PROGRAM_STS_STOPPED:
      // プログラム内部状態:終了
      break;
  }

  // 次のループ処理まで100ミリ秒待機
  delay(100);
}

void into_payload(float waterTemperature, float airTemperature) {
  union {
    float f;
    uint32_t i;
  } raw;
  raw.f = waterTemperature;

  payload[0] = 0x8A;

  // 水温設定
  payload[1] = (uint8_t)(raw.i >> 24 & 0xff);
  payload[2] = (uint8_t)(raw.i >> 16 & 0xff);
  payload[3] = (uint8_t)(raw.i >> 8 & 0xff);
  payload[4] = (uint8_t)(raw.i >> 0 & 0xff);

  raw.f = airTemperature;

  // 気温設定
  payload[5] = (uint8_t)(raw.i >> 24 & 0xff);
  payload[6] = (uint8_t)(raw.i >> 16 & 0xff);
  payload[7] = (uint8_t)(raw.i >> 8 & 0xff);
  payload[8] = (uint8_t)(raw.i >> 0 & 0xff);

  payload[15] = last_gga_info.m_pos_status;
}

動作確認

DS18B20での計測

SPRESENSEでOneWire.hが正しく動作せず、調べたところSPRESENSEでOneWire.hを使う|saya|noteの記事を参考に、ライブラリに修正を加えることで一応動作するようになります。

ELTRESのGNSS電波の受信

タワマンに住んでいるのですがGNSSの衛星電波の受信状態が悪く受信できず。時刻情報を取得できないとLPWA経由でのデータ送信ができないです。

Serial Monitorを眺めても invalid data が流れるだけ・・・・こんな環境でも、暫くほうちすると位置情報が表示され、時刻を受信できるみたい。

これだと、GNSS受信が容易な場所じゃないと、開発効率がグーンと下がりますね。

CLIP Viewer Liteへの送信

サンプルプログラムでも動作確認したのですが、LED表示的には送信が完了した状態であるが、何故かペイロードがダッシュボード上に表示されてこない・・・・ 😇

何度か試してると表示されたので、開発してるタワマン環境だと殆どが取りこぼしになってしまうみたいです。ちなみに周辺はタワマンぐらいしか障害物はないのですがベランダの向こう、タワマンが立ってるのでかなり、障害物多いのです。

キャプションを入力できます

設置イメージ

DS18B20の一つを池にエイヤと投げ入れ、もう一方は陸地に放り出す。
時間の都合で簡易的な試作なので防水等は考慮してません。

ごめんなさい 😇

キャプションを入力できます

温度計でも水温を計測

見たまんま池にぽちゃんとセンサーを投げ込む。

キャプションを入力できます

CLIP Viewer ダッシュボード

フリーペイロードで送信可能なことを確認。
障害物が多いせいか送信できてない時も結構ありました。

キャプションを入力できます

最後に

フリーペイロードなのでCLIP Viwer上で良い感じに表示する手段がないのですが、CLIP Viwerに収集されたデータはAPI経由で取得可能だそうです。

ただしAPI Token発行に手数料3000円が必要とのことで今回は断念しています。

misoのアイコン画像
寝るのが趣味
ログインしてコメントを投稿する