简介
该传感器适用于检测土壤肥沃情况,5-30V宽电压供电,RS485输出,可检测氮、磷、钾,响应快,输出稳定,可搭配Arduino UNO R3与TTL转485扩展板,快速搭建测试。
土壤传感器防护等级为IP68,采用黑色阻燃环氧树脂真空灌装密封,探针材质选用316型不锈钢,具有防锈防水防腐蚀、耐盐碱腐蚀、耐长期电解,受土壤含盐量影响较小,可长期埋入土壤中,适用各种土质。
土壤传感器广泛适用于稻田、大棚种植、水稻、蔬菜种植、果园苗圃、花卉以及土壤研究等。
注:氮磷钾传感器数据只能作为参考,无法像专业仪器精准。
特点
- 5-30V宽电压供电
- RS485输出,可搭配Arduino使用
- 不锈钢探针,可长期埋入土里或水里
- 树脂真空灌装密封,IP68防护等级
技术规格
- 供电电压:DC5-30V
- 功耗:0.15W@12V
- 输出方式:RS485
- 检测参数:氮、磷、钾
- 氮磷钾量程:0-2999mg/kg(mg/L)
- 分辨率:1mg/kg(mg/L)
- 精度:≤5%(以实际测定仪器为准)
- 防护等级:IP68
- 探针材料:防腐特制电极
- 密封材料:黑色阻燃环氧树脂
- 工作温度:0°C~+55°C
- 尺寸:123x45x15mm
- 线长:2m
引脚示意图
| 标号 | 名称 | 功能描述 | 
|---|---|---|
| 棕线 | VCC | 电源输入正极,DC5-30V供电 | 
| 黑线 | GND | 电源接地线 | 
| 黄线 | 485-A | RS485数据A线 | 
| 蓝线 | 485-B | RS485数据B线 | 
尺寸图
 
通信协议
1、通讯基本参数
| 接口 | 编码 | 数据位 | 奇偶校验位 | 停止位 | 错误校验 | 波特率 | 
|---|---|---|---|---|---|---|
| RS485 | 8位二进制 | 8 | 无 | 1 | CRC | 2400bit/s、4800bit/s、9600 bit/s 可设,默认9600bit/s | 
2、数据帧格式定义
采用 ModBus-RTU 通讯规约,格式如下:
- 初始结构 ≥4 字节的时间
- 地址码 = 1 字节
- 功能码 = 1 字节
- 数据区 = N 字节
- 错误校验 = 16 位 CRC 码
- 结束结构 ≥4 字节的时间
- 地址码:为传感器的地址,在通讯网络中是唯一的(出厂默认 0x01)。
- 功能码:主机所发指令功能指示,本传感器只用到功能码 0x03(读取寄存器数据)。0x06 0x10
- 数据区:数据区是具体通讯数据,注意 16bits 数据高字节在前!
- CRC 码:二字节的校验码。
主机问询帧结构:
| 地址码 | 功能码 | 寄存器起始地址 | 寄存器长度 | 校验码低位 | 校验码高位 | 
|---|---|---|---|---|---|
| 1byte | 1byte | 2byte | 2byte | 1byte | 1byte | 
从机应答帧结构:
| 地址码 | 功能码 | 有效字节数 | 数据一区 | 第二数据区 | 第N数据区 | 校验码 | 
|---|---|---|---|---|---|---|
| 1byte | 1byte | 1byte | 2byte | 2byte | 2byte | 2byte | 
3、通讯协议示例及解释
3.1、举例:读取设备地址 0x01 的氮含量暂存值
问询帧(16 进制):
| 地址码 | 功能码 | 寄存器起始地址 | 寄存器长度 | 校验码低位 | 校验码高位 | 
|---|---|---|---|---|---|
| 0x01 | 0x03 | 0x00 0x1E | 0x00 0x01 | 0xE4 | 0x0C | 
应答帧(16 进制):
| 地址码 | 功能码 | 返回有效字节数 | 氮暂存值 | 校验码低位 | 校验码高位 | 
|---|---|---|---|---|---|
| 0x01 | 0x03 | 0x02 | 0x00 0x20 | 0xB9 | 0x9C | 
氮含量暂存值计算:
- 氮含量暂存值:0020 H(16 进制)= 32 =>氮= 32mg/kg
3.2、举例:读取设备地址 0x01 的磷含量暂存值
问询帧(16 进制):
| 地址码 | 功能码 | 寄存器起始地址 | 寄存器长度 | 校验码低位 | 校验码高位 | 
|---|---|---|---|---|---|
| 0x01 | 0x03 | 0x00 0x1F | 0x00 0x01 | 0xB5 | 0xCC | 
应答帧(16 进制):
| 地址码 | 功能码 | 返回有效字节数 | 磷暂存值 | 校验码低位 | 校验码高位 | 
|---|---|---|---|---|---|
| 0x01 | 0x03 | 0x02 | 0x00 0x25 | 0x79 | 0x9F | 
磷含量暂存值计算:
- 磷含量暂存值:0025 H(16 进制)= 37 =>磷=37mg/kg
3.3、举例:读取设备地址 0x01 的钾含量暂存值
问询帧(16 进制):
| 地址码 | 功能码 | 寄存器起始地址 | 寄存器长度 | 校验码低位 | 校验码高位 | 
|---|---|---|---|---|---|
| 0x01 | 0x03 | 0x00 0x20 | 0x00 0x01 | 0x85 | 0xC0 | 
应答帧(16 进制):
| 地址码 | 功能码 | 返回有效字节数 | 钾暂存值 | 校验码低位 | 校验码高位 | 
|---|---|---|---|---|---|
| 0x01 | 0x03 | 0x02 | 0x00 0x30 | 0xB8 | 0x50 | 
钾含量暂存值计算:
- 钾含量暂存值:0030 H(16 进制)= 48 =>钾=48mg/kg
4、寄存器地址
| 寄存器地址 | PLC或组态地址 | 内容 | 操作 | 定义说明 | 
|---|---|---|---|---|
| 001EH | 40031(十进制) | 氮含量暂存值 | 读写 | 被写入的氮含量值或测试值 | 
| 001FH | 40032(十进制) | 磷含量暂存值 | 读写 | 被写入的磷含量值或测试值 | 
| 0020H | 40033(十进制) | 钾含量暂存值 | 读写 | 被写入的钾含量值或测试值 | 
| 03E8H | 41001(十进制) | 氮含量暂存值 系数高十六位 | 读写 | 浮点数 | 
| 03E9H | 41002(十进制) | 氮含量暂存值系数低十六位 | 读写 | 浮点数 | 
| 03EAH | 41003(十进制) | 氮含量暂存值的偏差值 | 读写 | 整数 | 
| 03F2H | 41011(十进制) | 磷含量暂存值 系数高十六位 | 读写 | 浮点数 | 
| 03F3H | 41012(十进制) | 磷含量暂存值系数低十六位 | 读写 | 浮点数 | 
| 03F4H | 41013(十进制) | 磷含量暂存值的偏差值 | 读写 | 整数 | 
| 03FCH | 41021(十进制) | 钾含量暂存值 系数高十六位 | 读写 | 浮点数 | 
| 03FDH | 41022(十进制) | 钾含量暂存值系数低十六位 | 读写 | 浮点数 | 
| 03FEH | 41023(十进制) | 钾含量暂存值的偏差值 | 读写 | 整数 | 
| 07D0H | 42001(十进制) | 设备地址 | 读写 | 1-254(出厂默认1) | 
| 07D1H | 42002(十进制) | 设备波特率 | 读写 | 0代表2400 1代表4800 2代表9600 | 
使用教程
准备
- 硬件
Arduino UNO x1
RS485土壤传感器(氮&磷&钾) x1
- 软件
Arduino IDE,点击下载 Arduino IDE
接线图
烧录代码前,请将扩展板的收发模式开关切换到AUTO,运行/编译开关切换到OFF,烧录代码后,运行/编译开关切换到ON,串口波特率选择9600
 
示例代码
uint8_t Com[8] = { 0x01, 0x03, 0x00, 0x1E, 0x00, 0x01, 0xE4, 0x0C };   //氮
uint8_t Com1[8] = { 0x01, 0x03, 0x00, 0x1F, 0x00, 0x01, 0xB5, 0xCC };  //磷
uint8_t Com2[8] = { 0x01, 0x03, 0x00, 0x20, 0x00, 0x01, 0x85, 0xC0 };  //钾
int N, P, K;
void setup() {
  Serial.begin(9600);  //初始化串口
}
void loop() {
  int N = readN();
  Serial.print("N = ");
  Serial.print(N);
  Serial.print(" mg/kg  ");
  int P = readP();
  Serial.print("P = ");
  Serial.print(P);
  Serial.print(" mg/kg  ");
  int k = readK();
  Serial.print("K = ");
  Serial.print(K);
  Serial.println(" mg/kg  ");
  delay(1000);
}
int readN(void) {
  uint8_t Data[10] = { 0 };
  uint8_t ch = 0;
  bool flag = 1;
  while (flag) {
    delay(100);
    Serial.write(Com, 8);
    delay(10);
    if (readN(&ch, 1) == 1) {
      if (ch == 0x01) {
        Data[0] = ch;
        if (readN(&ch, 1) == 1) {
          if (ch == 0x03) {
            Data[1] = ch;
            if (readN(&ch, 1) == 1) {
              if (ch == 0x02) {
                Data[2] = ch;
                if (readN(&Data[3], 4) == 4) {
                  if (CRC16_2(Data, 5) == (Data[5] * 256 + Data[6])) {
                    N = Data[3] * 256 + Data[4];
                    flag = 0;
                  }
                }
              }
            }
          }
        }
      }
    }
    Serial.flush();
  }
  return N;
}
int readP(void) {
  uint8_t Data1[10] = { 0 };
  uint8_t ch1 = 0;
  bool flag1 = 1;
  while (flag1) {
    delay(100);
    Serial.write(Com1, 8);
    delay(10);
    if (readN(&ch1, 1) == 1) {
      if (ch1 == 0x01) {
        Data1[0] = ch1;
        if (readN(&ch1, 1) == 1) {
          if (ch1 == 0x03) {
            Data1[1] = ch1;
            if (readN(&ch1, 1) == 1) {
              if (ch1 == 0x02) {
                Data1[2] = ch1;
                if (readN(&Data1[3], 4) == 4) {
                  if (CRC16_2(Data1, 5) == (Data1[5] * 256 + Data1[6])) {
                    P = Data1[3] * 256 + Data1[4];
                    flag1 = 0;
                  }
                }
              }
            }
          }
        }
      }
    }
    Serial.flush();
  }
  return P;
}
int readK(void) {
  uint8_t Data2[10] = { 0 };
  uint8_t ch2 = 0;
  bool flag2 = 1;
  while (flag2) {
    delay(100);
    Serial.write(Com2, 8);
    delay(10);
    if (readN(&ch2, 1) == 1) {
      if (ch2 == 0x01) {
        Data2[0] = ch2;
        if (readN(&ch2, 1) == 1) {
          if (ch2 == 0x03) {
            Data2[1] = ch2;
            if (readN(&ch2, 1) == 1) {
              if (ch2 == 0x02) {
                Data2[2] = ch2;
                if (readN(&Data2[3], 4) == 4) {
                  if (CRC16_2(Data2, 5) == (Data2[5] * 256 + Data2[6])) {
                    K = Data2[3] * 256 + Data2[4];
                    flag2 = 0;
                  }
                }
              }
            }
          }
        }
      }
    }
    Serial.flush();
  }
  return K;
}
uint8_t readN(uint8_t *buf, size_t len) {
  size_t offset = 0, left = len;
  int16_t Tineout = 500;
  uint8_t *buffer = buf;
  long curr = millis();
  while (left) {
    if (Serial.available()) {
      buffer[offset] = Serial.read();
      offset++;
      left--;
    }
    if (millis() - curr > Tineout) {
      break;
    }
  }
  return offset;
}
unsigned int CRC16_2(unsigned char *buf, int len) {
  unsigned int crc = 0xFFFF;
  for (int pos = 0; pos < len; pos++) {
    crc ^= (unsigned int)buf[pos];
    for (int i = 8; i != 0; i--) {
      if ((crc & 0x0001) != 0) {
        crc >>= 1;
        crc ^= 0xA001;
      } else {
        crc >>= 1;
      }
    }
  }
  crc = ((crc & 0x00ff) << 8) | ((crc & 0xff00) >> 8);
  return crc;
}
结果:将土壤传感器插入土里,则串口打印出传感器检测到的氮、磷、钾值。
注:氮磷钾传感器数据只能作为参考,无法像专业仪器精准。
 
安装使用方法
1、速测法
选定合适的测量地点,避开石块,确保钢针不会碰到坚硬的物体,按照所需测量深度抛开表层土,保持下面土壤原有的松紧程度,紧握传感器垂直插入土壤,插入时不可左右晃动,一个测点的小范围内建议多次测量求平均值。
 
2、埋地测量法
垂直挖直径>20cm 的坑,在既定的深度将传感器钢针水平插入坑壁,将坑填埋严实,稳定一段时间后,即可进行连续数天,数月乃至更长时间的测量和记录。

3、注意事项
- 测量时钢针必须全部插入土壤里。
- 避免强烈阳光直接照射到传感器上而导致温度过高,野外使用注意防雷击。
- 勿暴力折弯钢针,勿用力拉拽传感器引出线,勿摔打或猛烈撞击传感器。
- 传感器防护等级 IP68,可以将传感器整个泡在水中。
- 由于在空气中存在射频电磁辐射,不宜长时间在空气中处于通电状态。
常见问题
无输出或输出错误存在可能的原因:
- 电脑有 COM 口,选择的口不正确。
- TTL转485模块运行/编程拨动开关选择不正确。
- 波特率错误。
- 485 总线有断开,或者 A、B 线接反。
- 设备数量过多或布线太长,应就近供电。
- 设备损坏。
还有客户对此产品有任何问题,欢迎通过 qq 或者论坛联系我们!
更多问题及有趣的应用,可以访问论坛进行查阅或发帖