育苗箱ベンチレータ/基板作成

前回までで、if文を用いることで、設定時間内と設定時間外でスケッチを分割させることができました。このif文の前後に設定時間内外の処理を入れ込みました。設定時間内外で似たような処理が続くため、loop()の記述が倍になります。出来るだけ見やすくする為、RTCから現在時刻を読み出す部分をnowTime()関数として外部に出しました。

スケッチ

スケッチの概要を日本語で書くと下のような感じ。ただ、どうやって書いてもゴチャゴチャですっきりとはしないですね。スケッチの概要を簡単に記載する方法ってあるのかな。

変数定義
・RTCライブラリ(DFRobot_SD3031.h)読込
・I2Cライブラリ(Wire.h)読込:RTCはI2C接続
・OneWireライブラリ(OneWire.h)読込:DS18B20サーミスタ温度計はOneWire接続
・DS18B20ライブラリ(DallasTemperature.h)読込
・WiFiライブラリ(WiFi.h)読込
・WiFi接続のSSID、pass設定
・DHT22ライブラリ(DHT.h)読込
・ambientライブラリ(Ambient.h)読込
・設定時間指定
・XIAO ESP32C3の使用ピン指定

setup()での処理(電源投入時/DeepSleepからの復帰時)
・シリアル通信用意
・RTC(外部時計)開始
・DHT22(温湿度計)開始
・ピンモード設定
・WiFi開始/自動再接続実施
・ambient開始
・RTC時刻をNTPサーバと自動同期(時刻合わせ)

loop()での処理
・現在時間をRTCから読み出し
・RTC現在時刻が設定時間範囲内外どちらかを自動判定

【現在時刻が設定時間内の場合】
・関数nowTime()実行(RTC現在時刻読み出し+シリアル出力)
・2秒待機
・DHT22から育苗箱内気温、相対湿度を取得
・気温、相対湿度をシリアル出力
・気温をambient.1へ記録、相対湿度をambient.2へ記録
・気温が上側設定温度より高い場合、給排気ファンON
・気温が下側設定温度より低くなった場合、給排気ファンOFF
・DS18B20サーミスタ温度計よりポット内温度を取得
・ポット内温度をシリアル出力
・ポット内温度をambient.3へ記録
・ambient.1,2,3(気温、相対湿度、ポット内温度)をambientへ送信
・5秒待機
・loop()の最初へ戻る

【現在時刻が設定時間外の場合】
・関数nowTime()実行(RTC現在時刻読み出し+シリアル出力)
・2秒待機
・DHT22から育苗箱内気温、相対湿度を取得
・気温、相対湿度をシリアル出力
・気温をambient.1へ記録、相対湿度をambient.2へ記録
・DS18B20サーミスタ温度計よりポット内温度を取得
・ポット内温度をシリアル出力
・ポット内温度をambient.3へ記録
・ambient.1,2,3(気温、相対湿度、ポット内温度)をambientへ送信
・10秒待機
・1時間ほどdeep sleepに入る(→復帰時はsetup()から再開)

nowTime()の処理
・現在時刻をRTCから読み出し
・現在時刻をシリアル出力

スケッチの全体像は下の通り。

#include <DFRobot_SD3031.h>
#include <Wire.h>

#include <OneWire.h>  // For Water Temp.
#include <DallasTemperature.h>
#define ONE_WIRE_BUS D3
#define SENSOR_BIT 9  // resolution of temp meter

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

float pot_Temp;  //pot soil Temp

DFRobot_SD3031 rtc;  

#include <WiFi.h>  //wifi setting
const char SSID[] = "*********";
const char PASSWORD[] = "**************";
WiFiClient client;

#include <time.h>

#include <DHT.h>  //DHT22, ambient temp, humidity
#include <DHT_U.h>
#include <Adafruit_Sensor.h>
#define DHT_pin D0
#define DHTTYPE DHT22
DHT dht(DHT_pin, DHTTYPE);

#include <Ambient.h>  //Ambient setting
unsigned int channelId = *****; //Channel name "Seedling Box"
const char* writeKey = "********************";
Ambient ambient;

const byte hourStart = 7; //Set start time in hour
const byte minuteStart = 30; //Set start time in minute
const byte hourEnd = 16; //Set End time in hour
const byte minuteEnd = 30; //Set End time in minute

#define SLEEP (3588000000) //(60*60-12)*1000*1000・・・1時間

#define B1 D1

void setup() {
  Serial.begin(115200);
  struct tm timeInfo;
  rtc.begin();
  dht.begin();

  pinMode(DHT_pin, INPUT);       //D0 --DHT sensor, ambient temp/humidity
  pinMode(B1, OUTPUT);           //D1 --cooling fan(intake, outtake)
                                 //D2 --vacant
  pinMode(ONE_WIRE_BUS, INPUT);  //D3 --ONE WIRE BUS, DS18B20
                                 //D4 --vacant
                                 //D5 --vacant
                                 //D6 --vacant
                                 //D7 --vacant
                                 //D8 --vacant
                                 //D9 --vacant/caution when you use as LED pin
                                 //D10 --vacant

  bool connecting = true;  //WiFi connection, true = connection in progress, false = connected
  WiFi.begin(SSID, PASSWORD);
  while (connecting){
    Serial.print("WiFi connecting to the AP: ");
    Serial.println(SSID);
    unsigned long connect_start = millis();
    while (WiFi.status() != WL_CONNECTED && connect_start + 10000 > millis()){
      Serial.print (".");
      delay(500);
    }
    if (WiFi.status() == WL_CONNECTED){
      connecting = false;
      delay(500);
    } else {
      Serial.println("retry");
      delay(500);
      esp_deep_sleep(500);
    }
  }

  Serial.println("connected.");
  Serial.print("IP adress:");
  Serial.println(WiFi.localIP());

  ambient.begin(channelId, writeKey, &client);

  configTzTime("JST-9", "ntp.nict.jp","ntp.jst.mfeed.ad.jp");
  getLocalTime(&timeInfo);

  rtc.setTime(timeInfo.tm_year +1900, timeInfo.tm_mon +1, timeInfo.tm_mday, timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec);
}

void loop() {
  sTimeData_t sTime;  //now time
  sTime = rtc.getRTCTime();

  bool timeJudge1 = true;
  bool timeJudge2 = true;
  bool timeJudge3 = true;

  if (sTime.hour >= hourStart && sTime.hour <= hourEnd){
    timeJudge1 = true;
  } else {
    timeJudge1 = false;
  }

  if (sTime.hour == hourStart && sTime.minute < minuteStart){
    timeJudge2 = false;
  } else if (sTime.hour == hourStart && sTime.minute >= minuteStart){
    timeJudge2 = true;
  }

  if (sTime.hour == hourEnd && sTime.minute > minuteEnd){
    timeJudge3 = false;
  } else if (sTime.hour == hourEnd && sTime.minute <= minuteEnd){
    timeJudge3 = true;
  }

  if (timeJudge1 == true && timeJudge2 == true && timeJudge3 == true){
    nowTime();

    delay(2000);

    float HUM = dht.readHumidity();  //相対湿度測定
    float AmbTemp = dht.readTemperature();   //気温測定

    Serial.print("humidity = ");
    Serial.print(HUM);
    Serial.print(" %");
    Serial.print(", ");
    Serial.print("AmbTemp = ");
    Serial.print(AmbTemp);
    Serial.println(" C");

    ambient.set(1,AmbTemp); //ambient設定。データ1に室温をセット
    ambient.set(2,HUM); //ambient設定。データ2に相対湿度をセット


    if (AmbTemp >= 27){ //室温27℃以上でブロアB1 ON
      digitalWrite(B1, HIGH);
    } else if (AmbTemp < 25){ //室温25℃未満まで降下でブロアB1 OFF
      digitalWrite(B1, LOW);
    }

    sensors.requestTemperatures();  //DS18B20 サーミスタ温度計設定
    pot_Temp = sensors.getTempCByIndex(0);
    Serial.print("Pot Temp = ");
    Serial.print(pot_Temp);
    Serial.println(" C");

    ambient.set(3, pot_Temp);  //ambient設定。データ3へポット温度をセット
    ambient.send(); //ambient設定。セットしたデータ1,2,3をambientへ送信

    delay(5000);

  } else {
    nowTime();

    delay(2000);

    float HUM = dht.readHumidity();
    float AmbTemp = dht.readTemperature();

    Serial.print("humidity = ");
    Serial.print(HUM);
    Serial.print(" %");
    Serial.print(", ");
    Serial.print("AmbTemp = ");
    Serial.print(AmbTemp);
    Serial.println(" C");

    ambient.set(1,AmbTemp);
    ambient.set(2,HUM);

    sensors.requestTemperatures();
    pot_Temp = sensors.getTempCByIndex(0);
    Serial.print("Pot Temp = ");
    Serial.print(pot_Temp);
    Serial.println(" C");

    ambient.set(3, pot_Temp);

    ambient.send();

    delay(10000);

    esp_deep_sleep(SLEEP);

  }  
}

void nowTime(){
    sTimeData_t sTime;  //now time
    sTime = rtc.getRTCTime();

    Serial.print(sTime.year, DEC);  //year
    Serial.print('/');
    Serial.print(sTime.month, DEC);  //month
    Serial.print('/');
    Serial.print(sTime.day, DEC);  //day
    Serial.print(" (");
    Serial.print(sTime.week);  //week
    Serial.print(") ");
    Serial.print(sTime.hour, DEC);  //hour
    Serial.print(':');
    Serial.print(sTime.minute, DEC);  //minute
    Serial.print(':');
    Serial.print(sTime.second, DEC);  //second
    Serial.println(' ');
}

基板作成

今回もユニバーサル基板を使って作っていきますが、最終的に基板ボックスに入れようと考えています。あまり小さく作ってしまうと、予定している基板ボックス内がスカスカになってしまうので、25×15のユニバーサル基板を使います。

実装するセンサーや設備が少ないこともあり、ユニバーサル基板の半分くらいしか使用しません。余裕をもって配置すれば良かったですが、なんとなく将来の拡張性とか考えてしまって、面積の半分に押し込みました。これが失敗でしたね。電源をXIAO ESP32C3へのUSB給電で取ろうと考えていましたが、USBコネクタと基板ボックスの壁の距離が近すぎて、USBケーブルを差し込むことが出来なくなりました。仕方ないので、外部5V電源を取るコネクタを追加しました。次は気を付けよう。

基板全体

全体的に下半分に集めてしまいました。給排気ファンと、温湿度計、ポット温度を測る温度計、RTCくらいしかありません。上半分には、ヒーターとか追加できないかと考えています。

小さいとはいえ配線はごちゃごちゃしているので、それぞれ取り出してみます。

給排気ファン(B1)

給排気ファンはフォトリレーTLP222AFを通してスイッチのオンオフを行います。信号線はXIAO ESP32C3のD1ピンから取りますが、フォトリレーは内部にLEDを持つので、510Ωの抵抗を入れています。負荷側の電源は外部5Vから取ります。XIAO ESP32C3も同じ外部5Vから取っています。

温湿度計/DHT22

DHT22はプルアップや接続の整理されたOSOYOOのモジュールを使うので、基板上は電源と信号線だけのシンプル仕様です。ラクチンですね。

温度計/DS18B20

DS18B20は、信号線にプルアップ抵抗を入れ忘れますね。今回もブレッドボードでの試作で-127℃を表示するので、10分くらい考えてしまいました。それ以外は悩むところはないですね。信号線はD3ピンから取っています。

RTC/DS3031

DS3031はそのまま基板にハンダ付けしても良かったですが、育苗箱シーズンはそこまで長くないので、取り外しできるようにコネクタ接続にしました。その為、一旦4ピンのXHコネクタを経由しています。

実装

育苗箱ベンチレータ/基板裏面

うーん、ハンダ付けの技術はひどいもんですね。いや、あの、これには言い訳もあって、元々電源をXIAO ESP32C3のUSBで取ろうとしていましたが、基板ボックスの壁と干渉してしまって、急遽外部5Vのコネクタと配線を追加しています。何度かつけたり外したりをしたこともあり、ひどい仕上がりになりました。あー、まあ、これは言い訳にはならないですね。今後の反省です。

育苗箱ベンチレータ/基板上部

上から見るとこんな感じ。上側でコネクタと4本出しの配線でつながっているのがRTCです。最初からソケットピンを付けておけばよかったですね。まあ、これでも問題なく現在時刻を拾えています。アラート割り込み追加を想定して5ピンにしておけばよかったかな。

育苗箱ベンチレータ/基板横側

横から見ると、XIAO ESP32C3をソケットピンで浮かせていることもあり、少し厚みがありますね。ユニバーサル基板は基板ボックスと固定する為、ナット入り貼り付けスペーサーAST3-10Bを四隅に付けています。少々水が入っても、基板が浮いているので安心ですね。

育苗箱ベンチレータ/基板電源ケーブルが基板ボックス壁と干渉

USBケーブルを接続した様子が上の写真です。ボックスの壁に電源ケーブル等を通すための穴を開けグロメットを付けています。一応、USBケーブルが真っすぐ刺さる位置に調整はしてはいますが、差し込んでみると他のケーブルの圧もあり、基板が割れそうでしたので、USB給電は諦めました。基板はあと半分が空き地なので、XIAO ESP32C3を目一杯奥に配置すれば良かったですね。

長くなってしまったので、とりあえずここまで。次は育苗ケースへの実装と運転開始かな。

コメントを残す

しろすけ技術研究所
主任研究員

こんにちは。市民農園で野菜作りをしているしろすけです。極小規模の栽培管理システム開発をいつかやってみたいと思っていました。2024年から育苗ケース給排気温調や、水耕栽培管理システムの試作を電子工作で始めました。想像以上に検討することがあったので、記録を残すことにします。バイク(SR400)と耕運機(FV200)の整備記録もついでに記録に残しておきます。


Twitter/X

https://twitter.com/L4M3SsppilsCQ8C

しろすけ技術研究所をもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む