某SI企業で30年ほどシステムエンジニアをしている者です。
ひょんなことから、小さい規模で手軽な植物工場のようなものが必要になったのですが、
ちょうどいい製品/サービスが見つけられなかったため、自作しました。
折角なので、どのように作成したかをざっくり公開したいと思います。
本記事では、植物工場を自作する際に必要な機能、センサー選定、制御方式、などをまとめています。
また、製品化も目指すことにしましたので、そのための工夫や追加した機能についても解説します。
別の製品を考案中の方には、こちらが特に参考になるかもしれません。
植物工場に含めたい機能
光量のコントロール
植物の育成には、光合成のための適度な光が必要ですので、光量を光センサーで測定しLEDライトの出力をコントロールします。
光合成では、特に赤色(660nm付近)と青色(450nm付近)の波長の光が最も効率よく吸収され、緑色の光(500~600nm)も深部で吸収されますので、これら3つの波長を測定し利用します。
また、これらの波長のバランスによって成長速度や風味などが変わりますので、全波長のLED(赤緑強め)と青波長のLEDでバランスを調整可能にします。
気温、VPD(蒸散のしやすさ)、CO2濃度のコントロール
気温、湿度、CO2濃度をセンサーで測定し、ヒーター、換気ファン、加湿器をコントロールします。
(今回は冷却や除湿は行いませんので、外気の気温や湿度が高すぎる場合には対応していませんが、あればよりいいですね。)
植物の育成には、湿度そのものの値より、温度と湿度から算出したVPD(水分の蒸発のしやすさ)が重要ですので、制御にはVPDを利用します。
また、光合成によってCO2濃度が下がりますので、閾値から外れたときは換気を行います。
1日のサイクルを再現
朝、昼、夜で光量や気温の変化を再現します。
写真撮影
定期的に植物の状態を撮影します。
データ収集
定期的にセンサーデータと各機器の状態を収集します。
管理画面
Wifi環境内で利用できる、Webの管理画面で下記の機能を提供します。
- 育成環境の設定(光量、温度、VPD、CO2濃度の閾値、1日のサイクルを再現する時間設定)
- 写真の参照
- データの参照
- 制御プログラムの起動/停止
- 機器のシャットダウン/再起動
- Wifi接続設定
製品化のための機能
Wifi接続設定
Wifi接続設定前は、機器自体をアクセスポイントとして、Wifiで直接機器への接続を可能にします。
Wifi接続設定後は、設定されたWifiに自動で接続にします。
設定の変更やリセットも可能にします。
更新プログラムの配信
販売後のバグ修正や機能追加も可能にするため、更新プログラムの配信と、臨時スクリプトを実行する機能を組み込みます。
本来であれば、圧縮ファイルを専用サーバにアップロードし、機器側で取得する方式が望ましいのですが、初期は手軽さを優先し、GitHubから定期的にPullする方式にしました。
- メリット:構築が早い、サーバ不要
- デメリット:GitHub依存、Pull失敗時の考慮が必要
GitHubのリポジトリはPrivateにし、Pull専用のブランチを用意して、証明書によるアクセス制限を行っています。
※ハマったポイント:
リポジトリ内のファイルを機器側のプログラムで更新してしまうと、Pullがコンフリクトで失敗します。まぁ当然ですね...
データ収集
問い合わせ対応に利用するため、各種センサー値や実行ログをサーバーにアップロードします。
サーバーはAzureストレージで用意しました。手軽で高セキュリティ、本当に便利になったものです。
通知やその他のサービス
収集したデータを元に、将来的に各種サービスに利用する予定です。
例えば下記のようなサービスを考えています。
- メールでのアラート通知
- インターネットでどこでも確認/設定変更
- 分析やAIによるアドバイス
これらもAzureのFunctionsやStaticWebAppsなどを利用して実装可能です。
これも以前ならレンタルサーバーで構築しなくてはいけなかったのが、今はクラウドでできますね。
ワンタッチコネクタ
簡易に組み立て/設置ができるよう、接続をコネクタ化します。
制御用のケーブルはAWG28、ラズパイ側はデュポンコネクタ、機器側はJST-XH、
電源用のケーブルはAWG16、DCジャックまたはUSBで接続、に統一しました。
(JST-VHも考えましたが、できるだけシンプルにしたかったため)
この辺りは回線の専門ではないので、生成AIに相談しまくりました。何でも教えてくれます。(※確認は必要)
交換しやすい機器
WEBカメラ、USB/DCジャックで接続できる、入手/代替しやすい機器を採用。
構成
メイン基板
下記の理由により、Raspberry Pi 4 Model B 1GB を採用します。
- LinuxOSにより、開発/メンテナンスがしやすい
- WifiやUSBなど必要なインターフェースが揃っている
- 将来の機能拡張にも対応できるパフォーマンス
- 価格とのバランスがいい
- GUI不要なのでメモリは1GBで充分
メイン基盤のディスク用MicroSDカード
下記の理由により、8GB~の高耐久SLCモデルを採用します。
- 長期運用を踏まえて、書き込み回数と耐久性を重視
- 大量のデータを保持する必要はない
- 読み書き速度もそれほど高速である必要はない
センサー類
光センサー
下記の理由で、AS7341を使用します。
- 350〜1000nmの広い波長帯を11チャネルに分解して取得できるため、3つの波長を個別に計測可能
- I2C通信対応のため、Raspberry Piで扱いやすい
- 小型、省電力
温度/湿度/CO2
下記の理由で、SCD41を使用します。
- 精度がそこそこよく(50ppm + 2.5~3%)、測定レンジも充分(0〜40000ppm)
- 長期安定性がある
- I2C通信対応のため、Raspberry Piで扱いやすい
- 小型、省電力
配線/制御方式
- LEDはMOSFETモジュールを使用してPWM制御(ソフトPWM)
- ヒーターは、MOSFETモジュールでON/OFF制御
- USB機器(換気ファン、加湿器)も、MOSFETモジュールでON/OFF制御(USBを直接ON/OFF制御はできない)
- 配線は適切に分割し、コネクタで接続/延長を容易にする
- WEBカメラはシンプルにUSB接続
プログラム
言語
言語は主にPythonを採用します。
ライブラリが充実していて、様々なロジックに対応しやすいため。
実装機能
栽培環境制御、栽培データ収集
- 言語はPython
- 毎分、センサーから値を取得し、機器の制御を変更、状態をファイルに保存
- 1時間に一回程度、定期的に写真を撮影
- GPIOの初期化や前回の実行状態を保持する必要があるため、プログラムは終了せずsleepで実行間隔を空ける
- systemdサービスで実行
データのアップロード
- 言語はPython
- AzureBlobストレージに センサーデータ、機器の状態、写真、ログ をアップロード
- アップロードしたファイルはアップロード済みフォルダに移動し、再アップロードを防ぐ
- ファイルアクセスの競合を防ぐため、最新の1ファイルはスキップ(書き込み中かもしれないから)
- アップロードデータは、Azure側で、ラズパイ基盤のシリアル番号ごとに管理
- cronで定期的に実行
古いファイルを削除
- 言語はBash
- 定期的に古いデータや写真を削除
- cronで定期的に実行
更新プログラムの取得とスクリプトの実行
- 言語はBash
- 影響するsystemdサービスを停止
- 念のためターゲットのブランチに切り替えた後、git pull
- 新しいスクリプトがあるときは実行、実行済みスクリプト名をローカルファイルに書き出し、次回以降はスキップ
- cronで定期的に実行
管理画面
- 言語はPython
- flaskでWeb画面を提供
- 画面表示時に、Basic認証でパスワード認証を行う
- systemdサービスで常時実行、停止時は自動起動
アクセスポイントモードの制御
- 言語はBash
- Wifi設定があるときはWifi接続モード、ないとき又は接続できないときはアクセスポイントモードに変更
- systemdサービスで起動時に一回だけ実行
アクセスポイントモードは、hostapd(アクセスポイント)とdnsmasq(DHCP)を有効化、NetworkManagerを「wlan0を管理しない」設定にしてNetworkManagerを再起動します。(NetworkManagerがwlan0をつかんでいると、hostapdがwlan0を制御できず正しく動作しないため。)
Wifi接続モードは、逆にこれらを無効化、NetworkManagerの設定を元に戻してNetworkManagerを再起動します。
また、NetworkManagerの再起動時は、NetworkManagerが立ち上がるまで待つ処理を入れる必要があります。
このあたりはちょっとハマりましたので、コード例も記載しておきます:
# ---------------------------------------------------------
# NetworkManager 起動待ち
# ---------------------------------------------------------
wait_for_nm() {
local waited=0
local interval=1
local timeout=30
while [ "${waited}" -lt "${timeout}" ]; do
if systemctl is-active --quiet NetworkManager; then
# nmcli で wlan0 の状態を確認
devstate=$(nmcli -t -f DEVICE,STATE device 2>/dev/null \
| awk -F: '$1=="wlan0"{print $2}') || true
# devstate が非空で、利用可能と見なす状態になったら準備完了
if [ -n "$devstate" ]; then
case "$devstate" in
connected|disconnected|available|unmanaged)
return 0
;;
*)
# まだ準備できていない
;;
esac
fi
fi
sleep "${interval}"
waited=$((waited + interval))
done
logger -t "$TAG" "Timeout waiting for NetworkManager"
return 1
}
# ---------------------------------------------------------
# NetworkManagerの設定(wlan0を管理する)
# ---------------------------------------------------------
set_managed_by_network_manager() {
# 現在 unmanaged かどうか確認
STATE=$(nmcli -f GENERAL.STATE dev show wlan0 2>/dev/null | grep -o '20 (unmanaged)' || true)
if [ -n "$STATE" ]; then
# NetworkManagerの設定変更(wlan0管理を行う)
logger -t "$TAG" "wlan0 is unmanaged → setting managed yes"
nmcli dev set wlan0 managed yes 2>/dev/null || true
logger -t "$TAG" "Restarting NetworkManager"
systemctl restart NetworkManager 2>/dev/null || true
fi
wait_for_nm
return 0
}
# ---------------------------------------------------------
# NetworkManagerの設定(wlan0を管理しない)
# ---------------------------------------------------------
set_unmanaged_by_network_manager() {
# 現在 managed かどうか確認(unmanaged でなければ managed とみなす)
STATE=$(nmcli -f GENERAL.STATE dev show wlan0 2>/dev/null | grep -o '20 (unmanaged)' || true)
if [ -z "$STATE" ]; then
# NetworkManagerの設定変更(wlan0管理を行わない)
logger -t "$TAG" "wlan0 is managed → setting unmanaged no"
nmcli dev set wlan0 managed no 2>/dev/null || true
logger -t "$TAG" "Restarting NetworkManager"
systemctl restart NetworkManager 2>/dev/null || true
fi
wait_for_nm
return 0
}
ただここで一つ問題が。
アクセスポイントモードで接続先のWifiを利用者に選ばせたいのですが、アクセスポイントモードとのきはWifiを検索できません。無線チップがアクセスポイントとして動作するため、STAとしてのスキャン機能が使えません。
ですので、アクセスポイントモードにする前に、事前にWifiを検索してファイルに書き出しておくようにしました。
再検索したいときはラズパイを再起動する必要がありますが、仕方ありません。
また、Androidからアクセスポイントモードでラズパイに繋いだとき、インターネットにつながっていない接続は勝手に別のアクセスポイントに繋ぎ変えられてしまいます。
Android は「インターネットに到達できない Wi-Fi」を自動で切断する仕様があり、IoT デバイスの AP モードではよく起こる現象のようです。
これは機内モードにしてもらって回避するしかなさそうな感じです。PC(Windows)ではそのようなことは起こらないのですが、機器側でできる解決策は見つけられませんでした。
Wifi自動再接続
Wifi機器のONOFFなどにより、接続が切れた場合、そのままではつながらなくなってしまいますので、接続切れを検知して再接続する処理を入れています。
- 言語はBash
- cronで定期的に実行
コード例:
#!/bin/bash
PATH=/usr/sbin:/usr/bin:/sbin:/bin
IFACE="wlan0"
TAG="$(basename "$0")"
if ! ip -4 addr show dev "$IFACE" | grep -q "inet "; then
logger -t "$TAG" "IP lost. Restarting Wi-Fi..."
logger -t "$TAG" "Using NetworkManager (assumed)..."
nmcli radio wifi off
sleep 2
nmcli radio wifi on
sleep 5
# 接続を試みる。失敗したら NetworkManager を再起動して再試行
if ! nmcli device connect "$IFACE"; then
logger -t "$TAG" "nmcli connect failed, restarting NetworkManager and retrying"
systemctl restart NetworkManager 2>/dev/null || true
sleep 3
nmcli device connect "$IFACE" || logger -t "$TAG" "nmcli connect still failed"
fi
fi
さいごに
あまり詳しくは説明できませんでしたが、雰囲気伝わりましたでしょうか。
このくらいの規模なら、個人でも生成AIに相談しながら構築可能なのではと思います。
私も随分相談してなんとか構築できました。
以前なら個人では難しかったことも、生成AIやクラウドサービス等を活用して、かなりのことができるようになってきていると実感します。
時代の流れに乗って、世界を広げていきたいですね。
ではまた。


