There’s an old saying regarding to keeping fish, “Good fish deserves good water”. Good water quality is very important to the aquatic organisms. Dissolved oxygen is one of the important parameters to reflect the water quality. Low dissolved oxygen in water will lead to difficulty in breathing for aquatic organisms, which may threaten the lives of aquatic organisms their lives. We launched a new opensource dissolved oxygen sensor kit, which is compatible with Arduino. This product is used to measure the dissolved oxygen in water, to reflect the water quality. It is widely applied in many water quality applications, such as aquaculture, environment monitoring, natural science and so on. This sensor kit helps you quickly to build your own dissolved oxygen detector. The probe is a galvanic probe, no need polarization time, and available at any time. The filling solution and membrane cap is replaceable, leading to the low maintenance cost. The signal converter board is plug and play, and has the good compatibility. It can be easily integrated to any control or detecting system. This product is easy to use with high compatibility. With open-source code and detailed tutorial provided, this product is very suitable for your water projects in protecting detecting the dissolved oxygen concentration for the aquatic organisms.

The filling solution is 0.5 mol/L NaOH solution. You need to pour it in the membrane cap before use. Please be caution with the operation because the solution is corrosive. Please wear gloves! If the solution accidentally drops onto the skin, wash your skin with plenty of water immediately.
The oxygen permeable membrane in the membrane cap is sensitive and fragile. Be caution when handling with it. Fingernail and other sharp objects should be avoided.
The DO sensor will consume a little oxygen during the measurement. Please gently stir the solution and let the oxygen to be distributed evenly in the water.


Board Overview

No. Label Description
1 A Analog Signal Output (0~3.0V)
2 + VCC (3.3~5.5V)
3 - GND
4 BNC Probe Cable Connector


This tutorial will show how to use this dissolved oxygen sensor kit. The dissolved oxygen probe is a precision electrochemical sensor. Please pay attention to the usage details.

Before using the dissolved oxygen probe, 0.5 mol/L NaOH solution should be added into the membrane cap as the filling solution of the probe. As NaOH solution has strong corrosivity, protective gloves should be put on before handling the solution. If the solution accidentally drops onto the skin, wash your skin with plenty of water immediately.
The oxygen permeable membrane in the membrane cap is sensitive and vulnerable. Be caution when handling with it. Fingernail and other sharp objects should be avoided.
During the measuring process, the oxygen probe will consume a little oxygen. You need to gently stir the water and let the oxygen to be distributed evenly in water. On the other hand, do not stirring violently to prevent the oxygen in the air from quickly entering into the water.


Prepare the Probe

For a new dissolved oxygen probe, 0.5 mol/L NaOH solution should be added into the membrane cap firstly as the filling solution. If the probe has been used for some time and the error grows greatly, it is time to change the filling solution. The following tutorial details how to fill the probe with the NaOH solution.

Unscrew the membrane cap from the probe and fill about 2/3 volume of the cap with 0.5 mol/L NaOH solution. Make sure the probe is in vertical position with respect to the horizontal plane. Carefully screw the cap back to the probe. It would be nice if a little bit solution overflows out of the cap to ensure the probe is fully filled with NaOH solution.

Connection Diagram

When the probe is filled with NaOH solution, it needs calibration for once. Before calibration, please connect the probe as the following diagram. Connect the probe to BNC connector of the signal converter board. Connect the board to the analog input of Arduino main-board.

Probe Calibration

If this is the first time you use the probe or the probe has been used for some time, the probe needs to be calibrated for accuracy. Common calibration methods: single point calibration and double points calibration. The single point calibration calibrates the probe with saturated dissolved oxygen. The double points calibration calibrates the probe with both saturated dissolved oxygen and zero dissolved oxygen. In most cases, the single point calibration is good enough and convenient. The following tutorial details the process of single point calibration. 1. Upload the sample code to Arduino and open the serial monitor. The program refreshes and prints the dissolved oxygen on the screen every seconds. 2. Dip the probe into the saturated dissolved oxygen water and gently stir the water. Check the dissolved oxygen readings and wait for the readings to be stable.

3. Wait for the dissolved oxygen readings to be stable. Then you can do the calibration. The steps are as followed.

4. After the process stated above, the saturated dissolved oxygen calibration has completed. The probe is now ready for measurement.

Sample Code

 DFRobot Gravity: Analog Dissolved Oxygen Sensor / Meter Kit for Arduino

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

 GNU Lesser General Public License.
 See <> 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: <>

#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] 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,

void setup()
    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
      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:"));

   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 =;
    if (receivedChar == '\n' || receivedBufferIndex == ReceivedBufferLength)
    receivedBufferIndex = 0;
    return true;
        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;
      case 0:
         Serial.println(F("Command Error"));

      case 1:
      enterCalibrationFlag = 1;
      doCalibrationFinishFlag = 0;
      Serial.println(F(">>>Enter Calibration Mode<<<"));
      Serial.println(F(">>>Please put the probe into the saturation oxygen water! <<<"));

     case 2:
         Serial.println(F(">>>Saturation Calibration Finish!<<<"));
         EEPROM_write(SaturationDoVoltageAddress, averageVoltage);
         EEPROM_write(SaturationDoTemperatureAddress, temperature);
         SaturationDoVoltage = averageVoltage;
         SaturationDoTemperature = temperature;
         doCalibrationFinishFlag = 1;

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

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];
    bTemp = (bTab[iFilterLen / 2]   bTab[iFilterLen / 2 - 1]) / 2;
      return bTemp;

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


Q1.How to config the saturated dissolved oxygen water by myself ?
A1. You can pump air into water for about 20 minutes to saturate the water with oxygen to obtain 100% dissolved oxygen standard liquid.
Q2.How to make zero dissolved oxygen water ?
A2.Add sodium sulfite(Na2SO3) into water until it is saturated. This can consume all the oxygen in the water to obtain the zero dissolved oxygen liquid.
Q3.How to store the probe ?
1. Short time (over nigh to a week ): dip the probe into purified water or deionized water to prevent the filling solution to be evaporated. Disconnect the probe from instrument every time when it is not in use.
2. Long time: (over a week): unscrew the cap from the probe and wash the electrodes core (cathode: platinum, anode: lead) and the cap with purified water or deionized water. Dry all the components with tissue. Screw the cap back to the probe without adding any filling solution to prevent the anode from being consumed. Put all the components back into the package.
Q4.How to make 0.5 mol/L NaOH solution?
A4.You need to purchase the NaOH solution firstly, and add 1~2 drops glycerinum to the NaOH solution every 100mL. Only by adding the NaOH solution into the probe can it be ready for use.
Q5.What problems would I usually meet ? How to solve it ?
1. If the readings of zero dissolved oxygen liquid is not zero or close to zero, you can polish the cathode of the probe.
2. If the readings is not within normal range or the readings drifts, please check the membrane on the cap. If there are cracks, holes or contaminations on the membrane, please change the cap.
For any questions, advice or cool ideas to share, please visit the DFRobot Forum.

More Documents

DFshopping_car1.png Get Gravity: Analog Dissolved Oxygen Sensor from DFRobot Store or DFRobot Distributor.

Turn to the Top