Arduino 自动浇花系统 DIY 教程(含代码)

Illustration of an Arduino automatic plant watering system with soil moisture sensor, relay module and the small white submersible pump in a water tub

可以——用一块 Arduino Uno,大约一小时就能搭好一套自动浇花系统,全程免焊接。Uno 通过 A0 读取电容式土壤湿度传感器(capacitive soil moisture sensor)的数值,当土壤湿度低于你校准好的阈值时,就驱动继电器(relay)启动一个 5V 小型潜水泵。程序里还有安全超时,不会把花盆浇到淹水。

自动浇花系统是怎么工作的?

Arduino Uno 跑的是一个简单的“感知—判断—执行”循环:电容式土壤湿度传感器输出一个随含水量变化的电压,Uno 拿它和两个校准好的阈值比较,再由继电器模块开关水泵。这跟我们的自动晾衣架雨滴传感器教程是同一个套路——换掉传感器和执行器,逻辑照搬就行。

水装在什么容器里都行——旧冰淇淋桶就够用——水泵放进去,再接一条硅胶管通到花盆。土壤读数偏干时,水泵就抽水,直到传感器重新读到湿润为止。

需要哪些零件?

Arduino Uno、电容式土壤湿度传感器、继电器模块和 5V 水泵——整套系统就这四个模块,用杜邦线加上水泵自带的短水管连起来。Arduino Uno 兼容板(CH340G)是大脑;传感器、继电器和水泵分别负责输入和输出这两头。

你还需要一个 USB 移动电源(power bank)或闲置的 5V 手机充电器——水泵要用独立供电,接线章节会解释原因。

为什么选电容式土壤湿度传感器,而不是两叉式探针?

电容式土壤湿度传感器 v1.2(CAPSOIL)隔着阻焊层感应湿度,没有任何裸露金属接触土壤。便宜的电阻式两叉探针让电流直接流过湿土,电解会一点点吃掉表面镀层;对一套要长期无人值守运行的系统来说,电容式探头就是比它们耐用。

它的供电范围是 3.3–5.5V,但输出电压永远摆不满量程:5V 供电时,大致是在干燥空气中约 3V,泡进水里降到约 1.5V。电压越低表示土壤越湿,而且每一块的实际范围都不一样——所以校准是必做步骤,不是可选项。

提醒一句山寨批次的坑:有些批次用的是 555 定时器变体,或者稳压电路的接法只在 5V 下才正常,或者输出接近供电电压。在 5V 的 Uno 上这无伤大雅;但接 3.3V 板子之前,务必先量一下输出再决定信不信它。

传感器、继电器和水泵怎么接线?

5V 继电器模块(1WRELAY)是 Uno 的“大脑”和水泵的“肌肉”之间的桥梁——千万不要用 Arduino 引脚直接驱动水泵。Uno 单个引脚的额定电流大约是持续 20mA(绝对上限 40mA),而光是继电器线圈就要吃掉约 70mA,小水泵的工作电流在 100–220mA 左右,启动瞬间还有更大的冲击电流。引脚只负责给继电器模块发信号,大电流走的是另一条路。

模块引脚 Arduino Uno 引脚 用途
CAPSOIL VCC(红线) 5V 传感器供电
CAPSOIL GND(黑线) GND 接地
CAPSOIL AOUT(黄线) A0 模拟湿度信号
1WRELAY VCC 5V 继电器线圈与光耦供电
1WRELAY GND GND 接地
1WRELAY IN D7 触发信号(低电平有效)

大多数光耦隔离的继电器板,IN 引脚都是低电平触发(active LOW)——把 IN 拉到 GND 继电器才吸合——而且板上已经带了驱动三极管和续流二极管,线圈这一侧不需要任何额外元件。程序默认低电平触发;如果你的板子行为相反,把代码顶部那两个常量对调即可。

Arduino、土壤湿度传感器、继电器模块、潜水泵和移动电源的卡通接线总览
信号线接到 Uno;水泵的供电回路只经过继电器的触点。
说明
移动电源 5V(+) 继电器 COM 端子 水泵供电从开关这一侧进入
继电器 NO 端子 水泵红线 NO = 常开(normally open),水泵默认断电
水泵黑线 移动电源 GND(−) 完成水泵回路

小型潜水泵要用那个独立的 5V 电源供电,不要接 Uno 的 5V 引脚。电机的电流冲击一旦经过 Uno 的稳压器,就会出现经典的“水泵一转 Arduino 就重启”问题。由于继电器触点是电气隔离的,水泵回路甚至不需要和 Uno 共地。

两条摆放原则:传感器的线要避开水泵的电线,因为电机噪声会让模拟读数抖动;水泵必须完全泡在水里——它靠水散热,绝对不能干转。

剖面插图:潜水泵完全没入水桶水面之下,水管一路接到花盆
任何时候都让水泵保持在水面以下——它靠自己抽的水来散热。

土壤湿度传感器怎么校准?

电容式土壤湿度传感器永远读不满 0–1023 的 ADC 量程,而且没有两块读数完全一样,所以校准就是做两次快速测量。打开串口监视器,先记下探头在空气中干燥时的原始读数(5V Uno 上通常接近 590),再记下泡进一杯水里的读数(通常在 270 左右)。这两个数字就是程序里的 RAW_AIRRAW_WATER

探头只插到 PCB 上印的那条限位线为止,SMD 元件要留在土面以上,也不要被水溅到。把电子元件埋进土里,等于放弃了你选这款传感器换来的防腐蚀优势。

对比图:土壤湿度传感器在花盆中正确与错误的插入深度
只插到限位线为止——顶部的元件必须留在土壤外面。

安全的浇水代码长什么样?

下面这份 Arduino 程序用的是一个湿度区间,而不是单一阈值:湿度跌破 35% 才开泵,回升超过 55% 才停。单一阈值会让继电器在临界点上反复跳动;一个区间能让每次浇水都干脆地开始、干脆地结束。

程序内置了三道安全网。水泵每个周期最多运转约八秒;两次浇水至少间隔 30 分钟;任何超出校准范围的读数都会把水泵锁定关闭——传感器一旦脱落,A0 悬空后的读数通常会飘出这个区间。水在土壤里渗透得很慢,所以“短脉冲加休息”比一次浇个透更有效。

// 自动浇花系统 — Arduino Uno + 电容式土壤传感器 v1.2 + 继电器 + 5V 水泵
// 传感器 AOUT 接 A0,继电器 IN 接 D7。大多数光耦继电器板为低电平触发(ACTIVE LOW)。

const int SENSOR_PIN = A0;
const int RELAY_PIN  = 7;

const int RELAY_ON  = LOW;   // 如果你的继电器板是高电平触发,把这两个值对调
const int RELAY_OFF = HIGH;

// ---- 校准:换成你自己的读数(见校准章节) ----
const int RAW_AIR   = 590;   // 原始读数:探头在空气中干燥状态
const int RAW_WATER = 270;   // 原始读数:探头泡在一杯水里

// ---- 浇水区间(滞回) ----
const int START_PUMP_BELOW_PCT = 35;  // 比这个更干 -> 开始浇水
const int STOP_PUMP_ABOVE_PCT  = 55;  // 比这个更湿 -> 停止浇水

// ---- 安全限制 ----
const unsigned long MAX_PUMP_MS = 8000UL;                // 每个周期最多抽水 8 秒
const unsigned long MIN_GAP_MS  = 30UL * 60UL * 1000UL;  // 两次浇水至少间隔 30 分钟
const int FAULT_MARGIN = 60;  // 读数超出校准范围 +/- 余量 = 传感器故障

bool pumping = false;
unsigned long pumpStartedAt  = 0;
unsigned long lastWateringAt = 0;

void setup() {
  digitalWrite(RELAY_PIN, RELAY_OFF);  // 先写入 OFF 状态,再启用引脚
  pinMode(RELAY_PIN, OUTPUT);
  Serial.begin(9600);
}

int readMoisturePct() {
  long sum = 0;
  for (int i = 0; i < 10; i++) {  // 取 10 次读数平均,抹平电机噪声
    sum += analogRead(SENSOR_PIN);
    delay(20);
  }
  int raw = sum / 10;

  // 引脚悬空时读数不定 -> 当作故障处理。
  if (raw > RAW_AIR + FAULT_MARGIN || raw < RAW_WATER - FAULT_MARGIN) {
    return -1;
  }
  // 原始值越低越湿。映射到 0%(空气干燥).. 100%(水中)。
  int pct = map(raw, RAW_AIR, RAW_WATER, 0, 100);
  return constrain(pct, 0, 100);
}

void loop() {
  int moisture = readMoisturePct();
  unsigned long now = millis();

  if (moisture < 0) {  // 传感器故障 -> 故障安全:水泵锁定关闭
    digitalWrite(RELAY_PIN, RELAY_OFF);
    pumping = false;
    Serial.println("Sensor fault - pump locked off");
    delay(5000);
    return;
  }

  if (!pumping) {
    bool dryEnough = (moisture <= START_PUMP_BELOW_PCT);
    bool rested    = (lastWateringAt == 0) || (now - lastWateringAt >= MIN_GAP_MS);
    if (dryEnough && rested) {
      pumping = true;
      pumpStartedAt = now;
      digitalWrite(RELAY_PIN, RELAY_ON);
    }
  } else {
    bool wetEnough = (moisture >= STOP_PUMP_ABOVE_PCT);
    bool timedOut  = (now - pumpStartedAt >= MAX_PUMP_MS);
    if (wetEnough || timedOut) {
      pumping = false;
      lastWateringAt = now;
      digitalWrite(RELAY_PIN, RELAY_OFF);
    }
  }

  Serial.print("Moisture: ");
  Serial.print(moisture);
  Serial.println(" %");
  delay(1000);
}

想在动手之前先看一个类似的“传感器—继电器—水泵”组合是怎么搭起来的?这条视频讲的正是同一条链路:

真实客户最常踩的坑

传感器引脚没有标注。“air sensor takde label VCC/GND/SIG”——有些批次的传感器丝印很淡,甚至完全没印。CAPSOIL 传感器附带的线材通常约定红 = VCC、黑 = GND、黄 = 模拟输出;如果你的线色不一样,先用万用表确认哪个焊盘是供电,再通电。

用 Uno 的 5V 引脚给水泵供电。在桌面上测试看着一切正常,浇到一半 Uno 就重启。给水泵配一个独立的移动电源或充电器,这种“灵异重启”就会消失。

只用一个阈值,而不是一个区间。在阈值附近读数会抖动几个格,继电器就跟着快速开开关关。程序里的滞回区间就是专门用来防这个的。

把传感器一路插到接口。探头部分防腐蚀,顶部的元件可不防。只插到限位线,浇水时也别让水花溅到电子元件上。

常见问题

电容式传感器可以配 ESP32 或其他 3.3V 板子用吗?

可以——供电范围是 3.3–5.5V。但因为部分山寨批次在 5V 以下工作不正常,或输出接近供电电压,所以先单独给传感器上电、量一下输出,确认它不超过 3.3V,再接到 3.3V 的模拟输入。

为什么我的水泵一直抽到花盆溢水?

多半是传感器没插在水实际流到的区域,所以它永远读不到“湿”。把探头挪到出水管滴水处附近的根部区域,并保留程序里的 MAX_PUMP_MS 超时——它是限制单次浇水量的最后一道保险。

整套系统可以用电池跑吗?

一个 USB 移动电源可以同时带动 Uno 和水泵,不过很多移动电源在负载太小时会自动休眠——先整夜测试一下你手上那个。想要更精简的 18650 供电方案,参考我们的 TP4056 18650 充电教程

为什么 Arduino 开机时继电器会咔哒响一声?

常见原因是初始化顺序:pinMode(pin, OUTPUT) 默认把引脚拉低,在低电平触发的板子上,继电器会先吸合,直到 OFF 写入才松开。这份程序先写入 OFF 状态再启用引脚,所以整个启动过程继电器保持释放;至于上电瞬间模块自身发出的一声轻响,对水泵来说无伤大雅。

水泵可以干转吗?

不行——小型潜水泵靠水来散热和润滑。让进水口一直位于水面以下,水桶见底前记得加水,万一水真的抽完了,就靠代码里的水泵超时来限制损伤。

最后更新:2026年8月。有问题?WhatsApp 联系我们

Leave a Reply

Your email address will not be published. Required fields are marked *