(SKU:SEN0237)溶解氧传感器

来自DFRobot Product Wiki
跳转至: 导航搜索
Gravity:溶解氧传感器(SEN0237)

目录

简介

这是一款arduino兼容的溶解氧传感器,用于测量水中的溶解氧含量,反应水质状况。可应用于水产养殖、环境检测、自然科学等领域的水质检测。
俗话说,“养鱼先养水,好水养好鱼。”良好的水质,对水生物非常重要。溶解氧 (Dissolved Oxygen) 就是反应水质好坏的重要参数之一。水中的溶解氧值一旦过低,会造成水生物呼吸困难,对其生存造成威胁。
我们推出了一款arduino兼容的溶解氧传感器,用于测量水中的溶解氧含量,反应水质状况,可应用于水产养殖、环境检测、自然科学等领域的水质检测。本产品简单易用,因此DIY一套溶解氧检测仪不再是有难度的门槛。
本产品的溶解氧电极为原电池型电极,无需极化时间,随用随测。填充液与感应膜可更换,维护成本低;信号转接板具有工作电压宽、连线方便、即插即用等特点,可方便的接入到现成的控制系统中使用。
该产品简单易用、兼容性强、代码开源、配套资料详细,可快速应用到所需场景中,为你的水生物守住“生命之源”!

技术规格

  • 溶解氧电极
    • 电极类型:原电池
    • 检测范围:0~20mg/L
    • 温度范围:0~40℃
    • 响应时间:90秒达到98%全响应(25℃)
    • 压力范围:0~50PSI
    • 流动条件:0.3 mL/s
    • 电极芯寿命:1年(正常使用情况下)
    • 维护时间:视水质状况,对于浑水,通常1~2个月需更换膜帽;对于清水,通常4~5个月需更换膜帽。电极填充液建议每个月更换一次。
    • 线缆长度:2米
    • 连线接口:BNC
  • 信号转接板
    • 供电电压:3.3~5.5V
    • 输出电压:0~3.0V
    • 电极接口:BNC
    • 信号接口:Gravity接口 (PH2.0-3P)
    • 板子尺寸:42mm * 32mm


引脚说明

溶解氧传感器
溶解氧传感器信号转接板管脚定义
标号 名称 功能描述
1 A 模拟信号输出端(0~3.0V)
2 + 电源输入正极(3.3~5.5V)
3 - 电源输入负极
4 BNC 溶解氧电极连线接口


使用教程

本教程将演示如何使用这款溶解氧传感器。配套的电极处于精密电化学传感器,请仔细阅读本教程,注意步骤与细节。

Warning yellow.png
  • 使用新的溶解氧电极前,位于电极头部的膜帽需要加入0.5mol/L氢氧化钠溶液作为电极的填充液。该溶液碱性较强,请带好防水手套后操作。如不慎滴到皮肤上,请用大量清水冲洗。
  • 膜帽上透明的氧渗透膜为敏感元件,请注意保护,不能用指甲或者其他尖锐物体碰触。
  • 溶解氧电极在测量过程中需要耗氧,故测量的时候,需要轻轻搅拌液体,使得溶解氧均匀分布在液体中。但不能剧烈搅拌,以防空气中的氧气迅速溶入液体中!


准备材料

  • 硬件
    • 溶解氧电极(含膜帽) x1
    • 0.5mol/L氢氧化钠溶液 x1
    • Arduino UNO控制板 x1
    • 溶解氧传感器信号转接板 x1
    • Gravity 3P模拟连接线 x1
  • 软件


准备电极


使用全新的溶解氧电极之前,需要在膜帽(位于电极头部)中加入0.5mol/L氢氧化钠溶液,作为电极的填充液。如果电极使用有一段时间后,误差明显增大时,需要更换填充液。
下面将详细说明如何给溶解氧电极添加填充液。


拧开电极的膜帽盖,倒入0.5mol/L氢氧化钠溶液到膜帽盖中,倒入量大概为膜帽内部容积的三分之二,然后使电极方向与水平面方向垂直,把膜帽套到电极上再拧紧,以溢出一点氢氧化钠溶液为佳。

  • 在套上加有填充液的膜帽并拧紧时,电极尽量与水平面垂直,否则会出现不均匀,导致气泡产生。
  • 加入填充液时,不能把膜帽加满,否则套到电极上时,会溢出很多填充液;也不可太少,没加够填充液会导致膜帽内部产生气泡。故倒入三分之二左右,膜帽拧紧后,以溢出一点氢氧化钠溶液为佳。
  • 膜帽上溢出的氢氧化钠溶液,请用吸水纸吸干。
  • 开封过的氢氧化钠溶液瓶,需尽快密封好,防止空气中的二氧化碳对其产生影响。
AddSolution2.png


接线图


电极的填充液加入完毕后,需要进行一次校准。在校准前,请按照下图所示的接线图连接:电极接到信号转接板的BNC接口上,信号转接板接到arduino主控板的模拟口上。

DOapplication.png



校准电极

为保证精度,初次使用的电极,或者使用了一段时间的电极,需要进行校准。
常用的校准有:单点校准和两点校准。单点校准只进行饱和氧校准,而两点校准则要进行零氧校准和饱和氧校准。
大多数情况下采用单点校准即可,简单方便。
下面将详细说明如何进行单点校准。
1. 先上传样例代码至arduino主控板中,上传完毕后,打开串口监视器,即可看到溶解氧浓度值。
2. 将溶解氧电极插入到饱和氧水中,轻轻搅拌。查看串口监视器打印的溶解氧浓度值,等待浓度值慢慢稳定。

  • 溶解氧电极的反应需要一定时间,故通常需要一分钟以上,才能得到比较稳定的数值。
  • 如没有饱和氧水,用空气也可替代。方法是:将溶解氧电极放入水溶液中搅拌几下,即可湿润膜帽上的渗透膜,然后把电极静置在空气中即可,通常一分钟以上。

3. 待溶解氧浓度值几乎稳定后,即可进行饱和氧校准。具体步骤如下:

  • 1.在串口监视器中输入Calibration指令,进入校准模式.
EnterCalibration.png
  • 2.输入SATCAL指令进行饱和氧校准,校准成功与否会有相应的提示。
SATCAL.png
  • 3.校准完毕后,输入EXIT指令退出校准模式。
ExitCMD.png



4. 经过上述步骤,饱和氧校准就完成了,之后就可用于实际的测量。


样例代码

/***************************************************
 DFRobot Gravity: Analog Dissolved Oxygen Sensor / Meter Kit for Arduino
 <https://www.dfrobot.com/wiki/index.php/Gravity:_Analog_Dissolved_Oxygen_Sensor_SKU:SEN0237>
 
 ***************************************************
 This example reads the concentration of dissolved oxygen in water.
 The saturated oxygen calibration is available by UART commends with NL & CR:
 calibration  ----  enter the calibration mode
 satcal       ----  calibrate the parameters with saturated oxygen value
 exit         ----  exit the calibration mode
 
 Created 2017-5-22
 By Jason <jason.ling@dfrobot.com@dfrobot.com>
 
 GNU Lesser General Public License.
 See <http://www.gnu.org/licenses/> for details.
 All above must be included in any redistribution
 ****************************************************/
 
 /***********Notice and Trouble shooting***************
 1. This code is tested on Arduino Uno and Leonardo with Arduino IDE 1.0.5 r2 and 1.8.2.
 2. More details, please click this link: <https://www.dfrobot.com/wiki/index.php/Gravity:_Analog_Dissolved_Oxygen_Sensor_SKU:SEN0237>
 ****************************************************/

#include <avr/pgmspace.h>
#include <EEPROM.h>

#define DoSensorPin  A1    //dissolved oxygen sensor analog output pin to arduino mainboard
#define VREF 5000    //for arduino uno, the ADC reference is the AVCC, that is 5000mV(TYP)
float doValue;      //current dissolved oxygen value, unit; mg/L
float temperature = 25;    //default temperature is 25^C, you can use a temperature sensor to read it

#define EEPROM_write(address, p) {int i = 0; byte *pp = (byte*)&(p);for(; i < sizeof(p); i++) EEPROM.write(address+i, pp[i]);}
#define EEPROM_read(address, p)  {int i = 0; byte *pp = (byte*)&(p);for(; i < sizeof(p); i++) pp[i]=EEPROM.read(address+i);}

#define ReceivedBufferLength 20
char receivedBuffer[ReceivedBufferLength+1];    // store the serial command
byte receivedBufferIndex = 0;

#define SCOUNT  30           // sum of sample point
int analogBuffer[SCOUNT];    //store the analog value in the array, readed from ADC
int analogBufferTemp[SCOUNT];
int analogBufferIndex = 0,copyIndex = 0;

#define SaturationDoVoltageAddress 12          //the address of the Saturation Oxygen voltage stored in the EEPROM
#define SaturationDoTemperatureAddress 16      //the address of the Saturation Oxygen temperature stored in the EEPROM
float SaturationDoVoltage,SaturationDoTemperature;
float averageVoltage;

const float SaturationValueTab[41] PROGMEM = {      //saturation dissolved oxygen concentrations at various temperatures
14.46, 14.22, 13.82, 13.44, 13.09,
12.74, 12.42, 12.11, 11.81, 11.53,
11.26, 11.01, 10.77, 10.53, 10.30,
10.08, 9.86,  9.66,  9.46,  9.27,
9.08,  8.90,  8.73,  8.57,  8.41,
8.25,  8.11,  7.96,  7.82,  7.69,
7.56,  7.43,  7.30,  7.18,  7.07,
6.95,  6.84,  6.73,  6.63,  6.53,
6.41,
};

void setup()
{
    Serial.begin(115200);
    pinMode(DoSensorPin,INPUT);
    readDoCharacteristicValues();      //read Characteristic Values calibrated from the EEPROM
}

void loop()
{
   static unsigned long analogSampleTimepoint = millis();
   if(millis()-analogSampleTimepoint > 30U)     //every 30 milliseconds,read the analog value from the ADC
   {
     analogSampleTimepoint = millis();
     analogBuffer[analogBufferIndex] = analogRead(DoSensorPin);    //read the analog value and store into the buffer
     analogBufferIndex++;
     if(analogBufferIndex == SCOUNT) 
         analogBufferIndex = 0;
   }
   
   static unsigned long tempSampleTimepoint = millis();
   if(millis()-tempSampleTimepoint > 500U)  // every 500 milliseconds, read the temperature
   {
      tempSampleTimepoint = millis();
      //temperature = readTemperature();  // add your temperature codes here to read the temperature, unit:^C
   }
   
   static unsigned long printTimepoint = millis();
   if(millis()-printTimepoint > 1000U)
   {
      printTimepoint = millis();
      for(copyIndex=0;copyIndex<SCOUNT;copyIndex++)
      {
        analogBufferTemp[copyIndex]= analogBuffer[copyIndex];
      }
      averageVoltage = getMedianNum(analogBufferTemp,SCOUNT) * (float)VREF / 1024.0; // read the value more stable by the median filtering algorithm
      Serial.print(F("Temperature:"));
      Serial.print(temperature,1);
      Serial.print(F("^C"));
      doValue = pgm_read_float_near( &SaturationValueTab[0] + (int)(SaturationDoTemperature+0.5) ) * averageVoltage / SaturationDoVoltage;  //calculate the do value, doValue = Voltage / SaturationDoVoltage * SaturationDoValue(with temperature compensation)
      Serial.print(F(",  DO Value:"));
      Serial.print(doValue,2);
      Serial.println(F("mg/L"));
   }
   
   if(serialDataAvailable() > 0)
   {
      byte modeIndex = uartParse();  //parse the uart command received
      doCalibration(modeIndex);    // If the correct calibration command is received, the calibration function should be called.
   }
   
}

boolean serialDataAvailable(void)
{
  char receivedChar;
  static unsigned long receivedTimeOut = millis();
  while ( Serial.available() > 0 ) 
  {   
    if (millis() - receivedTimeOut > 500U) 
    {
      receivedBufferIndex = 0;
      memset(receivedBuffer,0,(ReceivedBufferLength+1));
    }
    receivedTimeOut = millis();
    receivedChar = Serial.read();
    if (receivedChar == '\n' || receivedBufferIndex == ReceivedBufferLength)
    {
	receivedBufferIndex = 0;
	strupr(receivedBuffer);
	return true;
    }else{
        receivedBuffer[receivedBufferIndex] = receivedChar;
        receivedBufferIndex++;
    }
  }
  return false;
}

byte uartParse()
{
    byte modeIndex = 0;
    if(strstr(receivedBuffer, "CALIBRATION") != NULL) 
        modeIndex = 1;
    else if(strstr(receivedBuffer, "EXIT") != NULL) 
        modeIndex = 3;
    else if(strstr(receivedBuffer, "SATCAL") != NULL)   
        modeIndex = 2;
    return modeIndex;
}

void doCalibration(byte mode)
{
    char *receivedBufferPtr;
    static boolean doCalibrationFinishFlag = 0,enterCalibrationFlag = 0;
    float voltageValueStore;
    switch(mode)
    {
      case 0:
      if(enterCalibrationFlag)
         Serial.println(F("Command Error"));
      break;
      
      case 1:
      enterCalibrationFlag = 1;
      doCalibrationFinishFlag = 0;
      Serial.println();
      Serial.println(F(">>>Enter Calibration Mode<<<"));
      Serial.println(F(">>>Please put the probe into the saturation oxygen water! <<<"));
      Serial.println();
      break;
     
     case 2:
      if(enterCalibrationFlag)
      {
         Serial.println();
         Serial.println(F(">>>Saturation Calibration Finish!<<<"));
         Serial.println();
         EEPROM_write(SaturationDoVoltageAddress, averageVoltage);
         EEPROM_write(SaturationDoTemperatureAddress, temperature);
         SaturationDoVoltage = averageVoltage;
         SaturationDoTemperature = temperature;
         doCalibrationFinishFlag = 1;
      }
      break;

        case 3:
        if(enterCalibrationFlag)
        {
            Serial.println();
            if(doCalibrationFinishFlag)      
               Serial.print(F(">>>Calibration Successful"));
            else 
              Serial.print(F(">>>Calibration Failed"));       
            Serial.println(F(",Exit Calibration Mode<<<"));
            Serial.println();
            doCalibrationFinishFlag = 0;
            enterCalibrationFlag = 0;
        }
        break;
    }
}

int getMedianNum(int bArray[], int iFilterLen) 
{
      int bTab[iFilterLen];
      for (byte i = 0; i<iFilterLen; i++)
      {
	  bTab[i] = bArray[i];
      }
      int i, j, bTemp;
      for (j = 0; j < iFilterLen - 1; j++) 
      {
	  for (i = 0; i < iFilterLen - j - 1; i++) 
          {
	    if (bTab[i] > bTab[i + 1]) 
            {
		bTemp = bTab[i];
	        bTab[i] = bTab[i + 1];
		bTab[i + 1] = bTemp;
	     }
	  }
      }
      if ((iFilterLen & 1) > 0)
	bTemp = bTab[(iFilterLen - 1) / 2];
      else
	bTemp = (bTab[iFilterLen / 2] + bTab[iFilterLen / 2 - 1]) / 2;
      return bTemp;
}

void readDoCharacteristicValues(void)
{
    EEPROM_read(SaturationDoVoltageAddress, SaturationDoVoltage);  
    EEPROM_read(SaturationDoTemperatureAddress, SaturationDoTemperature);
    if(EEPROM.read(SaturationDoVoltageAddress)==0xFF && EEPROM.read(SaturationDoVoltageAddress+1)==0xFF && EEPROM.read(SaturationDoVoltageAddress+2)==0xFF && EEPROM.read(SaturationDoVoltageAddress+3)==0xFF)
    {
      SaturationDoVoltage = 1127.6;   //default voltage:1127.6mv
      EEPROM_write(SaturationDoVoltageAddress, SaturationDoVoltage);
    }
    if(EEPROM.read(SaturationDoTemperatureAddress)==0xFF && EEPROM.read(SaturationDoTemperatureAddress+1)==0xFF && EEPROM.read(SaturationDoTemperatureAddress+2)==0xFF && EEPROM.read(SaturationDoTemperatureAddress+3)==0xFF)
    {
      SaturationDoTemperature = 25.0;   //default temperature is 25^C
      EEPROM_write(SaturationDoTemperatureAddress, SaturationDoTemperature);
    }    
}


Mind+(基于Scratch3.0)图形化编程

1、下载及安装软件。下载地址:http://www.mindplus.cc 详细教程:Mind+基础wiki教程-软件下载安装
2、切换到“上传模式”。 详细教程:Mind+基础wiki教程-上传模式编程流程
3、“扩展”中选择“主控板”中的“Arduino Uno”。 "扩展"“传感器”中搜索选择“模拟DO计”。详细教程:Mind+基础wiki教程-加载扩展库流程
4、进行编程,程序如下图:
5、菜单“连接设备”,“上传到设备”
6、程序上传完毕后,打开串口即可看到数据输出。详细教程:Mind+基础wiki教程-串口打印

Mind+DO.png


常见问题

Q1. 如何配制饱和氧水?

A. 一般情况下,往蒸馏水里充空气冒泡20分钟。这样就会使空气与氧气在水中达到饱和,从而制出了100%的溶氧校准标液。

Q2. 如何配置无氧水?

A. 一般情况下,往蒸馏水里加亚硫酸钠直至亚硫酸钠饱和,消耗掉水中的氧气,从而制得无氧水。

Q3. 电极不使用了,如何保存?

A.
短期(过夜或者一周): 装配好的电极应该存放在去离子水或蒸馏水中,防止填充液蒸发。任何不用的时候需要把它从仪器设备上拔下来。
长期:将电极的膜帽拧下来,用去离子水或蒸馏水冲洗电极芯(阴极:白金,阳极:铅)和膜帽组件。再用吸水纸将所有部件吸干。膜帽也可以放置电极上,但是它必须被存放在无填充液的状况下,以防止电极正极的消耗。把所有部件安全地保存在原有的电极盒里。

Q4. 如何配制电极填充液(氢氧化钠溶液(NaOH),浓度为0.5mol/L)?

A. 在使用本传感器前,可能需要配置氢氧化钠溶液(NaOH), 浓度为0.5mol/L,且每100mL氢氧化钠溶液中,添加1~2滴甘油。此溶液作为电极填充液,需要加入电极中,电极才能使用。如无法购买或配置好氢氧化钠溶液,本产品将不能正常使用!

Q5. 常见的故障有哪些?怎么处理?

A. 如果电极在无氧水中的读数不在零或不是非常接近零,请对电极芯头部(阴极)抛光。如果电极读数不在正常读数范围,或者电极读数漂移,请检测膜帽上的薄膜。如果膜帽上的薄膜有可看得见的破裂、穿孔、或污染,请更换膜帽。



更多问题及有趣的应用,可以 访问论坛 进行查阅或发帖。


更多资料


DFshopping car1.png DFRobot商城购买链接

个人工具
名字空间

变换
操作
导航
工具箱