Arduino UNO + ESP-01 + ThingSpeak + MIT App Inventorで作るシンプルIoTプロジェクト
必要な部品と材料
| 部品 | 数量 | 備考 |
|---|---|---|
| Arduino UNO | 1 | メインMCU |
| ESP8266 ESP-01 | 1 | WiFi通信モジュール |
| DHT22 温湿度センサー | 1 | 気温・湿度用 |
| DS18B20 防水温度センサー | 1 | 土壌温度用 |
| LDR(光センサー) | 1 | 照度測定用 |
| 2チャネル 5V リレーモジュール | 1 | ポンプ・ランプ制御用 |
使用アプリ・オンラインサービス
- Arduino IDE
- ThingSpeak API
- MIT App Inventor
プロジェクト概要
ローカルユニット(センサー・アクチュエーター)からデータを収集し、インターネット経由でクラウド(ThingSpeak)に送信。世界中どこからでもデータを確認し、遠隔でアクチュエーターを操作できるシステムを構築します。

以前はNodeMCUやESP32を使っていましたが、Arduino UNOとESP-01という初期の組み合わせで、より基礎的なIoTの仕組みを理解することを目的としています。

ThingSpeakを中心に、ローカルユニットがセンサーデータを「書き込み」、アクチュエーター制御コマンドを「読み取り」ます。Androidアプリも同様にStatus Channelを読み取り、Actuator Channelに書き込みます。
Step 1: はじめに

以下のデータを収集・制御します。
センサー(5項目)
- 気温・湿度(DHT22)
- 土壌温度(DS18B20)
- 土壌湿度(YL-69 + LM393)
- 照度(LDR)
アクチュエーター(2項目)
- 電動ポンプ
- 電球(220V)
アクチュエーターのON/OFF状態もクラウドに送信します。
判断ロジック:
- 土壌湿度が低い → ポンプON
- 土壌温度が低い → ランプON
遠隔操作用にAndroidアプリも作成します。
Step 2: 部品表(BoM)
主要部品の目安価格(USD):
- Arduino UNO - $10.00
- ESP8266-01 - $3.50
- DHT22 - $9.00
- DS18B20 - $6.00
- 土壌湿度センサー - $2.00
- LDR - $0.20
- LED(赤・緑)各1
- 2チャネル 5V リレーモジュール(フォトカプラ付き、Lowアクティブ) - $7.00
- 5V DCポンプ - $3.00
- 220V電球
- 抵抗:330Ω×2、10kΩ×2、4.7kΩ×1
- ブレッドボード、ジャンパーワイヤー
- リレー用外部5V電源
Step 3: ハードウェア組み立て手順

段階的に組み立て・テストすることを推奨:
- センサーを個別に設置・テスト
- ESP-01をベアミニマム構成で設定・テスト
- ESP-01を最終配線に変更してテスト
- ThingSpeak Status Channel設定
- ArduinoにThingSpeak送信コードを書き込み、クラウドで確認
- Androidアプリ第1版(ステータス表示)作成
- アクチュエーター(LED、リレー)設置
- ThingSpeak Actuator Channel設定
- アクチュエーター制御コードを書き込みテスト
- Androidアプリ最終版作成
Step 4: センサー配線

必要なライブラリと初期設定:
// DS18B20
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 5
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature DS18B20(&oneWire);
int soilTemp = 0;
// DHT22
#include "DHT.h"
#include <stdlib.h>
int pinoDHT = 11;
int tipoDHT = DHT22;
DHT dht(pinoDHT, tipoDHT);
int airTemp = 0, airHum = 0;
// LDR
#define ldrPIN A1
int light = 0;
// 土壌湿度
#define soilHumPIN A0
int soilHum = 0;
void setup() {
Serial.begin(9600);
DS18B20.begin();
dht.begin();
}
void loop() {
readSensors();
displaySensors();
delay(10000);
}
void readSensors() {
airTemp = dht.readTemperature();
airHum = dht.readHumidity();
DS18B20.requestTemperatures();
soilTemp = DS18B20.getTempCByIndex(0);
soilHum = map(analogRead(soilHumPIN), 1023, 0, 0, 100);
light = map(analogRead(ldrPIN), 1023, 0, 0, 100);
}
void displaySensors() {
Serial.print("airTemp (oC): "); Serial.println(airTemp);
Serial.print("airHum (%): "); Serial.println(airHum);
Serial.print("soilTemp (oC): "); Serial.println(soilTemp);
Serial.print("soilHum (%): "); Serial.println(soilHum);
Serial.print("light (%): "); Serial.println(light);
Serial.println("");
}

Step 5: ESP-01初期設定(ATコマンド)

ESP-01をシリアルブリッジ(ATコマンドモード)として使用します。工場出荷時は115,200 baudのことが多いため、9,600 baudに変更します。
- ArduinoにBareMinimumスケッチを書き込み(シリアル競合防止)
- ESP-01を接続し、シリアルモニタを115,200 baudで開く
AT→OK確認- ボーレート変更:
AT+UART_DEF=9600,8,1,0,0 - シリアルモニタを9,600 baudに切り替え、
ATで確認 - ステーションモード設定:
AT+CWMODE=1 - WiFi接続:
AT+CWJAP="SSID","PASSWORD" WIFI CONNECTED/WIFI GOT IP確認- IP確認:
AT+CIFSR
Step 6: ESP-01動作テスト

最終配線:
- ESP-01 RX → UNO D7
- ESP-01 TX → UNO D6
- ESP-01 CH_PD → 3.3V
- ESP-01 RST → UNO D8
- ESP-01 VCC → 3.3V
- ESP-01 GND → UNO GND
ATコマンドパススルー用テストコード:
#include <SoftwareSerial.h>
SoftwareSerial esp8266(6, 7); // Rx=D6, Tx=D7
#define speed8266 9600
void setup() {
esp8266.begin(speed8266);
Serial.begin(speed8266);
Serial.println("ESP8266 Setup test - use AT commands");
}
void loop() {
while(esp8266.available()) Serial.write(esp8266.read());
while(Serial.available()) esp8266.write(Serial.read());
}
テストコマンド:
AT→ OKAT+RST→ 再起動AT+GMR→ バージョン情報AT+CWMODE?→ モード確認AT+CWLAP→ 周辺APスキャンAT+CIFSR→ IP確認
起動時自動接続用関数も用意:
void connectWiFi() {
sendData("AT+RST\r\n", 2000, 0);
sendData("AT+CWJAP=\"YOUR_SSID\",\"YOUR_PASS\"\r\n", 2000, 0);
delay(3000);
sendData("AT+CWMODE=1\r\n", 1000, 0);
sendData("AT+CIFSR\r\n", 1000, 0);
Serial.println("8266 Connected");
}
String sendData(String command, const int timeout, boolean debug) {
String response = "";
EspSerial.print(command);
long int time = millis();
while ((time + timeout) > millis()) {
while (EspSerial.available()) {
char c = EspSerial.read();
response += c;
}
}
if (debug) Serial.print(response);
return response;
}
Step 7: センサーとESP-01の統合

Step 8: ThingSpeak設定

ThingSpeakアカウント作成後、以下のフィールド構成でStatus Channelを作成:
| フィールド | 内容 |
|---|---|
| Field 1 | アクチュエーター1(ポンプ) |
| Field 2 | アクチュエーター2(ランプ) |
| Field 3 | 気温(℃) |
| Field 4 | 湿度(%) |
| Field 5 | 土壌温度(℃) |
| Field 6 | 土壌湿度(%) |
| Field 7 | 照度(%) |
| Field 8 | スペア(通信エラーカウンター等) |
Write API Keyを控えておきます。
Step 9: ステータスデータのクラウド送信

ThingSpeakへの書き込みはHTTP GETリクエストで行います:
AT+CIPSTART="TCP","184.106.153.149",80(接続開始)AT+CIPSEND=116(データ長送信)- GET文字列送信:
GET /update?api_key=YOUR_KEY&field1=...&field8=...
完全な送信コード(抜粋):
// Thingspeak
String statusChWriteKey = "YOUR_WRITE_KEY";
#include <SoftwareSerial.h>
SoftwareSerial EspSerial(6, 7);
#define HARDWARE_RESET 8
// センサー設定(Step 4と同様)
// ... 省略 ...
long writeTimingSeconds = 17;
long startWriteTiming = 0, elapsedWriteTime = 0;
boolean pump = 0, lamp = 0;
int spare = 0;
boolean error;
void setup() {
Serial.begin(9600);
pinMode(HARDWARE_RESET, OUTPUT);
digitalWrite(HARDWARE_RESET, HIGH);
DS18B20.begin();
dht.begin();
EspSerial.begin(9600);
EspHardwareReset();
startWriteTiming = millis();
}
void loop() {
start:
error = 0;
elapsedWriteTime = millis() - startWriteTiming;
if (elapsedWriteTime > (writeTimingSeconds * 1000)) {
readSensors();
writeThingSpeak();
startWriteTiming = millis();
}
if (error == 1) {
Serial.println(" <<<< ERROR >>>>");
delay(2000);
goto start;
}
}
void writeThingSpeak() {
startThingSpeakCmd();
String getStr = "GET /update?api_key=";
getStr += statusChWriteKey;
getStr += "&field1=" + String(pump);
getStr += "&field2=" + String(lamp);
getStr += "&field3=" + String(airTemp);
getStr += "&field4=" + String(airHum);
getStr += "&field5=" + String(soilTemp);
getStr += "&field6=" + String(soilHum);
getStr += "&field7=" + String(light);
getStr += "&field8=" + String(spare);
getStr += "\r\n\r\n";
sendThingSpeakGetCmd(getStr);
}
// ESPリセット、TCP接続、GET送信関数は本文参照
Step 10: Androidアプリ第1部 - ステータス監視

MIT App Inventorで作成。2秒ごとにThingSpeak Status Channelを読み取り、センサー値とアクチュエーター状態を表示。閾値判定でアラート表示(点滅)も実装。




Step 11: アクチュエーター(LED・リレー)設置

ローレベルトリガーのフォトカプラ付きリレーモジュールを使用。リレーGNDはArduino GNDと接続せずノイズ対策。
Step 12: ThingSpeakアクチュエーターチャンネル設定

アクチュエーターごとに専用チャンネルを作成(Field 1のみ使用):
- Channel 375598: 赤LED(ポンプ) - 0=OFF, 1=ON
- Channel 375599: 緑LED(ランプ) - 0=OFF, 1=ON
Step 13: アクチュエーター制御コード

ThingSpeakから「読み取り」を行うコード。10秒ごとに各チャンネルのField 1最新値を取得し、リレーを制御。
// Thingspeak
String canalID1 = "375598"; // Actuator1
String canalID2 = "375599"; // Actuator2
#include <SoftwareSerial.h>
SoftwareSerial EspSerial(6, 7);
#define HARDWARE_RESET 8
long readTimingSeconds = 10;
long startReadTiming = 0, elapsedReadTime = 0;
#define ACTUATOR1 10 // 赤LED → ポンプ
#define ACTUATOR2 12 // 緑LED → ランプ
boolean pump = 0, lamp = 0;
int spare = 0;
boolean error;
void setup() {
Serial.begin(9600);
pinMode(ACTUATOR1, OUTPUT);
pinMode(ACTUATOR2, OUTPUT);
pinMode(HARDWARE_RESET, OUTPUT);
digitalWrite(ACTUATOR1, HIGH); // リレーはLowアクティブ
digitalWrite(ACTUATOR2, HIGH);
digitalWrite(HARDWARE_RESET, HIGH);
EspSerial.begin(9600);
EspHardwareReset();
startReadTiming = millis();
}
void loop() {
start:
error = 0;
elapsedReadTime = millis() - startReadTiming;
if (elapsedReadTime > (readTimingSeconds * 1000)) {
int command = readThingSpeak(canalID1);
if (command != 9) pump = command;
delay(5000);
command = readThingSpeak(canalID2);
if (command != 9) lamp = command;
takeActions();
startReadTiming = millis();
}
if (error == 1) {
Serial.println(" <<<< ERROR >>>>");
delay(2000);
goto start;
}
}
void takeActions() {
if (pump == 1) digitalWrite(ACTUATOR1, LOW);
else digitalWrite(ACTUATOR1, HIGH);
if (lamp == 1) digitalWrite(ACTUATOR2, LOW);
else digitalWrite(ACTUATOR2, HIGH);
}
int readThingSpeak(String channelID) {
startThingSpeakCmd();
String getStr = "GET /channels/" + channelID + "/fields/1/last\r\n";
String messageDown = sendThingSpeakGetCmd(getStr);
int command = 9;
if (messageDown.length() > 7 && messageDown[5] == '1') {
command = messageDown[7] - '0';
}
return command;
}
// 共通関数(EspHardwareReset, startThingSpeakCmd, sendThingSpeakGetCmd)はStep 9と同様
Step 14: ブラウザからのアクチュエーター操作テスト

ブラウザで以下のURLを叩いて動作確認:
- ポンプON:
https://api.thingspeak.com/update?api_key=ACT1_WRITE_KEY&field1=1 - ポンプOFF:
https://api.thingspeak.com/update?api_key=ACT1_WRITE_KEY&field1=0 - ランプON:
https://api.thingspeak.com/update?api_key=ACT2_WRITE_KEY&field1=1 - ランプOFF:
https://api.thingspeak.com/update?api_key=ACT2_WRITE_KEY&field1=0
Step 15: Androidアプリ完成版

各アクチュエーターにON/OFFボタンを配置。押下時は色変化でフィードバック。



Step 16: 全機能統合

定期的なセンサー送信とアクチュエーター受信を両立させ、ESP-01フリーズ検出・自動リセット機能も追加した最終コードはGitHubで公開。
Step 17: 結論

Arduino UNOとESP-01でも十分にIoTシステムが構築可能です。詳細なファイルはGitHub: ArduFarmBot_Light、ブログはMJRoBot.orgをご覧ください。
南半球より、Marcelo
コードリポジトリ
https://github.com/Mjrovai/ArduFarmBot_Light
回路図
製造プロセス
- Raspberry Pi 4B+ IoTボードで並列C++17 SYCLコードを構築:Khronos CL/SYCL、hipSYCL、GCC/Clangツールチェーン活用
- ArduinoとMAX30100で作るリアルタイムIoT心拍数モニター
- Arduino UNO WiFi Webサーバー:ブラウザからLEDをON/OFF制御
- Arduino UNOで作るシンプル電卓 - シリアルモニターで四則演算
- Arduino UNOとATtiny85で作る携帯型POV(残像)ディスプレイ
- 非接触温度スクリーニングゲート:Arduino MKR1000で迅速・高精度な検温
- Arduino UNO + WiFiシールドで温度をWebリアルタイム表示 — シリアル出力だけでグラフ化
- ArduinoとThingSpeakで作るリアルタイム気象モニタリングシステム
- 学校向けスマート体温監視システム: 正確、リアルタイム、安全
- サーミスタを使った温度測定: Arduinoステップバイステップガイド
- Azure IoT対応スマートプール制御システム:水温・気温監視と遠隔ポンプ制御