ArduFarmBot 第二部分:构建远程 IoT 控制站
组件与物料
| 组件 | 说明 | 数量 |
|---|---|---|
| Arduino Nano R3 | 主控制器 | 1 |
| Espressif ESP8266 ESP-01 | WiFi 模块 | 1 |
| DHT22 温湿度传感器 | 空气温湿度 | 1 |
| KY-018 光敏模块 | 光照强度 | 1 |
| 土壤湿度传感器 | 土壤含水量 | 1 |
| 继电器模块 | 控制水泵/灯泡 | 2 |
| LCD 20×4 (I2C) | 本地显示 | 1 |
| 12mm 轻触开关 | 本地按键操作 | 3 |
| LED (通用) | 状态指示 | 3 |
应用与在线服务
| 服务 | 说明 |
|---|---|
| ThingSpeak API | 云端数据存储与可视化 |
项目简介



本教程延续《ArduFarmBot:用 Arduino 和 IoT 控制家庭番茄农场》(第一部分)。第一部分实现了本地控制站:采集温度、空气湿度、光照、土壤湿度,自动决定加热灯与水泵动作,并支持本地按键手动干预。
第二部分引入 IoT 方案:通过互联网实现远程手动干预。数据上传至云存储(ThingSpeak.com),专用网页“远程控制页”近实时显示数据并提供水泵/灯泡远程开关控制。
步骤 1:物料清单 (BOM)
- Arduino Nano ($7.50)
- DHT22/DHT11 温湿度传感器 ($3.66)
- AD-018 光敏模块 ($0.71)
- 2× 土壤湿度传感器 ($1.99,可自制)
- LCD I2C 20×4 ($13.99)
- LED ($0.20)
- ESP8266 ESP-01 WiFi 模块 ($5.96)
- 有源蜂鸣器 KY-012 ($0.60)
- 2× 5V 继电器模块 ($11.60)
- 跳线、电阻 (10K/2.2K/1K/220Ω)
- Arduino Nano 扩展板 “Funduino” ($7.28)
- 4键薄膜键盘 ($6.65)
- 塑料外壳
步骤 2:硬件完善







在第一部分本地站基础上仅增加 ESP8266。框图展示所有引脚连接。注意电平转换:ESP8266 工作电压 3.3V,Rx 不能直连 Nano Tx (D3),需分压电阻降压。图示详细连接法。ESP8266 通过 SoftSerial 接 Nano D2(Tx)/D3(Rx),也可用硬件串口 0/1(上传代码时需断开)。
蜂鸣器接 D17 (A3),通讯故障报警用,测试阶段使用,最终版可选。
ESP8266 测试/配置代码: FC9DBPKIT682FY7.ino
步骤 3:ESP8266 连网

上电先复位 CH_PD 引脚:
void reset8266() {
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 ");
}
连接本地 WiFi(修改代码中的 USERNAME/PASSWORD),设为 Station 模式 (CWMODE=1):
void connectWiFi() {
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():
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

所有采集数据上传至 ThingSpeak 免费云服务。loop() 中调用 updateDataThingSpeak():
void updateDataThingSpeak() {
startThingSpeakCmd();
cmd = msg;
cmd += "&field1="; cmd += tempDHT; // 温度
cmd += "&field2="; cmd += humDHT; // 湿度
cmd += "&field3="; cmd += lumen; // 光照
cmd += "&field4="; cmd += soilMoist; // 土壤湿度
cmd += "&field5="; cmd += pumpStatus; // 水泵状态
cmd += "&field6="; cmd += lampStatus; // 灯泡状态
cmd += "\r\n";
sendThingSpeakCmd();
}
建立连接 startThingSpeakCmd():
void startThingSpeakCmd() {
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 = "";
}
发送数据 sendThingSpeakCmd():
String sendThingSpeakCmd() {
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:网页远程控制执行器


数据上云后,需从云端下发指令控制执行器。在 ThingSpeak 创建控制字段:
- Field 7: 1=开水泵, 0=关水泵
- Field 8: 1=开灯泡, 0=关灯泡
通过 API 写入:api.thingspeak.com/update?key=YOUR_WRITE_KEY&field7=1
读取最新指令:api.thingspeak.com/channels/CHANNEL_ID/fields/7/last
读取函数 readLastDataField7():
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 转数字
return command;
}
Field 8 同理。接收指令并执行 receiveCommands():
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);
}
本地按键操作时同步更新 ThingSpeak 字段 7/8,保持本地/远程状态一致。执行函数 aplyCmd() 统一驱动执行器。
协调策略:每循环检测本地按键;约每 20 秒轮询远程指令;约每 75 秒读传感器、自动控制、上传数据(自动控制优先级最高,覆盖手动操作)。两个定时器分别管理:
long sampleTimingSeconds = 75; // 传感器采样周期
long poolingRemoteCmdSeconds = 20; // 远程指令轮询周期
重写 loop():
void loop() {
elapsedRemoteCmdTime = millis() - startRemoteCmdTiming;
elapsedTime = millis() - startTiming;
reverseElapsedTimeSeconds = round(sampleTimingSeconds - elapsedTime/1000);
readLocalCmd(); showDataLCD();
if (elapsedRemoteCmdTime > (poolingRemoteCmdSeconds*1000)) {
receiveCommands(); updateDataThingSpeak(); startRemoteCmdTiming = millis();
}
if (elapsedTime > (sampleTimingSeconds*1000)) {
readSensors(); autoControlPlantation(); updateDataThingSpeak(); startTiming = millis();
}
}
步骤 6:专用网页界面

浏览器直接操作 ThingSpeak 不够优雅,开发专用网页交互 ThingSpeak:
- 读取字段 1-4 传感器数据
- 读取字段 5-6 执行器状态
- 写入字段 7-8 控制指令
- 读取 Yahoo 天气数据(扩展功能)
托管于 Byethost 免费空间,应用 Coursera/密歇根大学《网页设计》课程所学(HTML5/CSS3/JavaScript)。源码打包下载: FQOK9ENIT653YX5.zip
步骤 7:传感器-执行器矩阵决策



将复杂工况抽象为矩阵:每行一种传感器组合条件,对应执行器动作。Excel 表格含筛选版与多条件版。转为代码数组 SDf[18][10](8 传感器状态 + 2 执行器输出):
// +---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 列条件全真则执行后 2 列动作。另建数组 snsSts[8] 记录传感器状态用于日志。
Excel 文件下载: F2HWXBYITA8WIN1.xlsx
步骤 8:代码优化


- 显示优化: LCD 默认关闭,按“传感器读取”键切换开关,开启时刷新读数但不影响自动控制
- 初始化向导: 上电显示设置项(低温阈值、干湿土阈值、暗光阈值、水泵开时长、扫描周期、软件版本),按“传感器”键确认进入运行
- EEPROM 日志: 每周期记录传感器状态位串+水泵/灯泡状态,函数
storeDataLogEEPROM()循环写入 1023 字节空间 - 模块化拆分: 新增
communication.ino(ThingSpeak/ESP8266) 和stationCredentials.h(通道 ID/密钥) - PROGMEM 优化: 常量存 Flash 不占 SRAM,如
const PROGMEM byte DHTPIN = 5;需引用avr/pgmspace.h - 精简调试输出: 注释/删除
Serial.print()节省内存
完整代码分文件下载:
- FTUT5VIIT67U8ME.ino (主程序)
- FWMIPSSIT67U8MG.ino (通讯模块)
- FJPGZNKIT67U8MK.ino (传感器读取)
- FQH3X9VIT67U8NA.ino (自动控制)
- F15MY4YIT67U8NB.ino (本地按键)
- FVU64X1IT67U8NC.h (凭据头文件)
- FZ057E3IT67U8P5.h (配置头文件)
步骤 9:MJRovai 家庭农场实测











第二季从播种到移栽筛选最佳 6 株(约 45 天)全程记录:


步骤 10:总结


项目暂告段落!希望能助你探索电子、IoT 与机器人世界。第三部分将发布有机番茄酱意面食谱(玩笑)。
推荐新作《ArduFarmBot, the book!》系统整理全项目:

书籍涵盖 LCD/OLED、LED/按键、继电器驱动泵/灯、DHT22/DS18B20/YL69/LDR 等传感器。含详细文字、框图、高清照片、Fritzing 原理图、GitHub 完整代码、YouTube 视频。三部分内容:
- Arduino Nano 本地版(自动+手动)
- ESP8266-01 联网版,接入 ThingSpeak,开发网页远程控制
- NodeMCU ESP8266-12E 替代前二者,引入 Blynk 平台






书籍已上架 Amazon,欢迎下载评价,留言交流!更多项目访问 MJRoBot.org。来自南半球的问候,下个项目见!
代码片段汇总
- 代码片段 #1: ESP8266 复位函数
- 代码片段 #2: WiFi 连接函数
- 代码片段 #3: AT 命令发送函数
- 代码片段 #4: ThingSpeak 数据上传
- 代码片段 #5: ThingSpeak 连接建立
- 代码片段 #6: ThingSpeak 数据发送
- 代码片段 #9: 读取 Field 7 指令
- 代码片段 #10: 接收远程指令执行
- 代码片段 #11: 统一执行器驱动
- 代码片段 #12: 双定时器变量
- 代码片段 #13: 主循环调度
- 代码片段 #14: 传感器-执行器决策矩阵
- 代码片段 #15: EEPROM 日志记录
GitHub 仓库
https://github.com/Mjrovai/ArduFarmBot
原理图
ardufarmbot_qpNcBDX6Jr.fzz制造工艺
- Arduino驱动的六发鸡尾酒自动调配站 – 打造你的专属智能调酒机
- 多位置温度传感器系统,助力智能家居集成
- MotionSense:基于 Arduino 与 ESP8266 的智能入侵检测系统
- 3D 打印机防火卫士:智能传感器安全系统
- 基于 Arduino、Yaler 与 IFTTT 构建 Web 化 IoT 仪表盘
- Arduino 简易排斥式磁悬浮:电路与 DIY 指南
- 进阶遥控夹爪机器人:精密抓取 DIY 指南
- 高性能 Arduino 气象气球数据记录器
- 自制 10 美元便携 Arduino 气象站 - Nano + DHT22 + BMP180 + LCD
- 进阶微控制器实验平台:ESP32、Arduino、PIC 与多传感器模块集成
- CoroFence 热成像检测器:Panasonic GridEye + PIR + IoT 集成
- 物联网实施新趋势:洞察与策略