🌾

SDI-12センサの安価な測定システムの作り方 #2(測定データ保存編)

2024/03/18に公開

この記事のゴール

  • マイコン(M5Stack ATOM)を用いたSDI-12センサデータのSDカードへの保存
    • 保存形式はCSV

前提

  • この記事をご覧になるのは研究者の方を想定していますので,コードに関しては比較的丁寧に記載いたします
  • この記事はSDI-12センサの内,Acclima社製のTDTセンサを使用する例を記載しています
  • この記事は筆者のHPから移植している部分が多いため,最新でない情報も含まれています
  • Mac環境の例を記載していますので,Windowsの方は必要に応じて読み替えをお願いします
  • 前回の記事を先にご覧になることを推奨します

必要な知識・もの

  • Arduino言語の基本的な知識
  • PC
必要な物品リスト
  • SDI-12センサ(TDTセンサ) × 1
  • ブレッドボード × 1
  • 端子台(3極) × 1
  • ジャンパワイヤ × 10本程度
  • ATOM Lite × 1 (* その他のM5Stack系のマイコンでも動作可能ですが,適宜配線を変更する必要があります)
  • USB type-c ケーブル × 1

以下前回からの追加

ライブラリのインストール

  • 今回は前回に加えて SD.hSPI.h のライブラリを使用します
    • おそらくデフォルトでインストールされていると思います

配線

  • ATOM LiteとSDI-12センサ,SDカードスロットを接続します
    • 配線は下記の通りです(SDカード側のピンの呼称については microSDカードスロットDIP化キット のデータシートを参照ください)

      SDI-12センサ ATOM
      GND GND
      PWR 5V
      DATA 33
      SD ATOM
      VDD 3.3V
      VSS GND
      DAT0 21
      CMD 25
      CLK 19
      CD/DAT3 23

センサデータ保存の流れ

  • 5秒間隔で測定・保存するシンプルなデータ取得の流れは下記の通りです

    1. センサに測定コマンドを送信(*1)
    2. 測定にかかる時間を取得(*2)
    3. 測定にかかる時間分待機(*3)
    4. センサに測定結果取得コマンドを送信(*4)
    5. 測定結果を表示(*5)
    6. 測定結果文字列の+,に置換し,末尾に改行コードを追加(*6)
    7. SDカードにデータを保存(*7)
    8. 測定間隔分待機(*8)
  • 上記に加えて下記の設定・処理を行っています

    1. 測定データを書き込むファイルを指定(*A)
    2. SDカードを読み取れない場合,エラーを出力し,処理を停止(*B)
    • 処理が停止されるので,配線等を再度確認してください
    1. 書き込み対象のファイルが存在しない場合,ファイルを作成する(*C)
    #include <SDI12.h>
    #include "SD.h"
    #include "SPI.h"
    
    #define DATA_PIN 33
    
    #define SD_PIN 23
    #define SD_MISO_PIN 21
    #define SD_MOSI_PIN 25
    #define SD_CLK 19
    
    SDI12 mySDI12(DATA_PIN);
    String responseArray[10];
    
    const String sensorAddress = "1";
    String measureCommand = sensorAddress + "M!";
    String resultCommand = sensorAddress + "D0!";
    
    // *A
    const char *path = "/result.csv";
    
    void setup()
    {
      Serial.begin(115200);
      mySDI12.begin();
    
      // *B
      if (!SD.begin(SD_PIN))
      {
        Serial.println("SDカードのが読み取れません");
        while (1)
        {
        }
      }
    
      // *C
      File file = SD.open(path);
      if (!file)
      {
        SD.open(path, FILE_APPEND);
      }
      file.close();
    }
    
    void loop()
    {
      // *1
      const String response = sendCommandCollectResponse(measureCommand); // 例)100104
    
      // *2
      int waitTime = response.substring(3, 4).toInt() * 1000;
    
      // *3
      delay(waitTime);
    
      // *4
      const String result = sendCommandCollectResponse(resultCommand); // 例)1+20.27+15.2+10.67+0.41
    
      // *5
      Serial.println(result);
    
      // *6
      String record = replace(result) + "\n"; // 改行コードを追加
    
      // *7
      writeFile(path, record);
    
      // *8
      delay(5000);
    }
    
    /**
     * @brief コマンド送信とレスポンスの取得
     *
     * @param command コマンド文字列
     * @return String レスポンス
     */
    String sendCommandCollectResponse(String command)
    {
      String response = "";
    
      mySDI12.sendCommand(command);
    
      while (mySDI12.available())
      {
        char responseChar = mySDI12.read();
        if ((responseChar != '\n') && (responseChar != '\r'))
        {
          response += responseChar;
          delay(10);
        }
      }
      mySDI12.clearBuffer();
    
      return response;
    }
    
    /**
     * @brief SDカードに書き込む
     *
     * @param path ファイルパス
     * @param message 書き込む内容
     */
    void writeFile(const char *path, String message)
    {
    
      File file = SD.open(path, FILE_WRITE);
      if (!file)
      {
        return;
      }
    
      file.print(message);
      file.close();
    }
    
    /**
     * @brief +区切りの文字列をカンマ区切りに置換
     *
     * @param data +区切りの文字列
     * @return String 置換後の文字列
     */
    String replace(String data)
    {
      int index = 0;
      int arraySize = (sizeof(data) / sizeof((data)[0]));
      int dataLength = data.length();
    
      String resultString = "";
    
      for (int i = 0; i < dataLength; i++)
      {
        char dataChar = data.charAt(i);
        if (dataChar == '+')
        {
          resultString += ",";
          index++;
        }
        else
        {
          resultString += dataChar;
          index++;
        }
      }
    
      return resultString;
    }
    

おわりに

  • 上記のコードで5秒毎に測定されたデータをSDカードに保存することができます
  • 今回はSDカードに測定データを保存するまでをご紹介しました
    • 次回は測定データをインターネットを通じて任意の場所に保存する方法の前段として,マイコンをインターネットに接続する方法について紹介したいと思います

Discussion