スマートハーブボックス: 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 IDE
- Amazon Alexa Skills Kit (カスタムスキル開発)
- AWS Lambda (Alexaリクエストの中継)
- PHP Webサーバー (API・ダッシュボードホスティング)
プロジェクト概要
ハーブ栽培で「水のやりすぎ・不足」を繰り返していた筆者が、完全自動化・音声操作可能なスマート栽培ボックスを自作しました。市販のハーブ鉢をそのまま入れられるサイズで、以下の機能を搭載しています。
- 土壌湿度の自動監視・グラフ表示 (Webダッシュボード)
- 設定可能な自動給水 / 手動給水 (AlexaやWebから)
- 植物育成ライトのON/OFF制御
- Amazon Alexaによる音声操作 (湿度確認、給水指示、ライト操作等)
- 外部水タンクからの給水システム
技術スタック: 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で構築。以下の機能を提供:
- Arduino設定の遠隔変更 (センサーピン、チェック間隔、ポンプ割り当て等) – JSONで配信
- 現在の湿度値と履歴グラフ表示
- Arduino側は
ArduinoJsonライブラリでJSONパース、StensTimerで定期ポーリング
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();
};
実装インテント例:
ReadHumidityIntent「植物の調子はどう?」/「バジルの湿度は?」IrrigatePlantIntent「植物に水をやって」/「バジルを5秒間水やりして」SwitchIntent「ライトをオン/オフにして」ReadIrrigateIntent「どの植物が乾いてる?」ReadLastIrrigationIntent「バジルの最後の水やりはいつ?」
日本語・英語両対応のロケールサポートも実装済み。
完成デモ
音声コマンド例と応答:
- 「アレクサ、ハーブボックスに植物の様子を聞いて」→「植物1は良好、植物2は乾燥気味…」
- 「アレクサ、ハーブボックスにバジルを5秒水やりして」→「バジルを5秒間水やりします」
- 「アレクサ、ハーブボックスにどの植物が水不足か聞いて」→「植物1と3が乾燥しています」
- 「アレクサ、ハーブボックスにバジルの最後の水やりはいつか聞いて」→「バジルの最後の水やりは36時間前です」
- 「アレクサ、ハーブボックスにライトをつけて」→「植物育成ライトをオンにしました」
今後の拡張予定
- Arduino側の省電力モード実装
- 外部Arduino Nano + 2.4GHz無線で離れた植物も監視 (ボックスをハブ化)
- 複数ボックス対応API拡張
- ボックス本体に物理ボタン追加 (Web/Alexaなしで操作可能に)
- Alexaスキルカード画像対応
ソースコード (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)、アクションスタック送受信、ピン状態同期をカプセル化。詳細は元記事のコードを参照してください。
回路図・システム構成図
- 通信・インターフェース構成図
- 実装済みAlexaインテント一覧 (多言語対応)
- Alexa JSONリクエストのパース入り口フロー図
製造プロセス
- Web制御DMX照明システム:Arduino Megaマスターコントローラー
- Bluetooth操作のArduinoスパイボットを自作する
- SERENA:Arduino Mega 2560とTFT LCDタッチスクリーン搭載カスタムアラームシステム
- コンパクト気象ステーション:Arduino Nano & ESP8266 + BMP280 + OLED/Nokia LCD
- スマート地下室換気システム:Arduinoで湿度制御
- ArduinoとGoogleスプレッドシート連携 リアルタイムRFID勤怠管理システム
- ワイヤレスArduinoプログラミングシールド – HC-05 BluetoothでUSBケーブル不要に
- Arduino衝突警告システム – 超音波センサーで視覚・聴覚アラート
- Arduino Nano用 高性能2.8インチTFTシールド - 320×240 SPIディスプレイ
- Arduino UNO/MEGA対応 ニキシー管クロックシールド NCS314 – IN-14/IN-12互換
- Arduino駆動GrowBoxコントローラー - オープンソースファームウェア&ハードウェアガイド