传感器 · 土壤湿度 · YL-38/YL-69

土壤湿度传感器 STM32F103 接线与代码

给单片机装一个「土够不够湿」的检测器:两根金属叉探头插进土里,土壤含水量越高,探头间等效电阻越小;AO 输出连续湿润程度,DO 输出「过没过阈值」一个开关量。本页是 STM32F103C8T6 + 该模块 + 0.96 OLED 的完整实机验证记录,含接线表、完整代码和 PlatformIO 工程下载。最值得看的是「探头会腐蚀」和「STATE 三档和 DO 翻转点是两回事」这两段。

Quick Facts

  • 传感器:土壤湿度传感器(腐蚀式探头 + LM393 比较器小板)
  • 供电:3.3V,与 STM32 共地(别接 5V)
  • 接线:AO→PA0,DO→PA1,共 4 根线
  • 显示:0.96 寸 OLED,SSD1306,地址 0x3C
  • 实测:三档 DRY / MOIST / WET,OLED 显示 ADC + MOIST% + STATE

Verified

实机验证结果

本项目实测 下面的结果全部来自我们自己的实机测试,更新于 2026-08-18。

3 档DRY / MOIST / WET
2 层8 次平均 + EMA 滤波
2 路AO 连续 + DO 阈值
200 msOLED 刷新周期

STM32F103C8T6 已经能读探头的 AO 模拟量并换算成 0~100% 湿度,OLED 显示 ADC 原始值 + MOIST 百分比 + STATE 三档状态,右上角一个小圆点实时显示 DO 电平。

土壤湿度传感器实拍接线:探头插土 + 面包板 + STM32 + OLED 显示 DRY 状态
实拍接线照片:探头插入干土,OLED 显示 MOIST 0% / STATE DRY

三档湿度实测

探头状态ADC(8次平均)STATEDO
干土≈4000DRYHIGH
正常湿润土≈3700MOISTHIGH
DO 翻转点(电位器阈值)≈3250—HIGH → LOW
很湿的土≈2600WETLOW

STATE 三档分界:ADC ≥ 3850 为 DRY,≥ 3150(且 < 3850)为 MOIST,否则 WET。DO 翻转点由电位器单独决定,与 STATE 三档分界是两套独立阈值。

土壤湿度传感器实拍结果:插入湿土后 OLED 显示 ADC 3011 / MOIST 70% / STATE WET
实拍结果照片:加水后 OLED 显示 ADC 3011 / MOIST 70% / STATE WET

串口输出(115200)

Soil moisture demo v1.1 starting...
OLED 0x3C: found
ADC=4008  DO=HIGH
ADC=3712  DO=HIGH
ADC=3260  DO=LOW
ADC=2610  DO=LOW

每 400ms 一行:ADC=<8次平均原始值> DO=<HIGH/LOW>。

实测环境

项目本项目实际使用
主控STM32F103C8T6(BluePill)
模块土壤湿度传感器(腐蚀式探头 + LM393 比较器小板,4 针 VCC / GND / DO / AO)
显示0.96 寸 OLED,SSD1306,128×64,地址 0x3C
状态灯BluePill 板载 LED,PC13(低电平点亮,DO 为 LOW 时亮)
开发工具VS Code + PlatformIO
框架Arduino / STM32duino
烧录ST-Link
串口115200
依赖库Adafruit SSD1306、Adafruit GFX Library
未测试的环境不要当成支持。本项目没有在 Arduino IDE、Keil、STM32CubeIDE 下编译或验证过,因此不提供这些环境的支持说明。

What it does

这个模块干什么

土壤湿度传感器就是给单片机装一个「土够不够湿」的检测器。两根金属叉探头插进土里,土壤干燥时叉齿间导电弱、等效电阻大;含水后水膜跨接叉齿,电阻下降。单片机拿到的就是一个「越湿越小」的电压,再换算成百分比。

土壤含水量改变探头两极间的电阻,探头把变化送进 LM393 控制板,控制板分成两路输出:AO 连续模拟量、DO 阈值开关量。STM32 读这两路,显示到 OLED 和串口。

AO 和 DO 到底有什么区别

AO(模拟量)DO(数字量)
输出连续电压,反映湿润程度开关量,只答「过没过阈值」
用途算百分比、判三档报警、触发、点灯
受电位器影响基本不受完全由电位器决定翻转点

AO/DO 的区别来自模块输出定义,由我们整理成表。资料参考

STATE 三档和 DO 翻转点是两回事。STATE 按 ADC 校准换算,DO 翻转点由模块电位器单独决定,不要把 DO 的 HIGH/LOW 当成干湿状态名来用。

Wiring

接线

本项目实测 按下面接法实机跑通。模块 4 根线 + OLED 4 根线。

土壤湿度模块(丝印 VCC / GND / DO / AO)

信号模块丝印STM32F103C8T6
电源VCC3.3V
地GNDGND
模拟输出AOPA0(ADC)
数字输出DOPA1(内部上拉)

OLED

信号OLEDSTM32F103C8T6
电源VCC / VDD3.3V
地GNDGND
时钟SCL / SCKPB8
数据SDAPB9

烧录与串口(不接也能跑,接上方便看数据)

用途连接说明
烧录ST-Link → SWD 四线(3.3V / GND / SWDIO=PA13 / SWCLK=PA14)烧录用
串口PA9(TX) / PA10(RX) → CP2102 等看串口数据(115200,可选)
土壤湿度传感器整体外观:探头 + LM393 控制板 + 连接线
模块整体外观:探头(YL-69)+ LM393 控制板 + 连接线
最容易接错的三处:
1. 供电用 3.3V,不要接 5V。AO 直接进 STM32 ADC,3.3V 供电能保证 AO 输出 0~3.3V 不超量程。
2. OLED 用重映射引脚 PB8/PB9,不是默认 PB7/PB6。代码里必须先 Wire.setSDA(PB9)、Wire.setSCL(PB8),再 Wire.begin(),顺序反了读不到屏幕。
3. 必须共地。模块、OLED、STM32 的 GND 接一起,否则 ADC 和 I2C 都会莫名其妙失败。

Principle

工作原理

资料参考 这一节来自厂家资料和常规工程实践,不是我们实测的结论,用来帮助理解参数。实测结果只在上面「实机验证结果」一节。

环节说明来源类型
探头电极两根金属叉齿插入土壤,干燥土壤颗粒间导电弱,等效电阻大,AO 电压高厂家/模块资料
水膜跨接土壤水分含离子,水膜跨接两根叉齿形成导电路径,含水量越高电阻越小,AO 电压越低(越湿 AO 越小)公开测试资料
LM393 阈值比较信号电压 vs 电位器设定的参考电压(门槛),越线瞬间 DO 翻转、板载 LED 点亮厂家/模块资料
电位器调门槛调的是 DO 的触发点;不影响 AO 连续读数厂家/模块资料
LM393 控制板特写
LM393 控制板特写
电位器特写
电位器特写
探头特写,丝印 YL-69 土壤湿度检测
探头特写(丝印 YL-69)

DO 极性:不同模块可能相反

LM393 是开漏输出,DO 的「触发电平」取决于板上接法,不同厂家批次可能相反。我们实测这块板子是「干 HIGH / 湿 LOW」。若你的模块实测相反,把判断逻辑改一处,或直接调模块电位器把翻转门槛调过来。

不要照抄别人的 DO 逻辑。必须先在自己板子上实测「干土 / 湿土」时 DO 到底是 HIGH 还是 LOW,再决定代码怎么写。

探头会腐蚀 · 最需要提前知道的一条

腐蚀式探头是裸露金属叉齿,长期通电 + 插土会发生电化学腐蚀,金属会一点点被吃掉,直到失效。这是这种电阻式雨滴/土壤传感器的通病。

短时间测试、拍视频没问题;长期使用要改「间歇供电」。每隔几秒给探头上电 → 等几十毫秒读数 → 再断电,能显著减慢腐蚀速度。当前代码是持续采样(20ms 一次),是为演示方便,不是为长期部署。金属叉形探头可以短时接触水做极限参考,但 LM393 小板本身不能浸水。

和电容式传感器比有什么区别

电阻式(本项目)电容式
原理水改变探头叉齿间电阻水改变电极间电容
腐蚀金属裸露,会电解腐蚀电极可覆膜,耐腐蚀好很多
成本很便宜贵一些
精度够判断干湿档位更适合长期、量化记录

对比来自电阻式与电容式传感器的常规工程实践,由我们整理。资料参考

Code

完整代码

本项目实测 下面是实际烧录进板子、拍摄结果照片时运行的那一份(v1.1),一字未改。环境:VS Code + PlatformIO + Arduino(STM32duino)。

关键处说明

1. I2C 指到 PB8/PB9。STM32duino 默认 I2C 引脚不是 PB8/PB9,必须先 Wire.setSDA()/Wire.setSCL() 再 Wire.begin(),顺序反了读不到 OLED。

2. 8 次平均 + EMA 平滑。AO 一次读 8 次取平均,再送指数滑动平均(EMA),抗探头波动,数值才稳。

3. 按实测方向的百分比映射。map(smoothAdc, SOIL_DRY_ADC, SOIL_WET_ADC, 0, 100),按本项目实测的方向(ADC 越小越湿)线性换算,夹到 0~100。

4. STATE 三档和 DO 翻转点互不干扰。三档分界(≥3850 DRY / 3150~3849 MOIST / <3150 WET)和 DO 的电位器翻转点(≈3250)是两套独立阈值。

5. 校准值单独定义。SOIL_DRY_ADC / SOIL_WET_ADC 是干燥/湿润两个 ADC 读数,实测后只改这两行。

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define FIRMWARE_VERSION "v1.1"

// =========================
// GPIO 配置
// =========================
// 土壤湿度模块(腐蚀探头 + LM393 比较器小板,4 针:VCC / GND / DO / AO)
//   AO -> PA0(模拟量,ADC1_IN0,反映探头间电阻,湿度越大读数方向「已实测:越湿越小」)
//   DO -> PA1(数字量,LM393 比较器输出,超过电位器阈值时翻转)
//   VCC -> 3.3V(3.3V 供电,AO 输出 0~3.3V,与 STM32 ADC 量程一致,不要接 5V)
//   GND -> GND
// 注意:腐蚀式探头持续通电会电腐蚀电极。短时测试/拍视频直接接 3.3V 没问题;
//       长期插土里使用时,改成 GPIO/三极管控制供电(隔几秒上电→等几十ms→采样→断电)。
// 板载 LED -> PC13(bluepill 板载 LED,低电平点亮;DO 低电平时点亮,作观察用)
constexpr uint8_t SOIL_AO_PIN = PA0;
constexpr uint8_t SOIL_DO_PIN = PA1;
constexpr uint8_t LED_PIN = PC13;

// OLED 使用 I2C1 重映射引脚:SCL=PB8 / SDA=PB9(与 01~08 系列公共标准一致)
constexpr uint8_t I2C_SDA_PIN = PB9;
constexpr uint8_t I2C_SCL_PIN = PB8;
constexpr uint8_t OLED_ADDRESS = 0x3C;
constexpr uint8_t SCREEN_WIDTH = 128;
constexpr uint8_t SCREEN_HEIGHT = 64;
constexpr int OLED_RESET = -1;

// =========================
// 校准参数(实测数据,2026-08-18 记录)
// =========================
// ADC: 4000   STATE: DRY        DO: HIGH
// ADC: 3700   STATE: MOIST      DO: HIGH
// ADC: 3250   STATE: THRESHOLD  DO: LOW   <- DO 电位器实测翻转点,只是 DO 的阈值,不是湿度档位中点
// ADC: 2600   STATE: WET        DO: LOW
// 方向:ADC 越高越干,越低越湿。MOIST% 按 DRY(0%) ~ WET(100%) 线性换算。
// STATE(土壤干湿档位)与 DO(比较器电平)是两个独立概念,分开显示,不用 HIGH/LOW 当干湿状态名。
// 注意:水中测试只作极限参考,不要把「水中 ADC」当成实际土壤的 100%;
//       且只能让金属叉形探头接触水,LM393 小板不能浸水。
constexpr uint16_t SOIL_DRY_ADC = 4000;  // 干土 ADC 读数(0%)
constexpr uint16_t SOIL_WET_ADC = 2600;  // 湿土 ADC 读数(100%)

// STATE 三档分界(DRY / MOIST / WET,与 DO 翻转点 3250 无关,避免把 DO 阈值当成湿度中点)
constexpr uint16_t SOIL_TH_DRY   = 3850; // >= 该值 -> DRY
constexpr uint16_t SOIL_TH_MOIST = 3150; // >= 该值(且 < DRY)-> MOIST,否则 WET

// =========================
// 采样 / 滤波 / 刷新周期
// =========================
constexpr uint32_t SAMPLE_INTERVAL_MS = 20;    // 采样周期
constexpr uint8_t  ADC_AVG_SAMPLES    = 8;     // 每次读 8 次 ADC 取平均
constexpr float    EMA_ALPHA          = 0.08f; // 轻度指数滑动平均(越小越平滑,OLED 显示用)
constexpr uint32_t OLED_REFRESH_MS    = 200;   // OLED 重绘周期(约 5fps)
constexpr uint32_t SERIAL_INTERVAL_MS = 400;   // 串口打印周期(200~500ms)

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// 状态
bool     g_hasOled = false;     // OLED 是否初始化成功
uint16_t g_rawAdc = 0;          // 最近一次 ADC 读数(8 次平均,串口/校准用)
float    g_smoothAdc = 0;       // 滑动平均后的 ADC(OLED 显示用,更稳)
bool     g_smoothInit = false;
bool     g_doLevel = false;     // DO 原始电平:true=HIGH / false=LOW

static uint32_t g_lastSampleMs = 0;
static uint32_t g_lastOledMs = 0;
static uint32_t g_lastSerialMs = 0;

// =========================
// 读取
// =========================
// DO:LM393 开漏输出,板上一般已有上拉,这里再开内部上拉更稳。
// 本阶段只如实上报电平 HIGH/LOW,不做 DRY/WET 判断(不同模块逻辑可能相反,待实测)。
// 连续读 5 次取多数(>=3 次 HIGH 才算 HIGH),避免电位器阈值附近抖动。
static bool readDoLevelStable() {
  uint8_t highCount = 0;
  for (uint8_t i = 0; i < 5; i++) {
    if (digitalRead(SOIL_DO_PIN) == HIGH) highCount++;
  }
  return highCount >= 3;
}

// =========================
// 采样(8 次平均) + 轻度滑动平均
// =========================
// 一次读 8 次 ADC 取平均,再送指数滑动平均(仅 OLED 显示用)。
// 串口保留「未经百分比转换」的 8 次平均原始 ADC 值,供校准记录。
static uint16_t readAdcAverage() {
  uint32_t sum = 0;
  for (uint8_t i = 0; i < ADC_AVG_SAMPLES; i++) {
    sum += analogRead(SOIL_AO_PIN);
  }
  return (uint16_t)(sum / ADC_AVG_SAMPLES);
}

static void updateSensor() {
  if (millis() - g_lastSampleMs < SAMPLE_INTERVAL_MS) return;
  g_lastSampleMs = millis();

  g_rawAdc = readAdcAverage();
  if (!g_smoothInit) {
    g_smoothAdc = (float)g_rawAdc;
    g_smoothInit = true;
  } else {
    g_smoothAdc += ((float)g_rawAdc - g_smoothAdc) * EMA_ALPHA;
  }

  g_doLevel = readDoLevelStable();
  digitalWrite(LED_PIN, g_doLevel ? HIGH : LOW);  // DO 低电平时点亮(bluepill LED 低电平点亮)
}

static const char* getDoText() {
  return g_doLevel ? "HIGH" : "LOW";
}

// ADC 越小越湿:DRY_ADC(0%) ~ WET_ADC(100%) 线性换算,并夹到 0~100
static uint8_t getMoisturePercent() {
  long pct = map((long)(g_smoothAdc + 0.5f), SOIL_DRY_ADC, SOIL_WET_ADC, 0, 100);
  if (pct < 0) pct = 0;
  if (pct > 100) pct = 100;
  return (uint8_t)pct;
}

static const char* getStateText() {
  uint16_t adc = (uint16_t)(g_smoothAdc + 0.5f);
  if (adc >= SOIL_TH_DRY)   return "DRY";
  if (adc >= SOIL_TH_MOIST) return "MOIST";
  return "WET";
}

// =========================
// OLED UI 绘制
// =========================
static void drawOledUi() {
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextWrap(false);

  // 顶部标题
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print(F("SOIL MOISTURE"));

  // 右上角 DO 指示点(实心 = LOW,空心 = HIGH)
  if (!g_doLevel) display.fillCircle(120, 4, 3, WHITE);
  else            display.drawCircle(120, 4, 3, WHITE);

  // 分隔线
  display.drawFastHLine(0, 9, 128, WHITE);

  // 右上角 DO 电平文字(与串口一致,如实上报,不做 DRY/WET 判断)
  display.setTextSize(1);
  display.setCursor(86, 16);
  display.print(F("DO:"));
  display.print(getDoText());

  // ADC 原始值(平滑后,现场观察更稳;串口才是 8 平均原始值)
  display.setCursor(0, 16);
  display.print(F("ADC:"));
  display.setTextSize(2);
  display.setCursor(0, 27);
  display.print((uint16_t)(g_smoothAdc + 0.5f));

  // MOIST(按实测 SOIL_DRY_ADC/SOIL_WET_ADC 换算)
  display.setTextSize(1);
  display.setCursor(0, 48);
  display.print(F("MOIST: "));
  display.print(getMoisturePercent());
  display.print(F(" %"));

  // 状态(按实测阈值分档:DRY/NORMAL/WET/VERY WET)
  display.setCursor(0, 57);
  display.print(F("STATE: "));
  display.print(getStateText());

  display.display();
}

// =========================
// 串口输出
// =========================
static void printSerialData() {
  Serial.printf("ADC=%u  DO=%s\n",
                (unsigned)g_rawAdc,
                getDoText());
}

void setup() {
  Serial.begin(115200);
  delay(100);
  Serial.printf("Soil moisture demo %s starting...\n", FIRMWARE_VERSION);

  pinMode(SOIL_DO_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
  analogReadResolution(12);  // 明确 12 位 ADC,读数 0~4095

  // I2C1 重映射:PB9=SDA / PB8=SCL,必须先 setSDA/setSCL 再 begin()
  Wire.setSDA(I2C_SDA_PIN);
  Wire.setSCL(I2C_SCL_PIN);
  Wire.begin();
  Wire.setClock(100000);  // I2C 100kHz,与 01~08 系列公共标准一致

  if (display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
    g_hasOled = true;
    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);
    display.display();
    Serial.println(F("OLED 0x3C: found"));
  } else {
    Serial.println(F("OLED 0x3C: NOT found (continue with serial only)"));
  }

  // 首帧采样
  g_rawAdc = readAdcAverage();
  g_smoothAdc = (float)g_rawAdc;
  g_smoothInit = true;
  g_doLevel = readDoLevelStable();
  digitalWrite(LED_PIN, g_doLevel ? HIGH : LOW);
  g_lastSampleMs = millis();

  // 串口校准提示
  Serial.println(F("=== 校准(先做这步,再定最终阈值)==="));
  Serial.println(F("1. 探头悬空(空气中)   -> 记录 ADC 值"));
  Serial.println(F("2. 探头插入干土       -> 记录 ADC 值 = 干燥值"));
  Serial.println(F("3. 探头插入湿土/很湿的土 -> 记录 ADC 值 = 湿润值"));
  Serial.println(F("4. 改 main.cpp 里 SOIL_DRY_ADC / SOIL_WET_ADC 后重烧(v2)"));
  Serial.println(F("   水中只作极限参考,别当土壤 100%;且只能探头叉入水,小板别浸水"));
  Serial.println(F("输出格式: ADC:<原始8平均>  DO:<HIGH/LOW>"));
  Serial.println(F("注意: ADC 方向(越湿越大/越小)待实测,OLED 上 MOIST 暂不换算百分比"));

  if (g_hasOled) drawOledUi();
}

void loop() {
  updateSensor();

  if (millis() - g_lastOledMs >= OLED_REFRESH_MS) {
    g_lastOledMs = millis();
    if (g_hasOled) drawOledUi();
  }

  if (millis() - g_lastSerialMs >= SERIAL_INTERVAL_MS) {
    g_lastSerialMs = millis();
    printSerialData();
  }
}

依赖库:Adafruit SSD1306 和 Adafruit GFX Library,用 PlatformIO 打开工程会按 platformio.ini 自动下载,不用手动安装。

Video

视频演示

本项目实测 208 秒完整演示动画(1920×1080 / 60fps / 带音效)。

网页版视频待压缩上传。完整动画在源工程 视频\土壤_动画_千问办公制作\成品\ 目录下,网页嵌入前需压成小体积版本再放到站点资源目录。当前先给出分段导航。

分段导航(14 段 · 共 208 秒)

时间段落内容
00:00成品效果盆栽+探针→加水→OLED 12%→85%,DRY→MOIST→WET
00:14模块认识比较小板/探针/电位器/AO·DO/VCC·GND
00:32接线AO→PA0/DO→PA1/VCC→3.3V/GND→GND/OLED PB8/PB9
00:52干土湿土干土导电弱 → 湿土水膜导电路径
01:08AO 模拟量电压随加水连续下降(实测干 3.2V→很湿 1.9~2.3V)
01:22ADC 流水线7 节点数据链:土壤→…→ADC→校准映射→湿度%
01:40湿度百分比OLED 大号 UI + 三档状态 + 滤波稳定
01:58电位器与 DO调 Threshold,干 HIGH/湿 LOW 两轮对比
02:18AO 与 DO 对比DO 只有 HIGH/LOW,AO 连续变化
02:30校准DRY ADC→0% / WET ADC→100% 三步映射
02:48实际使用盆栽浇水 35%→71%
03:02腐蚀提醒1/3/7 DAYS 探针腐蚀 + 间歇供电
03:18总结数据链重现 + 总结

Download

下载

PlatformIO 工程,解压后直接打开即可编译烧录,一般不需要改代码,也不需要改 platformio.ini。

PlatformIO 工程 ZIP 含 platformio.ini、src/main.cpp、README.md。已去掉本机构建路径,解压即可用。
下载 ZIP

第一次使用

  1. 按上面接线表接好:模块 AO→PA0、DO→PA1、VCC→3.3V、GND→GND;OLED SDA→PB9、SCL→PB8、VCC→3.3V、GND→GND
  2. 下载 ZIP,解压,用 VS Code + PlatformIO 打开这个文件夹(第一次打开会自动下载 Adafruit SSD1306 和 GFX 库)
  3. 点击 Build(底部状态栏 ✓ 图标),看到 SUCCESS 就说明代码没问题
  4. 接上 ST-Link,点击 Upload(底部状态栏 → 图标),看到 ** Verified OK ** 和 [SUCCESS] 表示烧录成功
  5. 看 OLED:先显示 SOIL MOISTURE 标题和 ADC 数值,把探头插进干土再插进湿土,MOIST 百分比和 STATE 会跟着变化

正常应该看到什么

动作OLED 上的表现
探头插干土ADC≈4000,MOIST≈0%,STATE: DRY
换插正常湿土ADC 下降,MOIST 上升,STATE: MOIST
插很湿的土ADC≈2600,MOIST≈100%,STATE: WET,DO 翻转、LED 亮
不要照抄 COM 号。查当前串口:pio device list,找 CP2102 / CP210x / CH340 对应的那个,然后在自己电脑的 platformio.ini 里加 monitor_port = COMx 和 monitor_speed = 115200。只有需要看串口时才需要加这两行,正常烧录用 ST-Link 不需要。
这块板子不能用网页烧录器。网页烧录(WebSerial)主要面向 ESP32 / ESP8266 这类自带 USB 串口下载的芯片。STM32F103C8T6 本项目用的是 ST-Link + SWD,浏览器接触不到 SWD 接口。

Troubleshooting

常见问题排查

下面每一条都是这个模块真实会遇到的。标注「实测」的是我们自己踩过的。

供电接错、共地没接,读数乱跳

按这个顺序查:

  1. 供电是否 3.3V。AO 直接进 STM32 ADC,接 5V 会让 AO 超出 3.3V 量程。
  2. 是否共地。模块、OLED、STM32 的 GND 要接在一起,否则 ADC 和 I2C 都会莫名其妙失败。
  3. PA13 / PA14 别占用。这两个脚留给 ST-Link 调试,占用后可能烧不进去。
百分比方向反了:土越湿,百分比越低

校准值 SOIL_DRY_ADC / SOIL_WET_ADC 填反了。按自己模块实测重填这两行即可:map(smoothAdc, SOIL_DRY_ADC, SOIL_WET_ADC, 0, 100),本项目实测方向是「ADC 越小越湿」。

DO 极性反了:干土时 DO 是 LOW

不同厂家批次的比较器输出极性可能不同。程序默认按「干 HIGH / 湿 LOW」写。若实测相反,调模块电位器,或改 readDoLevelStable() 的判断逻辑。不要照抄别人的 DO 逻辑,先在自己板子上实测。

STATE 和 DO 对不上

这是正常现象,两者是独立的两套阈值。STATE 按 ADC 校准换算(≥3850 DRY / 3150~3849 MOIST / <3150 WET),DO 翻转点由电位器单独决定(本项目实测≈3250),不要把 DO 的 HIGH/LOW 当成干湿状态名来用。

OLED 不亮 / 串口打印 OLED 0x3C: NOT found

十有八九是 I2C 引脚或地址。按这个顺序查:

  1. 确认 SDA 接 PB9、SCL 接 PB8,不是默认的 PB7/PB6。
  2. 确认代码里 setSDA/setSCL 在 Wire.begin() 之前,顺序反了配置不生效。
  3. 确认 OLED 地址。常见 0.96 寸 SSD1306 大多是 0x3C,少数是 0x3D。
  4. 确认共地。OLED 的 GND 必须和 STM32 接在一起。
串口看不到数据

先查当前串口:

pio device list

找到 CP2102 / CP210x / CH340 对应的那个 COM,然后在自己电脑的 platformio.ini 里加:

monitor_port = COMx
monitor_speed = 115200

COMx 要换成你自己电脑实际识别到的端口号,不要直接抄。只有需要看串口时才加这两行,正常用 ST-Link 烧录不需要。

长期用探头腐蚀、失效

这是金属探头 + 通电 + 插土产生的电化学腐蚀,是这类腐蚀式探头的固有特性,不是坏了。短时间演示、拍视频完全没问题。长期部署建议改成「上电 → 稳定 → 采样 → 断电」的间歇供电,或换电容式传感器(覆膜电极,耐腐蚀好很多)。

References

资料来源

本页信息来源声明

内容来源
接线、代码、OLED UI、三档阈值、滤波参数、串口输出格式本项目实测 2026-08-18 实机测试
探头电极、水膜导电、LM393 比较器、电位器门槛资料参考 厂家/模块资料与公开测试资料
AO / DO 两类输出的对比、电阻式与电容式传感器对比资料参考 常规工程实践,由我们整理成表
DO 触发电平极性(干 HIGH / 湿 LOW)本项目实测 本块板子实测结果,不同批次可能相反
探头具体型号(YL-38/YL-69)待确认 图上丝印常见于该系列,淘宝批次型号可能不同

本页所有「资料参考」内容均由我们重新整理表述,未直接复制原文。参数以你手上模块的实际批次和卖家说明为准;本节任何一条都不代表我们实测验证过,实测结果只在「实机验证结果」一节。