IoT for Beginners 数字农业第 2 课:土壤湿度检测完整实战——从传感器类型、I²C/UART/SPI 通信协议到湿度校准
【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners
本课是 IoT for Beginners 项目(数字农业系列)"检测土壤湿度"一课的完整技术指南,对应仓库 2-farm/lessons/2-detect-soil-moisture。你将理解水对植物生长的作用与两类土壤湿度传感器的原理,系统掌握传感器与 IoT 设备之间 GPIO、模拟引脚、I²C、UART、SPI 与无线通信的工作机制,并分别在 Wio Terminal、Raspberry Pi 与虚拟设备上完成土壤湿度读取,最后通过重量法(gravimetric)校准把 0–1023 的原始读数换算成真实的土壤含水量百分比。
为什么要检测土壤湿度
上一课我们通过测量环境温度来预测植物生长。温度可以控制,但成本高昂,需要受控环境。对于植物来说最容易控制的环境变量是水——从大型灌溉系统到提着浇水壶的孩子,每天都在做这件事。
水对植物的作用体现在三个方面:
- 光合作用(Photosynthesis):植物利用水、二氧化碳和光发生化学反应,生成碳水化合物和氧气;
- 蒸腾作用(Transpiration):植物通过叶片气孔利用水扩散吸收空气中的二氧化碳。这一过程同时把养分输送到植物全身,并像人类出汗一样为植物降温;
- 结构支撑:植物约 90% 由水构成(人类仅约 60%),水分使细胞保持坚挺。缺水时植物会萎蔫,最终死亡。
植物的根系从土壤水分中吸水。土壤水分过少,植物无法吸收足够的水分生长;水分过多,根系又无法吸收足够的氧气,导致根系死亡、养分供给中断。因此对农民而言,最佳生长状态要求土壤"不干不涝"。IoT 设备通过实时测量土壤湿度,让农民只在需要时灌溉,这正是本课的核心价值。
两类土壤湿度传感器:电阻式与电容式
测量土壤湿度有两类主流传感器,它们都是模拟传感器,输出一个随湿度变化的电压:
电阻式(Resistive):传感器有两条探针插入土壤,电流从一根探针发出、另一根接收,通过测量土壤电阻来判断湿度。水是电的良导体,土壤含水量越高、电阻越低。用两根相隔几厘米的金属(如钉子)配合万用表,即可自制一个简易电阻式传感器。
电容式(Capacitive):通过测量正负电极板之间可储存的电荷量(即电容)来判断湿度。土壤电容随含水量变化,可转换为 IoT 设备可测量的电压:土壤越湿,输出电压越低(与电阻式正好相反,电阻式是越湿电压越高)。
📌 本项目三个硬件平台使用的都是 Grove 电容式土壤湿度传感器(Seeed 出品,防腐蚀),其原理即"湿度升高 → 电容变化 → 电压下降"。
既然传感器输出的是电压,它如何进入你的代码?这就引出本课的技术核心——传感器与 IoT 设备的通信机制。
传感器如何与 IoT 设备通信
要让电阻测量变成一个代码中可用的数字,既需要硬件连接,也需要通信协议(一套明确定义的数据收发规则)。以电容式土壤湿度传感器为例,需要回答一连串问题:传感器如何连到 IoT 设备?模拟电压需要 ADC 转成数字表示,二进制位按多长时间发送?电压持续 0.1s 是一个"1"位、两个连续"1"位还是十个?数值从哪里开始,00001101是 25 还是前 5 位属于上一个值?——硬件提供物理连接,而通信协议保证数据以正确方式被收发和解读。
GPIO 引脚
GPIO(通用输入输出)是一组可用于把硬件连接到 IoT 设备的引脚,常见于 Raspberry Pi、Wio Terminal 等开发套件。部分引脚提供电压(通常 3.3V 或 5V),部分为接地,其余可编程设置为输出电压(输出)或接收电压(输入)。
仅关心"开/关"(高电平/低电平)时,可直接用 GPIO 连接数字传感器与执行器:
- 按钮:把按钮接在 5V 引脚与一个输入引脚之间。按下时电路导通,代码读到高电平(5V→1),松开则为低电平(0V→0)。注意代码读到的并非真实电压,而是基于阈值判定的数字信号 1/0;
- LED:把 LED 接在输出引脚与接地引脚之间(必须串电阻防止烧毁)。输出引脚置高电平即送出 3.3V,点亮 LED。
更高级的传感器则通过 GPIO 直接收发数字数据,或借助带 ADC/DAC 的控制板与模拟传感器/执行器通信。例如 Raspberry Pi 的 Grove Base Hat 内部就有把模拟信号转为数字、再经 GPIO 送出的硬件。
模拟引脚与 ADC
Arduino 等部分设备提供模拟引脚。它与 GPIO 引脚相同,但内置 ADC(模数转换器)把电压范围转换为数值,通常为 10 位分辨率,即把电压映射为 0–1,023 的整数:
- 3.3V 板子上,传感器返回 3.3V → 读值 1,023;
- 若返回 1.65V → 读值 511。
土壤湿度传感器依赖电压信号,因此使用模拟引脚,读数范围为 0–1,023。此前的光照传感器同样如此:Wio Terminal 上接模拟引脚;Raspberry Pi 上接 Base Hat 的模拟引脚(内含 ADC 与 GPIO 通信);虚拟设备则直接模拟 0–1,023 的模拟引脚读数。
内部集成电路 I²C
I²C(读作I-squared-C)是一个多控制器、多外设协议,任何连接的设备都可通过 I²C 总线充当控制器或外设。数据以带地址的数据包发送,每个数据包包含目标设备的地址。旧术语"主/从"(master/slave)因与奴役制的关联正被弃用,Open Source Hardware Association 已采用 controller/peripheral 说法,但你仍可能在老资料中看到旧术语。
设备接入 I²C 总线时使用其地址,该地址通常固化在设备中。例如 Seeed 的每类 Grove 传感器地址相同——所有光传感器同址、所有按钮同址且与光传感器不同。部分设备可通过改跳线或焊接引脚来改变地址。
I²C 总线由 2 根主信号线加 2 根电源线组成:
| 线 | 名称 | 说明 |
|---|---|---|
| SDA | 串行数据线 | 用于设备间发送数据 |
| SCL | 串行时钟 | 以控制器设定的速率发送时钟信号 |
| VCC | 电源(电压公共集电极) | 设备供电;通过上拉电阻接入 SDA 与 SCL 为其供电,无设备充当控制器时信号被关闭 |
| GND | 地 | 为电路提供公共接地 |
发送数据时,某设备先发出起始条件表明准备就绪,随即成为控制器;控制器发送目标设备地址及读/写标志;数据传完后控制器发出停止条件,之后另一设备可成为控制器继续收发。
I²C 有速度限制,3 种模式固定速率运行:
- High Speed 模式:最快,3.4 Mbps(兆比特每秒),但支持设备极少;
- Fast 模式:400 Kbps,Raspberry Pi 即被限制在此速率;
- Standard 模式:100 Kbps。
💁 若使用 Raspberry Pi + Grove Base Hat,可在板上看到多个 I²C 插槽用于连接 I²C 传感器。模拟 Grove 传感器也借助 ADC 经 I²C 把模拟值作为数字数据发送——由于 Raspberry Pi 只支持数字引脚,你之前用的光传感器其实是在"模拟一个模拟引脚",数值经 I²C 传输。
通用异步收发器 UART
UART 通过物理电路让两台设备通信。每台设备有 2 个通信引脚——发送(Tx)与接收(Rx),第一台的 Tx 接第二台的 Rx,第二台的 Tx 接第一台的 Rx,实现双向数据传输:
- 设备 1 从 Tx 发数据,设备 2 在 Rx 接收;
- 设备 2 从 Tx 发数据,设备 1 在 Rx 接收。
🎓 数据逐位发送,称为串行通信。多数操作系统与微控制器都提供串行端口,即代码可用的收发串行数据的连接。
UART 设备有波特率(Baud rate,也称符号率),表示每秒收发多少比特。常见波特率为 9,600,即每秒发送 9,600 个比特(0 和 1)。UART 使用起始位与停止位——发送前先发一个起始位表示"即将发送一个字节(8 位)",8 位发送完后再发停止位。UART 速率取决于硬件,最快的实现也不超过 6.5 Mbps。你可以用 GPIO 引脚实现 UART:将一个引脚设为 Tx、另一个设为 Rx,再连接到另一台设备。
💁 Raspberry Pi + Grove Base Hat 的板上有 UART 插槽,可用于连接使用 UART 协议的传感器。
串行外设接口 SPI
SPI 专为短距离通信设计,例如微控制器与 Flash 存储器的通信。它基于控制器/外设模型:单个控制器(通常是 IoT 设备的处理器)与多个外设交互,控制器通过选择某个外设并发送或请求数据来掌控一切(与 I²C 相同,controller/peripheral 是较新的术语,旧术语仍会出现)。
SPI 控制器使用 3 根线,外加每台外设 1 根额外线;外设则用 4 根线:
| 线 | 名称 | 说明 |
|---|---|---|
| COPI | 控制器输出、外设输入 | 用于从控制器向外设发送数据 |
| CIPO | 控制器输入、外设输出 | 用于从外设向控制器发送数据 |
| SCLK | 串行时钟 | 以控制器设定的速率发送时钟信号 |
| CS | 片选 | 控制器为每台外设各有一根线,每根线连到对应外设的 CS 引脚 |
CS 线用于每次激活一台外设,使其通过 COPI 与 CIPO 通信。控制器需要切换外设时,先停用当前外设的 CS 线,再激活目标外设的 CS 线。SPI 是全双工的——控制器可同时经 COPI 与 CIPO 与同一外设收发数据。SPI 用 SCLK 上的时钟信号同步设备,因此不像 UART 那样需要起始/停止位。SPI 没有定义速度上限,实现常可达到每秒数兆字节的吞吐。IoT 开发套件常用部分 GPIO 引脚支持 SPI,例如 Raspberry Pi 可用 GPIO 19、21、23、24、26 引脚。
无线通信
部分传感器可通过标准化无线协议通信:蓝牙(主要是低功耗蓝牙 BLE)、LoRaWAN(Long Range 低功耗组网协议)或WiFi,从而支持与 IoT 设备无物理连接的远端传感器。
典型例子是商用土壤湿度传感器:在田间测量湿度后,通过 LoRaWAN 把数据发给 Hub 设备,由 Hub 处理或经互联网转发。这样传感器可与管理数据的 IoT 设备分离,降低功耗,也免去大规模 WiFi 网络或长距离线缆。BLE 则流行于高级可穿戴传感器(如腕上健身追踪器),多个传感器组合后经 BLE 把数据发给手机等 IoT 设备。
商用设备还有一种热门连接方式Zigbee:利用 WiFi 在设备间构成网状网络,每个设备尽量连接更多邻近设备,形成蛛网般的海量连接。某台设备要发消息到互联网时,可发给最近的设备,再由它们接力转发,直到抵达协调器并接入互联网。
🐝 Zigbee 的名字源于蜜蜂回巢后的"摇摆舞"(waggle dance)。
实操:用三类平台测量土壤湿度
用土壤湿度传感器、一台 IoT 设备和一株盆栽(或附近一小块土壤),即可实测湿度。完整分步指南见以下三个仓库文档:
- Arduino - Wio Terminal 指南
- 单板计算机 - Raspberry Pi 指南
- 单板计算机 - 虚拟设备指南
三者都基于 Grove 电容式土壤湿度传感器,因此输出规律一致:浇水后读数(电压相关值)下降。
Wio Terminal(Arduino)
Wio Terminal 使用板载 ADC 从模拟引脚读取 0–1,023 的值。接线要点:传感器经 Grove 线插入右侧 Grove 插座(看屏幕时离电源按钮最远的那个);插入土壤时不得超过传感器上的白色"最高位置线"。程序无需专门传感器库,直接用 Arduino 内置analogRead。本项目提供的完整实现见 code/wio-terminal/soil-moisture-sensor/src/main.cpp:
#include <Arduino.h> void setup() { pinMode(A0, INPUT); // 把 A0 设为输入,可读取电压 } void loop() { int soil_moisture = analogRead(A0); // 读取 0-1023 的模拟值 Serial.print("Soil Moisture: "); Serial.println(soil_moisture); delay(5000); // 每 5 秒读取一次 }对应 platformio.ini 配置:平台atmelsam、板卡seeed_wio_terminal、框架arduino。教程中的代码使用delay(10000)(10 秒),仓库内的实际实现为 5 秒,可按需调整采样间隔。串口监视器输出示例(浇水后电压明显下降):
Soil Moisture: 526 Soil Moisture: 529 Soil Moisture: 521 Soil Moisture: 494 Soil Moisture: 454 Soil Moisture: 456 Soil Moisture: 395Raspberry Pi(单板计算机)
Raspberry Pi 只支持数字引脚,因此电容式传感器接到 Grove Base Hat 上标有A0的模拟插座(GPIO 引脚旁那排插座左数第二个),由 Base Hat 的 10 位 ADC 把电压转为 1–1,023 的数字信号,再经 I²C 通过 GPIO 引脚送给 Pi。程序使用 Grove Python 库的ADC类。完整实现见 code/pi/soil-moisture-sensor/app.py:
import time from grove.adc import ADC adc = ADC() # 与 Base Hat 上的 ADC 交互 while True: soil_moisture = adc.read(0) # 读取 A0(引脚 0) print("Soil moisture:", soil_moisture) time.sleep(10) # 每 10 秒读取一次运行python3 app.py后,控制台输出湿度读数(数值随加水而下降):
pi@raspberrypi:~/soil-moisture-sensor $ python3 app.py Soil moisture: 615 Soil moisture: 612 Soil moisture: 498 Soil moisture: 493 Soil moisture: 490 Soil Moisture: 388虚拟设备(CounterFit)
没有实体硬件时,可用 CounterFit 模拟 Grove 电容式土壤湿度传感器,实验流程与真实 Raspberry Pi 完全一致:在 CounterFit Web 应用中创建Soil Moisture传感器(Sensor type选Soil Moisture、Units保持NoUnits、Pin设为 0),然后用模拟 ADC 读取。完整实现见 code/virtual-device/soil-moisture-sensor/app.py:
from counterfit_connection import CounterFitConnection CounterFitConnection.init('127.0.0.1', 5000) # 连接 CounterFit import time from counterfit_shims_grove.adc import ADC adc = ADC() # 虚拟 ADC,对接 CounterFit 传感器 while True: soil_moisture = adc.read(0) # 读取引脚 0 的模拟值 print("Soil moisture:", soil_moisture) time.sleep(10)在 CounterFit 中可手动输入Value后点Set(该值即传感器返回值),或勾选Random并设置Min/Max让每次读取返回区间内随机数,观察控制台输出随设置变化。
传感器校准:把 0–1023 变成真实的含水量
传感器本质是测量电阻或电容这类电气属性。电阻以欧姆(Ω)度量,是电流通过物质的阻碍程度;电容以法拉(F)度量,是元件或电路收集储存电能的能力。但"22.5 kΩ"对使用毫无意义,必须把测量值校准为有用单位——即把测得值与真实被测量对应起来,从而把新读数换算成正确单位。
部分传感器出厂即已校准。例如上一课用的温度传感器已能直接返回 °C:工厂把首枚传感器暴露在一系列已知温度下测出电阻,据此构建从 Ω 到 °C 的换算公式(计算温度电阻的公式即 Steinhart–Hart 方程)。
土壤湿度的度量方式
土壤湿度用重量含水量或体积含水量度量:
- 重量法(Gravimetric):单位重量土壤中水的重量,即每千克干土中的水千克数;
- 体积法(Volumetric):单位体积土壤中水的体积,即每立方米干土中的水立方米数。
🇺🇸 由于单位一致性,美制单位下可用磅替代千克、用立方英尺替代立方米。
校准步骤与图表插值
土壤湿度传感器的读数不仅随湿度变化,也随土壤类型变化——土壤成分会改变其电气特性。理想做法是把传感器读数与更科学的方法测得的数值对照校准:例如实验室每年几次采集特定田块样本计算重量含水量,用这些数值匹配传感器读数完成校准。
校准过程如下(详见 2-farm/lessons/2-detect-soil-moisture/assignment.md):
- 用土壤湿度传感器读取土壤样本的湿度读数,记下该读数;
- 取一份土壤样本称重(湿重 W_wet);
- 烘干土壤——最佳方式是用 110°C(230°F)暖炉烘数小时,也可用阳光或温暖干燥处风干,直至土壤完全干燥、呈粉状松散。实验室最高精度做法是烘 48–72 小时,烘干越久样本越干、结果越准;
- 再次称重(干重 W_dry)——若用烘箱务必等冷却后再称。
重量法含水量按下式计算:
土壤含水量 % =(W_wet − W_dry)÷ W_dry × 100
举例:湿重 212g、干重 197g,则 (212 − 197) / 197 × 100 = 7.6%,即该样本重量含水量为 7.6%。
把传感器读数(电压/ADC 值)与实验室测得的含水量绘图并拟合直线:先为多个不同湿度的土壤样本记录电压,再逐一在实验室烘干称重获得含水量,将若干组数据点连成拟合线。之后 IoT 设备采集的任意读数,都能沿曲线读出真实土壤含水量。
💁 电阻式传感器:湿度升高 → 电压升高,曲线向上倾斜;电容式传感器:湿度升高 → 电压下降,曲线向下倾斜。
这种方案意味着农民只需为一个田块做几次实验室测量,之后即可用 IoT 设备快速完成湿度测量,大幅缩短测量时间。
课后挑战与延伸思考
电阻式与电容式土壤湿度传感器差异显著:请思考两者在工作原理(电阻 vs 电容)、耐腐蚀性、成本、寿命上的差异,哪一种更适合农民使用?答案在发展中国家与发达国家之间是否会改变?
复习与自修
围绕本课硬件与协议继续深入:
- 本课配套图形笔记见 sketchnotes/lesson-6.jpg;
- 若需从零搭建环境,可回看 1-getting-started 第 1 课 中的 Wio Terminal、Raspberry Pi 与虚拟设备搭建说明;
- 下一篇将基于本课湿度数据实现自动化浇灌系统(见 2-farm/lessons/3-automated-plant-watering);
- 依据本课校准方法,完成 校准你的传感器 实践作业:至少采集 3 个不同湿度的样本,绘制"含水量 % ↔ 传感器读数"校准图,并把一次新读数换算为重量法含水量。
【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考