🌾

SDI-12センサの安価な測定システムの作り方 #3(Wi-Fi接続編)

2024/03/23に公開

この記事のゴール

  • マイコン(M5Stack ATOM)のインターネットへの接続

前提

  • この記事をご覧になるのは研究者の方を想定していますので,コードに関しては比較的丁寧に記載いたします
  • この記事は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
  • microSDカードスロットDIP化キット
  • microSDカード × 1

以下前回からの追加

  • 追加なし

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

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

配線

  • 配線は前回の同じため,割愛します

Wi-Fi接続の流れ

  • コード全体が長くなりましたので,前回からの差分を紹介します
    • アクセスポイントのSSIDを設定(*1)
    • アクセスポイントのパスワードを設定(*2)
    • アクセスポイントに接続(*3)
    • 接続に失敗した場合,再起動(*4)
    • 処理中にWi-Fiの接続が切れた場合に再接続(*5)
// 前略
#include "WiFi.h"
// 中略

// Wi-Fiの設定
const char *ssid = "sample-access-point"; // *1
const char *pass = "sample-pass"; // *2

// 中略

void setup()
{
  // 中略
  const bool wifiStatus = connectToAccessPoint(ssid, pass); // *3
  if (!wifiStatus)
  {
    // 5秒待機して再起動
    delay(5000);
    ESP.restart(); // *4
  }
}

void loop()
{
  // 再接続処理
  handleWiFiReconnection(); // *5

  // 後略
}

 * @brief アクセスポイントに接続
 *
 * @param ssid 接続先SSID
 * @param password 接続先PASS
 * @return true: 成功, false: 失敗
 */
bool connectToAccessPoint(const char *ssid, const char *password)
{
  WiFi.begin(ssid, password);
  int waitCountInSec = 0;
  while (WiFi.status() != WL_CONNECTED)
  {
    waitCountInSec++;
    delay(1000);
    Serial.print(".");
    // 10秒待って接続できなかった場合失敗とする
    if (waitCountInSec == 10)
    {
      Serial.println("Wi-Fi の接続に失敗しました。");
      return false;
    }
  }
  Serial.println("WiFi に接続しました。");
  return true;
}

/**
 * @brief アクセスポイントへの接続状況に応じて再接続する
 */
void handleWiFiReconnection()
{
  // 接続状態でない場合
  if (WiFi.status() != WL_CONNECTED)
  {
    Serial.println("接続状態が解除されました。");

    // 再接続を試みる
    const bool wifiStatus = connectToAccessPoint(ssid, pass);

    if (!wifiStatus)
    {
      // 5秒待機して再起動
      delay(5000);
      ESP.restart();
    }
  }
}
コード全体
#include <SDI12.h>
#include "SD.h"
#include "SPI.h"
#include "WiFi.h"

#define DATA_PIN 33

#define SD_PIN 23
#define SD_MISO_PIN 21
#define SD_MOSI_PIN 25
#define SD_CLK 19

// Wi-Fiの設定
const char *ssid = "sample-access-point";
const char *pass = "sample-pass";

// SDI-12の設定
SDI12 mySDI12(DATA_PIN);
String responseArray[10];
const String sensorAddress = "1";
String measureCommand = sensorAddress + "M!";
String resultCommand = sensorAddress + "D0!";

// CSVの設定
const char *path = "/result.csv";

void setup()
{
  Serial.begin(115200);
  mySDI12.begin();

  if (!SD.begin(SD_PIN))
  {
    Serial.println("SDカードのが読み取れません");
    while (1)
    {
    }
  }

  File file = SD.open(path);
  if (!file)
  {
    SD.open(path, FILE_APPEND);
  }
  file.close();

  const bool wifiStatus = connectToAccessPoint(ssid, pass);
  if (!wifiStatus)
  {
    // 5秒待機して再起動
    delay(5000);
    ESP.restart();
  }
}

void loop()
{
  // 再接続処理
  handleWiFiReconnection();

  // 測定コマンド送信
  const String response = sendCommandCollectResponse(measureCommand); // 例)100104

  //  測定時間を取得
  int waitTime = response.substring(3, 4).toInt() * 1000;

  // 測定待機
  delay(waitTime);

  // 測定結果取得コマンド送信
  const String result = sendCommandCollectResponse(resultCommand); // 例)1+20.27+15.2+10.67+0.41

  Serial.println(result);

  //  +区切りの文字列をカンマ区切りに置換
  String record = replace(result) + "\n"; // 改行コードを追加

  // ファイルに書き込み
  writeFile(path, record);

  // 5秒間隔測定
  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;
}

/**
 * @brief アクセスポイントに接続
 *
 * @param ssid 接続先SSID
 * @param password 接続先PASS
 * @return true: 成功, false: 失敗
 */
bool connectToAccessPoint(const char *ssid, const char *password)
{
  WiFi.begin(ssid, password);
  int waitCountInSec = 0;
  while (WiFi.status() != WL_CONNECTED)
  {
    waitCountInSec++;
    delay(1000);
    Serial.print(".");
    // 10秒待って接続できなかった場合失敗とする
    if (waitCountInSec == 10)
    {
      Serial.println("Wi-Fi の接続に失敗しました。");
      return false;
    }
  }
  Serial.println("WiFi に接続しました。");
  return true;
}

/**
 * @brief アクセスポイントへの接続状況に応じて再接続する
 */
void handleWiFiReconnection()
{
  // 接続状態でない場合
  if (WiFi.status() != WL_CONNECTED)
  {
    Serial.println("接続状態が解除されました。");

    // 再接続を試みる
    const bool wifiStatus = connectToAccessPoint(ssid, pass);

    if (!wifiStatus)
    {
      // 5秒待機して再起動
      delay(5000);
      ESP.restart();
    }
  }
}

おわりに

  • 上記のコードでマイコンをWi-Fiアクセスポイントに接続することができます
    • loop処理中にWiFi接続が切れた場合に再接続する処理を入れています
  • 今回はWi-Fiに接続するまでをご紹介しました
    • 次回はマイコンからインターネットを通じて飛んできた測定データを保存するシステム構築の前段として,構築するシステムの設計についてご紹介したいと思います

Discussion