(SKU:SEN0237)溶解氧传感器

来自DFRobot Product Wiki
跳转至: 导航搜索
产品名称

Update/modify/delete Forbidden, 禁止更改本图,请更改图片名称,避免覆盖上传

目录

简介

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

Warning yellow.png
  • 待撰写


技术规格

  • 溶解氧电极
    • 电极类型:原电池
    • 检测范围:0~20mg/L
    • 响应时间:
    • 最大水压:
    • 温度范围:
    • 使用寿命:
    • 维护时间:
    • 连线长度:
    • 连线接口:BNC
  • 信号转接板
    • 供电电压:3.3~5.5V
    • 电极接口:BNC接口
    • 信号接口: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氢氧化钠溶液,作为电极的填充液。如果电极使用有一段时间后,误差明显增大时,需要更换填充液。
下面将详细说明如何给溶解氧电极添加填充液。

1.取出溶解氧电极,然后去掉电极连线接口上的BNC短接帽。

  • 这个BNC短接帽是直接短路电极的信号输出线,在不添加填充液的情况下,可使得电极的两极保持相同的电势,从而保护电极。但添加保护液后,电极将输出电信号,此时不能短路电极的输出,因此在添加填充液之前,必须去掉电极连线接口上的BNC短接帽,否则会影响电极寿命。
BncPullOut.png

2.拿起电极,使得电极方向与重力方向一致(电极与水平面垂直)。拧开电极的膜帽盖,加入0.5mol/L氢氧化钠溶液到膜帽盖中,倒入量大概为膜帽容积的二分之一,然后把膜帽套到电极上再拧紧,以溢出一点氢氧化钠溶液为佳。

  • 拿起电极的时候,电极尽量与重力方向一致(电极与水平面垂直),否则在套上加有填充液的膜帽并拧紧时,会出现不均匀,导致气泡产生。
  • 加入填充液时,不能把膜帽加满,否则套到电极上时,会溢出很多填充液;也不可太少,没加够填充液会导致膜帽内部产生气泡。故倒入二分之一左右,膜帽拧紧后,以溢出一点氢氧化钠溶液为佳。
  • 膜帽上溢出的氢氧化钠溶液,直接吸干或者擦净即可。
  • 开封过的氢氧化钠溶液瓶,需尽快密封好,防止空气中的二氧化碳对其产生影响。
AddSolutionUpdate.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 analogBufferIndex = 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();
      averageVoltage = getMedianNum(analogBuffer,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 = 3300.0;   //
      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);
    }    
}


常见问题

Q1. 如何配制饱和氧水?

A. 一般情况下,用气泵往水溶液里打入空气,持续15分钟以上即可。

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

A. 将电极膜帽中的填充液倒出,然后用清水清洗电极与膜帽。在阴凉处晾干后,把膜帽套到电极上,不要拧紧,大约拧一半即可。电极连线接口套上BNC短接帽。整套放入包装盒中保存即可。



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


更多



DFshopping car1.png Link DFRobot商城购买链接

个人工具
名字空间

变换
操作
导航
工具箱