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

スマートハーブボックス: Alexa対応 自動水やり・環境監視システム

必要な部品と材料

部品名 数量 備考
Amazon Alexa Echo Dot 1 音声操作用
Arduino UNO 1 メイン制御
Espressif ESP8266 ESP-01 1 Wi-Fi通信用
Arduino Proto Shield 1 配線用
Arduino 4リレーシールド 1 ポンプ・ランプ駆動用 (高電圧分離)
水中ポンプ (12V) 2 自動給水用
植物育成ライト (LED) 1 光合成補助用
220V ACアダプター 1 育成ライト用
12V DCアダプター 1 ポンプ用
USB電源アダプター + ケーブル 1 Arduino/ESP用
エアホース (チューブ) 1 給水用
ジャンパーワイヤー 1式 配線用
土壌湿度センサー (YL-69等) 4 各植物鉢用
ブナ材 (エンクロージャ用) 適量 耐水性あり

使用アプリ・オンラインサービス

プロジェクト概要

ハーブ栽培で「水のやりすぎ・不足」を繰り返していた筆者が、完全自動化・音声操作可能なスマート栽培ボックスを自作しました。市販のハーブ鉢をそのまま入れられるサイズで、以下の機能を搭載しています。

技術スタック: Arduino (センサー/アクチュエータ制御) + ESP-01 (Wi-Fi) + PHP/JS (Web API・ダッシュボード) + AWS Lambda + Alexa Skill。

Step 1: 土壌湿度センサーの読み取り

YL-69タイプのセンサーを使用。電極腐食防止のため、測定時のみVCCを供給する設計にしています。

byte vccPin = 6;  // センサーVCC制御用GPIO
byte dataPin = A1; // アナログ入力

void setup() {
  pinMode(vccPin, OUTPUT);
  digitalWrite(vccPin, LOW);
  Serial.begin(9600);
  while (!Serial);
}

int readHumidity() {
  digitalWrite(vccPin, HIGH);   // センサー通電
  delay(500);                   // 安定待ち (要調整)
  int value = analogRead(dataPin);
  digitalWrite(vccPin, LOW);    // 通電停止 (腐食防止)
  return 1023 - value;          // 値が大きいほど湿潤
}

void loop() {
  Serial.print("Humidity (0-1023): ");
  Serial.println(readHumidity());
  delay(10000);
}

Step 2: リレーシールドでポンプとライトを制御

Arduino (5V)、ポンプ (12V)、育成ライト (220V) の電源系を分離するため、4チャネルリレーシールドを使用。重要: このシールドは「LOWでON、INPUT/LOWでOFF」というアクティブロー動作です。ピンをOUTPUTにした瞬間にONになるため、初期化時はINPUT/LOWにしておく必要があります。

byte pump1 = 11;
byte pump2 = 10;

void setup() {
  Serial.begin(9600);
  while (!Serial);
  pinMode(pump1, OUTPUT);      // pump1: HIGHでOFF
  digitalWrite(pump2, LOW);    // pump2: LOWでON (初期状態)
  pinMode(pump2, INPUT);       // 即INPUTに戻しOFF状態に
}

void loop() {
  digitalWrite(pump1, HIGH);   // pump1 OFF
  pinMode(pump2, INPUT);       // pump2 OFF
  delay(1000);
  digitalWrite(pump1, LOW);    // pump1 ON
  pinMode(pump2, OUTPUT);      // pump2 ON
  delay(1000);
}

Step 3: ESP-01でWi-Fi通信

ESP-01は独立したマイコンで、ArduinoとはSoftwareSerialで通信します。Arduinoのハードウェアシリアル (ピン0/1) はUSBデバッグ用に温存し、ピン2/3をRX/TXとして割り当てます。ボーレートは安定性重視で9600bpsに落としています。


#include <SoftwareSerial.h>
SoftwareSerial espSerial(3, 2); // RX=3, TX=2

const char* ssid = "";
const char* pass = "";

void setup() {
  Serial.begin(9600);
  espSerial.begin(9600);
  while(!Serial);
  while(!connectToWiFi()); // 接続完了までブロック
  
  // テスト用HTTPリクエスト
  if (httpRequest("my.server.com", "/site/subsite/index.php")) {
    while (espSerial.available()) Serial.write(espSerial.read());
  }
}

void loop() {
  // シリアルパススルー (ATコマンド手動テスト用)
  if (espSerial.available()) Serial.write(espSerial.read());
  if (Serial.available()) espSerial.write(Serial.read());
}

// 以下、connectToWiFi(), httpRequest() 等の実装 (本文参照)

ポイント: ATコマンドでTCP接続 → `AT+CIPSEND` でHTTPリクエスト送信 → レスポンス解析。HTTP/1.0を使用し、チャンク転送の煩わしさを回避しています。

Step 4: 木製エンクロージャの製作

市販ハーブ鉢3個分 + 全電子部品が収まるサイズでブナ材 (耐水グレードD3木工ボンド使用) をレーザーカット/ジグソーで加工。ピクセル穴 (LED用) もこの段階で開けています。背面は開放せず、後から2.5インチ幅の板で塞ぎ、基板と配線を保護しています。受け皿を内側に設置し、漏水対策も施しています。

Step 5: Web API・ダッシュボード

jQuery, Bootstrap, X-editable, Chart.js, PHPで構築。以下の機能を提供:

Step 6: Alexa連携

Web APIがAlexaスキルのバックエンドとして機能。AWS LambdaがAlexaからのJSONリクエストを受け取り、自前APIに転送 → APIがArduino向けコマンドJSONに変換 → Arduinoがポーリングして実行、というフローです。


// AWS Lambda (Node.js) 抜粋
var https = require('https');
exports.handler = (event, context, callback) => {
  var postData = JSON.stringify(event);
  var options = {
    host: '',
    path: '',
    port: 443,
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'Content-Length': postData.length }
  };
  var req = https.request(options, (res) => {
    res.setEncoding('utf8');
    res.on('data', (chunk) => {
      callback(null, JSON.parse(chunk));
    });
  });
  req.write(postData);
  req.end();
};

実装インテント例:

日本語・英語両対応のロケールサポートも実装済み。

完成デモ

音声コマンド例と応答:

動画デモ: 給水デモ / ライト操作デモ

今後の拡張予定

ソースコード (Arduino側主要ファイル)

EcoActionBuffer.h

#ifndef ECOACTIONBUFFER_H
#define ECOACTIONBUFFER_H
#include "Arduino.h"
#include "StensTimer.h"

struct EcoActionBuffer : public IStensTimerListener {
  long entryNo;
  int action;
  int pin;
  long duration;
  void timerCallback(Timer* timer);
  void switchPin(int pin, bool value);
  void readStack();
  void process();
  void toSerial();
  void reset();
};
#endif

EcoActionBuffer.cpp

#include "EcoActionBuffer.h"
#include "StensTimer.h"
#include "WhiteWalnutApi.h"

#define ACTION_ECOACTION_READ 1
#define ACTION_ECOACTION_HIGH 2
#define ACTION_ECOACTION_LOW 3

void EcoActionBuffer::readStack() {
  reset();
  WhiteWalnutApi::receiveActionFromStack(*this);
  if (entryNo != 0) process();
}

void EcoActionBuffer::process() {
  toSerial();
  pinMode(pin, OUTPUT);
  digitalWrite(pin, HIGH);
  switch (action) {
    case ACTION_ECOACTION_HIGH: switchPin(pin, true); break;
    case ACTION_ECOACTION_LOW:  switchPin(pin, false); break;
  }
  if (duration != 0) {
    StensTimer::getInstance()->setTimer(this, -pin, duration);
  }
}

void EcoActionBuffer::timerCallback(Timer* timer) {
  switch (timer->getAction()) {
    case ACTION_ECOACTION_READ: readStack(); break;
  }
  if (timer->getAction() < 0) {
    switchPin(abs(timer->getAction()), false);
  }
}

void EcoActionBuffer::switchPin(int pin, bool value) {
  digitalWrite(pin, value ? LOW : HIGH); // アクティブロー
  WhiteWalnutApi::switchPin(pin, value);
}

void EcoActionBuffer::reset() { entryNo=0; action=0; pin=0; duration=0; }

void EcoActionBuffer::toSerial() {
  Serial.print(entryNo); Serial.print(" - Action: "); Serial.print(action);
  Serial.print(", Pin: "); Serial.print(pin); Serial.print(", Duration: "); Serial.println(duration);
}

Plant.h / Plant.cpp


// Plant.h
#ifndef PLANT_H
#define PLANT_H
#include "Arduino.h"
#include "StensTimer.h"

struct Plant : public IStensTimerListener {
  const char* code;
  int humidityDataPin;
  long humidityCheckInterval;
  void checkHumidity();
  void timerCallback(Timer* timer);
  void toSerial();
  void updateApi();
};
#endif

// Plant.cpp
#include "Plant.h"
#include "StensTimer.h"
#include "WhiteWalnutApi.h"

#define ACTION_PLANT_CHECKHUMIDITY 2
#define PIN_HUMIDITY_VCC 12

void Plant::checkHumidity() {
  if (humidityDataPin != 0) {
    Serial.print(code); Serial.print(" - Check humidity...");
    digitalWrite(PIN_HUMIDITY_VCC, HIGH);
    delay(200);
    int humidity = 1023 - analogRead(humidityDataPin);
    digitalWrite(PIN_HUMIDITY_VCC, LOW);
    Serial.println(humidity);
    WhiteWalnutApi::sendHumidity(*this, humidity);
    if (humidityCheckInterval == 0) humidityCheckInterval = 60000;
    StensTimer::getInstance()->setTimer(this, ACTION_PLANT_CHECKHUMIDITY, humidityCheckInterval);
  } else {
    StensTimer::getInstance()->setTimer(this, ACTION_PLANT_CHECKHUMIDITY, 60000);
  }
}

void Plant::updateApi() {
  WhiteWalnutApi::updatePlant(*this);
  pinMode(PIN_HUMIDITY_VCC, OUTPUT);
  toSerial();
}

void Plant::timerCallback(Timer* timer) {
  if (timer->getAction() == ACTION_PLANT_CHECKHUMIDITY) checkHumidity();
}

void Plant::toSerial() {
  Serial.print(code); Serial.print(" - DataPin: "); Serial.print(humidityDataPin);
  Serial.print(", Interval: "); Serial.println(humidityCheckInterval);
}

WhiteWalnut.ino (メインスケッチ)


#include "EcoActionBuffer.h"
#include "Plant.h"
#include "StensTimer.h"
#include "WhiteWalnutApi.h"

struct TimerHelper : public IStensTimerListener {
  void updateApi();
  void timerCallback(Timer* timer);
};

StensTimer* stensTimer;
TimerHelper apiTimer;
Plant leftPlant, centerPlant, rightPlant, externalPlant;
EcoActionBuffer actionBuffer;

#define ACTION_PLANT_UPDATE 1
#define ACTION_ECOACTION_READ 1

void setup() {
  Serial.begin(9600);
  while (!Serial);
  stensTimer = StensTimer::getInstance();
  
  leftPlant.code = "LEFT";
  centerPlant.code = "CENTER";
  rightPlant.code = "RIGHT";
  externalPlant.code = "EXTERNAL";

  while(!WhiteWalnutApi::connectToWiFi()) delay(2000);
  WhiteWalnutApi::switchPin(0, false);
  
  apiTimer.updateApi();
  leftPlant.checkHumidity();
  centerPlant.checkHumidity();
  rightPlant.checkHumidity();
  externalPlant.checkHumidity();
  actionBuffer.readStack();
  
  StensTimer::getInstance()->setInterval(&apiTimer, ACTION_PLANT_UPDATE, 60000);
  StensTimer::getInstance()->setInterval(&actionBuffer, ACTION_ECOACTION_READ, 1000);
}

void loop() { stensTimer->run(); }

void TimerHelper::updateApi() {
  leftPlant.updateApi();
  centerPlant.updateApi();
  rightPlant.updateApi();
  externalPlant.updateApi();
}

void TimerHelper::timerCallback(Timer* timer) {
  if (timer->getAction() == ACTION_PLANT_UPDATE) updateApi();
}

WhiteWalnutApi.cpp / .h (ESP-01通信・HTTP・JSON処理)

Wi-Fi接続、HTTP/1.0リクエスト送信、JSONパース (ArduinoJson)、アクションスタック送受信、ピン状態同期をカプセル化。詳細は元記事のコードを参照してください。

回路図・システム構成図

製造プロセス

  1. Web制御DMX照明システム:Arduino Megaマスターコントローラー
  2. Bluetooth操作のArduinoスパイボットを自作する
  3. SERENA:Arduino Mega 2560とTFT LCDタッチスクリーン搭載カスタムアラームシステム
  4. コンパクト気象ステーション:Arduino Nano & ESP8266 + BMP280 + OLED/Nokia LCD
  5. スマート地下室換気システム:Arduinoで湿度制御
  6. ArduinoとGoogleスプレッドシート連携 リアルタイムRFID勤怠管理システム
  7. ワイヤレスArduinoプログラミングシールド – HC-05 BluetoothでUSBケーブル不要に
  8. Arduino衝突警告システム – 超音波センサーで視覚・聴覚アラート
  9. Arduino Nano用 高性能2.8インチTFTシールド - 320×240 SPIディスプレイ
  10. Arduino UNO/MEGA対応 ニキシー管クロックシールド NCS314 – IN-14/IN-12互換
  11. Arduino駆動GrowBoxコントローラー - オープンソースファームウェア&ハードウェアガイド