RS485土壤传感器(氮&磷&钾)

简介

该传感器适用于检测土壤肥沃情况,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

TTL转RS485扩展板(DFR0259) x1

RS485土壤传感器(氮&磷&钾) x1

  • 软件

Arduino IDE,点击下载 Arduino IDE

接线图

烧录代码前,请将扩展板的收发模式开关切换到AUTO,运行/编译开关切换到OFF,烧录代码后,运行/编译开关切换到ON,串口波特率选择9600

Arduino连接图

示例代码

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;
}

结果:将土壤传感器插入土里,则串口打印出传感器检测到的氮、磷、钾值。

注:氮磷钾传感器数据只能作为参考,无法像专业仪器精准。

Arduino串口打印数据图

安装使用方法

1、速测法

选定合适的测量地点,避开石块,确保钢针不会碰到坚硬的物体,按照所需测量深度抛开表层土,保持下面土壤原有的松紧程度,紧握传感器垂直插入土壤,插入时不可左右晃动,一个测点的小范围内建议多次测量求平均值。

速测法

2、埋地测量法

垂直挖直径>20cm 的坑,在既定的深度将传感器钢针水平插入坑壁,将坑填埋严实,稳定一段时间后,即可进行连续数天,数月乃至更长时间的测量和记录。
速测法

3、注意事项

  • 测量时钢针必须全部插入土壤里。
  • 避免强烈阳光直接照射到传感器上而导致温度过高,野外使用注意防雷击。
  • 勿暴力折弯钢针,勿用力拉拽传感器引出线,勿摔打或猛烈撞击传感器。
  • 传感器防护等级 IP68,可以将传感器整个泡在水中。
  • 由于在空气中存在射频电磁辐射,不宜长时间在空气中处于通电状态。

常见问题

无输出或输出错误存在可能的原因:

  • 电脑有 COM 口,选择的口不正确。
  • TTL转485模块运行/编程拨动开关选择不正确。
  • 波特率错误。
  • 485 总线有断开,或者 A、B 线接反。
  • 设备数量过多或布线太长,应就近供电。
  • 设备损坏。

还有客户对此产品有任何问题,欢迎通过 qq 或者论坛联系我们!
更多问题及有趣的应用,可以访问论坛进行查阅或发帖

更多

DFRobot 商城购买链接