工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Manufacturing Technology >> 製造プロセス

ArduFarmBot Part 2:リモートIoT制御ステーションの構築

必要な部品と用品

部品 説明 数量
Arduino Nano R3 1
Espressif ESP8266 ESP-01 1
DHT22 温湿度センサー 1
KY-018 光センサーモジュール 1
土壌湿度センサー 1
リレーモジュール 2
LCD 20x4 (I2C) 1
タクトスイッチ 12mm 3
LED 3

アプリとオンラインサービス

サービス 説明
ThingSpeak API クラウドデータストレージとして使用

プロジェクト概要

ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築

このチュートリアルは「ArduFarmBot: ArduinoとIoTでトマト家庭農園を制御」の続編です。

第1部ではローカル制御ステーションを作成し、トマト栽培環境の気温、相対湿度、光量、土壌湿度を取得。これらのデータに基づき、ArduFarmBotが自動で適切なタイミングと量で給水と加温を制御しました。また、手動操作でポンプとランプを操作することも可能でした。

今回の第2部では、この「手動介入」をインターネット経由でリモートからも可能にするIoTアプローチを実装します。ブロック図の通り、取得データはクラウドストレージ(ThingSpeak.com)に送信され、専用ウェブページ「リモートコントロールページ」でほぼリアルタイムに監視・表示。さらにポンプとランプの遠隔操作も可能になります。

ステップ1:部品表

ステップ2:ハードウェアの完成

ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築

第1部で作ったローカルステーションにESP8266を追加するだけです。ブロック図に全ピン接続を記載。電圧レベルに注意:ESP8266は3.3V動作なので、NanoのTxピン(D3)を直接ESP8266のRxにつなぐことはできません。分圧抵抗でレベル変換を行います。

ESP8266の詳細については、私の以下のチュートリアルを参照:

ESP8266はNanoピン2(Tx)とピン3(Rx)に接続し、SoftwareSerialライブラリを使用。ピン0,1(ハードウェアシリアル)を使う場合は、書き込み時に外す必要があります。

※ブザーを接続する場合はピンD17(=A3)に。通信エラー時に音で知らせると便利。テスト時のみ使用し、最終版ではハードウェアから外しました(コードには残っています)。

ESP8266のテスト・セットアップ用コード:FC9DBPKIT682FY7.ino

ステップ3:ESP8266接続と初期化

ArduFarmBot Part 2:リモートIoT制御ステーションの構築

インターネット接続にはESP8266を使用。安価でプログラミングも容易なIoTモジュールです。まずCH_PDピンでリセット:

/***************************************************
 * リセット関数
 ***************************************************/
void reset8266(void) {
  pinMode(CH_PD, OUTPUT);
  digitalWrite(CH_PD, LOW);
  delay(300);
  digitalWrite(CH_PD, HIGH);
  Serial.print("8266 reset OK");
  lcd.clear();
  lcd.println("8266 reset OK ");
}

リセット後、認証情報(コード内のUSERNAME/PASSWORDを書き換え)でローカルネットワークに接続し、STAモード(CWMODE=1)で初期化:

/***************************************************
 * WiFi接続
 ***************************************************/
void connectWiFi(void) {
  sendData("AT+RST\r\n", 2000, DEBUG); // リセット
  sendData("AT+CWJAP=\"USERNAME\",\"PASSWORD\"\r\n", 2000, DEBUG); // ネットワーク接続
  delay(3000);
  sendData("AT+CWMODE=1\r\n", 1000, DEBUG);
  sendData("AT+CIFSR\r\n", 1000, DEBUG); // IPアドレス表示
  lcd.clear();
  lcd.print("8266 Connected");
  Serial.println("8266 Connected");
}

ATコマンド送信用関数 sendData():

/***************************************************
 * ATコマンド送信
 ***************************************************/
String sendData(String command, const int timeout, boolean debug) {
  String response = "";
  esp8266.print(command);
  long int time = millis();
  while ((time + timeout) > millis()) {
    while (esp8266.available()) {
      char c = esp8266.read();
      response += c;
    }
  }
  if (debug) Serial.print(response);
  return response;
}

これらの関数はsetup()で呼び出されます。正しく設定されていれば、シリアルモニタに上記のようなメッセージが表示されます。

ステップ4:クラウドストレージ - ThingSpeak.com

ArduFarmBot Part 2:リモートIoT制御ステーションの構築

全取得データを無料サービスのThingSpeak.comにアップロード。loop()内でセンサー読み取り(readSensors())後、updateDataThingSpeak()を呼び出します:

/***************************************************
 * ThingSpeak.comへデータ送信
 ***************************************************/
void updateDataThingSpeak(void) {
  startThingSpeakCmd();
  cmd = msg;
  cmd += "&field1="; cmd += tempDHT;      // field1: DHT温度
  cmd += "&field2="; cmd += humDHT;       // field2: DHT湿度
  cmd += "&field3="; cmd += lumen;        // field3: 光量
  cmd += "&field4="; cmd += soilMoist;    // field4: 土壌湿度
  cmd += "&field5="; cmd += pumpStatus;   // field5: ポンプ状態
  cmd += "&field6="; cmd += lampStatus;   // field6: ランプ状態
  cmd += "\r\n";
  sendThingSpeakCmd();
}

ThingSpeakとの通信開始:

/***************************************************
 * ThingSpeak.comとの通信開始
 ***************************************************/
void startThingSpeakCmd(void) {
  cmd = "AT+CIPSTART=\"TCP\",\"";
  cmd += IP;
  cmd += "\",80";
  esp8266.println(cmd);
  delay(2000);
  if (esp8266.find("Error")) {
    Serial.println("ESP8266 START ERROR");
    return;
  }
  Serial.println("Thinkspeak Comm Started");
  cmd = "";
}

データ送信:

/***************************************************
 * ThingSpeakチャネル更新
 ***************************************************/
String sendThingSpeakCmd(void) {
  esp8266.print("AT+CIPSEND=");
  esp8266.println(cmd.length());
  if (esp8266.find(">")) {
    esp8266.print(cmd);
    Serial.println(""); Serial.println(""); Serial.println(cmd);
    delay(500);
    String messageBody = "";
    while (esp8266.available()) {
      String line = esp8266.readStringUntil('\n');
      if (line.length() == 1) {
        messageBody = esp8266.readStringUntil('\n');
        Serial.print("Message received: "); Serial.println(messageBody);
      }
    }
    return messageBody;
  } else {
    esp8266.println("AT+CIPCLOSE");
    Serial.println("ESP8266 CIPSEND ERROR: RESENDING");
    error = 1;
    return "error";
  }
}

これらの関数はMichalis Vasilakis氏の詳細なチュートリアルをベースにしています。詳細はArduino IOT: Temperature and Humidity (with ESP8266 WiFi)を参照。

ステップ5:Webからアクチュエーターを制御

ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築

データをクラウドにアップロードして監視できるようになりましたが、取得データに基づいてリモートからポンプやランプをON/OFFしたい場合もあります。そのため、ThingSpeakチャネルに制御用フィールドを追加:

ThingSpeakでこれらのフィールドを設定するには、プラグインを書くか、外部Webサイトから操作します(ここでは後者を採用)。以下のコマンドで書き込み:

api.thingspeak.com/update?key=YOUR_WRITE_KEY&field7=1

ブラウザで実行すると、該当フィールドが更新されます。返り値はシリアル番号です。

次に、この「命令」をローカルのArduFarmBotで読み取ります:

api.thingspeak.com/channels/CHANNEL_ID/fields/7/last

ブラウザでテストすると、そのフィールドの最新値が表示されます。

フィールド7の最新データを読み取る関数:

/***************************************************
 * ThingSpeak field7の最新データ読み取り
 ***************************************************/
int readLastDataField7() {
  startThingSpeakCmd();
  cmd = msgReadLastDataField7;
  cmd += "\r\n";
  String messageDown = sendThingSpeakCmd();
  Serial.print("Command received: "); Serial.println(messageDown[7]);
  int command = messageDown[7] - 48; // ASCII '0'=48
  return command;
}

フィールド8用も同様に作成。取得した命令でアクチュエーターを制御:

/***************************************************
 * ThingSpeakから命令を受信してアクチュエーター制御
 ***************************************************/
void receiveCommands() {
  field7Data = readLastDataField7();
  if (field7Data == 1) { digitalWrite(PUMP_PIN, HIGH); pumpStatus = 1; showDataLCD(); }
  if (field7Data == 0) { digitalWrite(PUMP_PIN, LOW); pumpStatus = 0; showDataLCD(); }
  delay(500);
  field8Data = readLastDataField8();
  if (field8Data == 1) { digitalWrite(LAMP_PIN, HIGH); lampStatus = 1; showDataLCD(); }
  if (field8Data == 0) { digitalWrite(LAMP_PIN, LOW); lampStatus = 0; showDataLCD(); }
  delay(500);
}

これでブラウザからポンプとランプをリモート操作可能。シリアルモニタに受信コマンドが表示されます。

ローカル操作とリモート操作の「調整」も重要。readLocalCmd() 関数でローカルボタン操作時にThingSpeakのField 7,8も同期更新するよう修正:

field7Data = pumpStatus;
field8Data = lampStatus;

これでWeb命令とローカル操作が同期。実際にアクチュエーターを動かす aplyCmd() 関数:

/***************************************************
 * 命令を受けてアクチュエーター駆動
 ***************************************************/
void aplyCmd() {
  if (field7Data == 1) digitalWrite(PUMP_PIN, HIGH);
  if (field7Data == 0) digitalWrite(PUMP_PIN, LOW);
  if (field8Data == 1) digitalWrite(LAMP_PIN, HIGH);
  if (field8Data == 0) digitalWrite(LAMP_PIN, LOW);
}

テストすると、ローカル/Webどちらの手動操作も、自動制御関数 autoControlPlantation() に上書きされてしまいます。「最終決定権」を誰にするか設計が必要です。ここでは以下の優先順位としました:

  1. 毎ループ:ローカルボタン押下をチェック
  2. 約毎分:ThingSpeakをポーリングしてリモート命令確認
  3. 約毎10分:センサー読み取り、ThingSpeak更新、自動制御実行(これが最優先で維持される)

タイマーを2つ使用:

long sampleTimingSeconds = 75;      // センサー読み取り間隔(秒)
long poolingRemoteCmdSeconds = 20;  // リモート命令ポーリング間隔(秒)
long startTiming = 0, elapsedTime = 0;
long startRemoteCmdTiming = 0, elapsedRemoteCmdTime = 0;
int reverseElapsedTimeSeconds = 0;

書き換えた loop():

void loop() {
  elapsedRemoteCmdTime = millis() - startRemoteCmdTiming;
  elapsedTime = millis() - startTiming;
  reverseElapsedTimeSeconds = round(sampleTimingSeconds - elapsedTime/1000);
  
  readLocalCmd();    // ローカルボタン読み取り
  showDataLCD();     // LCD表示
  
  if (elapsedRemoteCmdTime > (poolingRemoteCmdSeconds * 1000)) {
    receiveCommands();        // リモート命令受信
    updateDataThingSpeak();   // 状態アップロード
    startRemoteCmdTiming = millis();
  }
  
  if (elapsedTime > (sampleTimingSeconds * 1000)) {
    readSensors();            // センサー読み取り
    autoControlPlantation();  // 自動制御
    updateDataThingSpeak();   // データアップロード
    startTiming = millis();
  }
}

ステップ6:専用Webページの実装

ArduFarmBot Part 2:リモートIoT制御ステーションの構築

この段階でArduFarmBotはWebから制御可能ですが、ブラウザでコマンド打ち込みは「エレガント」ではありません。完全なIoTソリューションには、データ表示とアクチュエーター操作ボタンを備えた専用Webページが必要です。

無料ホスティングのByethostを使用。Coursera/ミシガン大学の「Webデザインと作成」スペシャリゼーション(HTML5, CSS3, JavaScript)で学んだ知識を活用しました。

Webページ開発の詳細は本筋ではないため割愛しますが、HTML/CSS/JavaScriptのソースコードを同梱しています。興味があればコメント欄で議論しましょう。

重要:このページはArduFarmBotローカルステーションと直接通信しません。ThingSpeakチャネルと以下のようにやり取りします:

項目4は必須ではありませんが、ローカルのトマト栽培とは独立にリモート判断する際の参考データになります。別のThingSpeakチャネルに保存してArduinoにダウンロードし、ローカルLCDに天気表示するのも面白いです(別プロジェクトで実装済み)。

Webページソース:FQOK9ENIT653YX5.zip

ステップ7:センサー・アクチュエーターマトリクスアプローチ

ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築

第1部ではアクチュエーターの動作条件をシンプルに定義しましたが、複雑な状況組み合わせには対応できません。「センサー・アクチュエーターマトリクス」で体系化します。

各センサーの条件とアクチュエーター出力をマトリクスで定義(同梱Excelファイル参照)。フィルター機能付きテーブルと、複数条件選択でアクチュエーター動作を確認できるバージョンの2シート構成。

これをコードに落とし込み、18行×10列の配列で条件を管理:


// +---SOIL----+-LIGHT-+---TEMP---+---ACTUAT----+
// SL SM SH LL LH TL TM TH Pump Lamp
boolean SDf[18][10] = {
  {1,0,0, 0,1, 0,0,1, 1,0},
  {1,0,0, 0,1, 0,1,0, 1,0},
  {1,0,0, 0,1, 1,0,0, 1,1},
  {1,0,0, 1,0, 0,0,1, 1,0},
  {1,0,0, 1,0, 0,1,0, 1,0},
  {1,0,0, 1,0, 1,0,0, 0,1},
  {0,1,0, 0,1, 0,0,1, 0,0},
  {0,1,0, 0,1, 0,1,0, 0,0},
  {0,1,0, 0,1, 1,0,0, 0,1},
  {0,1,0, 1,0, 0,0,1, 0,0},
  {0,1,0, 1,0, 0,1,0, 0,1},
  {0,1,0, 1,0, 1,0,0, 0,1},
  {0,0,1, 0,1, 0,0,1, 0,0},
  {0,0,1, 0,1, 0,1,0, 0,0},
  {0,0,1, 0,1, 1,0,0, 0,1},
  {0,0,1, 1,0, 0,0,1, 0,0},
  {0,0,1, 1,0, 0,1,0, 0,1},
  {0,0,1, 1,0, 1,0,0, 0,1},
};

defSensorStatus() 関数で各行の最初の8列条件が全てTRUEなら、最後の2列(ポンプ、ランプ)を実行。内部でセンサー状態配列 boolean snsSts[8] も作成し、ログ記録にも使用。

Excelファイル:F2HWXBYITA8WIN1.xlsx

ステップ8:コード最適化

ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築

開発過程で仕様変更を行いました:

ディスプレイ

LCDはデフォルトOFF。センサー読み取りボタンをトグルとして、任意のタイミングでON/OFF可能。表示ON/OFF時はセンサー値を更新して表示しますが、通常制御には使用しません。

初期セットアップ

起動/リセット時に「初期設定」画面を表示。センサーボタン押下でプログラム開始。表示項目:

ログ記録

監査用にセンサー読み取りとアクチュエーションのログをEEPROMに記録。各読み取りサイクルで storeDataLogEEPROM() を実行:

/***************************************************
 * Arduino EEPROMへログデータ保存
 ***************************************************/
void storeDataLogEEPROM(void) {
  for (int i = 0; i < 8; i++) {
    logData = logData + (snsSts[i] << 1);
  }
  EEPROM.write(memoAddr, logData);
  memoAddr++;
  logData = 0;
  logData = logData + pumpStatus;
  logData = logData << 1;
  logData = logData + lampStatus;
  EEPROM.write(memoAddr, logData);
  EEPROM.write(0, memoAddr + 1);
  logData = 0;
  if ((memoAddr + 1) == 1023) memoAddr = 1;
  else memoAddr++;
}

snsSts[] のビット列+ポンプ/ランプ状態をEEPROMに格納。シリアルモニタのログ表示例は写真参照。

コードは複数ファイルに分割。第2部で新規追加:

  • communication.ino - ThingSpeak/ESP8266専用関数
  • stationCredentials.h - ThingSpeakチャネルIDとライトキー

コードサイズ増大に対応し、定数データをフラッシュメモリ(PROGMEM)に配置してSRAMを節約:

// #define DHTPIN 5  の代わりに
const PROGMEM byte DHTPIN = 5;

avr/pgmspace.h をインクルード。デバッグ用 Serial.print() はコメントアウトまたは削除(ログ表示コード等もコメントアウト済み)。

完全なArduFarmBotコード一式:

ステップ9:MJRovaiホームファーム

ArduFarmBotで制御した最初のトマト栽培の連続写真:

ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築

2回目の栽培:種まきから約45日で優良苗6株を選抜・移植するまで:

ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築

ステップ10:結論

ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築

今回はここまで!

このプロジェクトが、エレクトロニクス、IoT、ロボティクスの魅力的な世界への道しるべになれば幸いです。

近いうちに第3部(最終回)として、オーガニックトマトソースパスタのレシピを公開予定です!

写真にある通り、マウリシオの栽培にも生命の兆しが。最後に、新刊『ArduFarmBot, the book!』をご紹介:

ArduFarmBot, the Book!

ArduFarmBot Part 2:リモートIoT制御ステーションの構築

Amazon.comでも購入可能。電子コントローラー「ArduFarmBot」を題材に、ハードウェアとソフトウェアの両面から学べる実践書:

  • LCD/OLEDディスプレイ
  • LEDとボタン
  • リレーによるポンプ/ランプ制御
  • センサー:DHT22(気温・湿度)、DS18B20(土壌温度)、YL69(土壌湿度)、LDR(光量)
ArduFarmBot Part 2:リモートIoT制御ステーションの構築

全工程を説明文、ブロック図、高解像度カラー写真、Fritzing回路図、GitHub格納コード、YouTube動画で詳細に文書化。

ArduFarmBot Part 2:リモートIoT制御ステーションの構築

第1部:Arduino Nanoベースのローカル版ArduFarmBot(手動/自動制御)

ArduFarmBot Part 2:リモートIoT制御ステーションの構築

第2部:ESP8266-01でWi-Fi接続、ThingSpeak.com連携、Webページ作成によるリモート操作

ArduFarmBot Part 2:リモートIoT制御ステーションの構築

第3部:NodeMCU ESP8266-12E導入でArduino NanoとESP8266-01を置き換え、Blynkプラットフォームも活用

ArduFarmBot Part 2:リモートIoT制御ステーションの構築
ArduFarmBot Part 2:リモートIoT制御ステーションの構築

書籍をダウンロードしてレビューを書いてください。コメント欄でご意見・ご提案・ご批判をお待ちしています!

その他のプロジェクトはブログ MJRoBot.org で公開中。

南半球からサルードス!

次のプロジェクトでお会いしましょう!

ありがとう
Marcelo

コードスニペット

主要関数をスニペットとして掲載(完全版は上記ファイル参照):

  • Code snippet #1: reset8266()
  • Code snippet #2: connectWiFi()
  • Code snippet #3: sendData()
  • Code snippet #4: updateDataThingSpeak()
  • Code snippet #5: startThingSpeakCmd()
  • Code snippet #6: sendThingSpeakCmd()
  • Code snippet #9: readLastDataField7()
  • Code snippet #10: receiveCommands()
  • Code snippet #11: aplyCmd()
  • Code snippet #12: タイマー変数定義
  • Code snippet #13: loop() メインループ
  • Code snippet #14: センサー・アクチュエーターマトリクス配列
  • Code snippet #15: storeDataLogEEPROM()

GitHubリポジトリ:https://github.com/Mjrovai/ArduFarmBot

回路図

Fritzingファイル

製造プロセス

  1. Arduino制御の6連ショット・ドリンクミキシングステーション – 自作カクテル自動製造機
  2. スマートホーム連携向けマルチポジション温度センサーシステム
  3. MotionSense: ArduinoとESP8266で作るスマート侵入検知システム
  4. 3Dプリンター火災予防:スマートセンサーセーフティシステム
  5. Arduino、Yaler、IFTTTでWeb対応IoTゲージを構築する
  6. Arduinoで作る斥力式磁気浮上装置: シンプル回路とDIYガイド
  7. 高度なリモートコントロールグリッパーロボット: 精密ハンドリング向け
  8. 高性能 Arduino 気象観測気球データロガーの製作ガイド
  9. 10ドルで作る携帯型Arduino気象観測ステーション – Nano、DHT22、BMP180、LCDで簡単DIY
  10. CoroFenceサーマルディテクター:Grid-EYE & PIRセンサー搭載IoT体温監視
  11. IoT実装における新興トレンド:インサイトと戦略