Abnormal peak voltage in Arduino sensor reading

Dear all,

I am running few tests in a chamber where the oxygen level is being sucked so the % is going down and my Oxygen sensor is reading it... BUT something weird is happening, though!

After around 5h 30min, I have a small peak in voltage, less than 2.5%, something like:


image uploader

The thing is that this is happening for all the tests I run until now (5!) exactly after 5h 25min - 5h 30min... Weird, isn't it?

Any idea?

I was thinking about some strange voltage peak/drop in the building but it is not because the tests are at a different time of the day...

Thanks a lot for any idea.

M.

Self calibration/adjustment due to temperature rise as there is less gas to carry away heat?

Does your sensor have a heater?

Rather than guess, it would be helpful to see a link to your sensor, how its connected (circuit diagram), and your code.

Sorry for not providing the full information. Here we go:

Sensor: ME2-O2
http://www.winsen-sensor.com/products/electrochemical-oxygen-sensor/me2-o2-ф20.html

Connections:
GRD to GRD (through breadboard)
VCC to 5V (through breadboard)
SIG to A2 (straight into Arduino UNO)

Code:

// Oxygen content reading in real-time with ME2-O2 Sensor, LCD screen, RTC and SD logging data - ACTIVATION (QUICK VERSION)

// Libraries
#include <LiquidCrystal.h> // Includes the libraries for the LCD
#include <DS3231.h>; // Includes the libraries for the clock
#include <SPI.h>; // Includes the libraries for the Serial Peripheral Interface that allows the Master (Arduino) to control a slave device
#include <SD.h>; // Includes the libraries for the SD card

// Interface and Objects definitions
DS3231 rtc(SDA, SCL); // Initialises the DS3231 using the hardware interface
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Creates a LCD LiquidCrystal object
const int chipSelect = 10; // Assigns the pin for the SPI-SD chip selection

void setup()
{
  pinMode(chipSelect, OUTPUT); // Ensures that the SPI-SD selection pin is an output
  
  rtc.begin(); // Initialises the rtc object
  // rtc.setTime(10, 51, 05); // Sets the time HH:MM:SS (24hr format)
  // rtc.setDate(15, 2, 2017); // Sets the date DDMMYYYY
  lcd.begin(16, 2); // Initialises the interface to the LCD screen, and specifies the dimensions
  // Initialising sensor and SD card:
  // lcd.print("Initialising SD");
  // delay (5000); // Waits 5 seconds
  lcd.clear(); // // Clears the LCD screen and positions the cursor in the upper-left corner
  if (!SD.begin(chipSelect)) // Checks if the SD card is working
  {
    lcd.println("SD failed!");
    delay(6000);
    return;
  }
  //lcd.println("SD OK");
  //delay(1000); 
  //lcd.clear();
  //lcd.print("Reading sensor"); // Prints to the LCD "Reading sensor"
  //delay (1000);
}

void loop()
{
  File readings = SD.open("readings.csv", FILE_WRITE); // Opens the file on the SD card
  
  // Defining the sensor variables:
  float sensorValue;
  float sensorVoltage;
  float SensorVoltageADJ;
  float OxygenCont;
  sensorValue = analogRead(A2);
  sensorVoltage =(sensorValue/1024)*5.0; // It adapts the output with a fraction of 0-5V input from Arduino Voltage. In V.
  SensorVoltageADJ = sensorVoltage/344*1000; // I am getting rid of the OPAmp. We can use this in the characteristic curve provided with the sensor. In mV.
  OxygenCont = SensorVoltageADJ*20.95/5.90; // Calibration with reference to standard condition (linear interpolation)
  
  if (isnan(sensorValue)) // Checks if any reads failed and exit early (to try again)
  {
    lcd.println("Sensor FAIL!");
    delay(6000);
    return;
  }
  lcd.clear();
  
  if (readings) // If the file opened OK, write to it on SD card
  {
    Time t = rtc.getTime(); // Defines a structure for the date and time
    readings.print(t.year); // Years
    readings.print(","); // Equivalent to "TAB", moves to the next horizontal cell
    readings.print(t.mon); // Months
    readings.print(",");
    readings.print(t.date); // Day of the month
    readings.print(",");
    readings.print(t.hour); // Hours
    readings.print(",");
    readings.print(t.min); // Minutes
    readings.print(",");
    readings.print(t.sec); // Seconds
    readings.print(",");
    readings.println(OxygenCont); // Using the characteristic curve provided with the sensor to output the Oxygen % HP: linear from zero
    readings.close(); // close the file to save the data on the SD card
  }
  else // if the file didn't open, print an error
  {
   lcd.println("FILE NOT OPEN");
   delay(6000);
  }
  
  // Printing the results on the LCD screen with time and date:
  lcd.print("Oxygen: "); lcd.print(OxygenCont); lcd.print("%");
  
  delay(15000); // Defines the time step of the readings (milliseconds)
}

Thanks for any help.

N.B. With another test today, same issue 0.5% Oxygen% peak after 5h 30min, incredible, there must be SOMETHING going on here :frowning:


image hosting

Your formulas test OK (linear OxygenCont values for all possible ADC values):

// Oxygen content reading in real-time with ME2-O2 Sensor (formula test)

int sensorReading = 1023;

void setup()
{
Serial.begin(115200);
}

void loop()
{
  // Defining the sensor variables:
  float sensorValue;
  float sensorVoltage;
  float SensorVoltageADJ;
  float OxygenCont;
  sensorValue = sensorReading;
  sensorVoltage = (sensorValue / 1024) * 5.0; // It adapts the output with a fraction of 0-5V input from Arduino Voltage. In V.
  SensorVoltageADJ = sensorVoltage / 344 * 1000; // I am getting rid of the OPAmp. We can use this in the characteristic curve provided with the sensor. In mV.
  OxygenCont = SensorVoltageADJ * 20.95 / 5.90; // Calibration with reference to standard condition (linear interpolation)

  if (isnan(sensorValue)) // Checks if any reads failed and exit early (to try again)
  {
    Serial.println("Sensor FAIL!");
    return;
  }
  Serial.print("OxygenCont ");
  Serial.println(OxygenCont); // Using the characteristic curve provided with the sensor to output the Oxygen % HP: linear from zero

  sensorReading--;
  if (sensorReading <= 0) while (1); //stop test
}

In your code, you have this

 delay(15000); // Defines the time step of the readings (milliseconds)

which should be replaced with a millis() timer as in the BlinkWithoutDelay example. I think this could cause various issues, like stalling SD card operations, etc.

What is the file size at 5h 30min? Perhaps at this point, the SD card needs go through some operations to prepare for a larger file size.

As a test, couldn't you just create a file with size that could handle (for example) 12hrs of data in setup? Then just open it once in setup. In the main loop, fill the file with data, then at 12hrs, close the file.

Let's try to understand together what's going on here.

mikb55, I matched the temperature path with the sensor reading for the first 4 tests:

test1

upload image

test2

upload image

test3

upload image

test4

upload image

As we can see there might be some change in temperature affecting the sensor calibration etc but they are not too clear. But it might be a good point yours.

dlloyd, I thank you as well for your good insights.

I was able to isolate the line after which we have the sudden peak (even small...) so the size of the 4 files (size ON DISK) until the peak is exactly 32K and precisely 32768 bytes.
The size (NOT on disk, I am reading from windows properties of the files) is:
test1 - 31.8K (32652 bytes)
test2 - 31.8K (32603 bytes)
test3 - 31.8K (32654 bytes)
test4 - 31.6K (32385 bytes)

Interesting, isn't it?

I will try further tests as indicated by you as soon as I can.

M.

Yes, interesting. The max positive value of an int = 32,767

An easy test might be to just comment out the readings for year, month and day. Then see if the time at which the peak occurs is extended.

//readings.print(t.year); // Years
readings.print(","); // Equivalent to "TAB", moves to the next horizontal cell
//readings.print(t.mon); // Months
readings.print(",");
//readings.print(t.date); // Day of the month
readings.print(",");

Another test. Here, a millis() timer is used instead of delay() for your 15 second reading interval

// Oxygen content reading in real-time with ME2-O2 Sensor, LCD screen, RTC and SD logging data - ACTIVATION (QUICK VERSION)

// Libraries
#include <LiquidCrystal.h> // Includes the libraries for the LCD
#include <DS3231.h>; // Includes the libraries for the clock
#include <SPI.h>; // Includes the libraries for the Serial Peripheral Interface that allows the Master (Arduino) to control a slave device
#include <SD.h>; // Includes the libraries for the SD card

// Interface and Objects definitions
DS3231 rtc(SDA, SCL); // Initialises the DS3231 using the hardware interface
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Creates a LCD LiquidCrystal object
const int chipSelect = 10; // Assigns the pin for the SPI-SD chip selection

unsigned long previousMillis = 0;
const word interval = 15000; // Defines the time step of the readings (milliseconds)

void setup()
{
  pinMode(chipSelect, OUTPUT); // Ensures that the SPI-SD selection pin is an output

  rtc.begin(); // Initialises the rtc object
  // rtc.setTime(10, 51, 05); // Sets the time HH:MM:SS (24hr format)
  // rtc.setDate(15, 2, 2017); // Sets the date DDMMYYYY
  lcd.begin(16, 2); // Initialises the interface to the LCD screen, and specifies the dimensions
  // Initialising sensor and SD card:
  // lcd.print("Initialising SD");
  // delay (5000); // Waits 5 seconds
  lcd.clear(); // // Clears the LCD screen and positions the cursor in the upper-left corner
  if (!SD.begin(chipSelect)) // Checks if the SD card is working
  {
    lcd.println("SD failed!");
    delay(6000);
    return;
  }
  //lcd.println("SD OK");
  //delay(1000);
  //lcd.clear();
  //lcd.print("Reading sensor"); // Prints to the LCD "Reading sensor"
  //delay (1000);
}

void loop()
{
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    File readings = SD.open("readings.csv", FILE_WRITE); // Opens the file on the SD card

    // Defining the sensor variables:
    float sensorValue;
    float sensorVoltage;
    float SensorVoltageADJ;
    float OxygenCont;
    sensorValue = analogRead(A2);
    sensorVoltage = (sensorValue / 1024) * 5.0; // It adapts the output with a fraction of 0-5V input from Arduino Voltage. In V.
    SensorVoltageADJ = sensorVoltage / 344 * 1000; // I am getting rid of the OPAmp. We can use this in the characteristic curve provided with the sensor. In mV.
    OxygenCont = SensorVoltageADJ * 20.95 / 5.90; // Calibration with reference to standard condition (linear interpolation)

    if (isnan(sensorValue)) // Checks if any reads failed and exit early (to try again)
    {
      lcd.println("Sensor FAIL!");
      delay(6000);
      return;
    }
    lcd.clear();

    if (readings) // If the file opened OK, write to it on SD card
    {
      Time t = rtc.getTime(); // Defines a structure for the date and time
      readings.print(t.year); // Years
      readings.print(","); // Equivalent to "TAB", moves to the next horizontal cell
      readings.print(t.mon); // Months
      readings.print(",");
      readings.print(t.date); // Day of the month
      readings.print(",");
      readings.print(t.hour); // Hours
      readings.print(",");
      readings.print(t.min); // Minutes
      readings.print(",");
      readings.print(t.sec); // Seconds
      readings.print(",");
      readings.println(OxygenCont); // Using the characteristic curve provided with the sensor to output the Oxygen % HP: linear from zero
      readings.close(); // close the file to save the data on the SD card
    }
    else // if the file didn't open, print an error
    {
      lcd.println("FILE NOT OPEN");
      delay(6000);
    }

    // Printing the results on the LCD screen with time and date:
    lcd.print("Oxygen: "); lcd.print(OxygenCont); lcd.print("%");

  } // end interval
} // end loop

Again, test 5:

size on disk: 32768 bytes (size 32536 bytes)

Interesting the fact that it seems that the temperature is not affecting the sensor (Am I right?):


free hosting online

I will try all the suggested tests as soon as I can.

Thanks a lot.

// Oxygen content reading in real-time with ME2-O2 Sensor, LCD screen, RTC and SD logging data - ACTIVATION (QUICK VERSION)

// Libraries
#include <LiquidCrystal.h> // Includes the libraries for the LCD
#include <DS3231.h>; // Includes the libraries for the clock
#include <SPI.h>; // Includes the libraries for the Serial Peripheral Interface that allows the Master (Arduino) to control a slave device
#include <SD.h>; // Includes the libraries for the SD card

// Interface and Objects definitions
DS3231 rtc(SDA, SCL); // Initialises the DS3231 using the hardware interface
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Creates a LCD LiquidCrystal object
const int chipSelect = 10; // Assigns the pin for the SPI-SD chip selection

unsigned long previousMillis = 0;
const word interval = 15000; // Defines the time step of the readings (milliseconds)

void setup()
{
  pinMode(chipSelect, OUTPUT); // Ensures that the SPI-SD selection pin is an output

  rtc.begin(); // Initialises the rtc object
  // rtc.setTime(10, 51, 05); // Sets the time HH:MM:SS (24hr format)
  // rtc.setDate(15, 2, 2017); // Sets the date DDMMYYYY
  lcd.begin(16, 2); // Initialises the interface to the LCD screen, and specifies the dimensions
  // Initialising sensor and SD card:
  // lcd.print("Initialising SD");
  // delay (5000); // Waits 5 seconds
  lcd.clear(); // // Clears the LCD screen and positions the cursor in the upper-left corner
  if (!SD.begin(chipSelect)) // Checks if the SD card is working
  {
    lcd.println("SD failed!");
    delay(6000);
    return;
  }
  //lcd.println("SD OK");
  //delay(1000);
  //lcd.clear();
  //lcd.print("Reading sensor"); // Prints to the LCD "Reading sensor"
  //delay (1000);
}

void loop()
{
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    File readings = SD.open("readings.csv", FILE_WRITE); // Opens the file on the SD card

    // Defining the sensor variables:
    float sensorValue;
    float sensorVoltage;
    float SensorVoltageADJ;
    float OxygenCont;
    sensorValue = analogRead(A2);
    sensorVoltage = (sensorValue / 1024) * 5.0; // It adapts the output with a fraction of 0-5V input from Arduino Voltage. In V.
    SensorVoltageADJ = sensorVoltage / 344 * 1000; // I am getting rid of the OPAmp. We can use this in the characteristic curve provided with the sensor. In mV.
    OxygenCont = SensorVoltageADJ * 20.95 / 5.90; // Calibration with reference to standard condition (linear interpolation)

    if (isnan(sensorValue)) // Checks if any reads failed and exit early (to try again)
    {
      lcd.println("Sensor FAIL!");
      delay(6000);
      return;
    }
    lcd.clear();

    if (readings) // If the file opened OK, write to it on SD card
    {
      Time t = rtc.getTime(); // Defines a structure for the date and time
      readings.print(t.year); // Years
      readings.print(","); // Equivalent to "TAB", moves to the next horizontal cell
      readings.print(t.mon); // Months
      readings.print(",");
      readings.print(t.date); // Day of the month
      readings.print(",");
      readings.print(t.hour); // Hours
      readings.print(",");
      readings.print(t.min); // Minutes
      readings.print(",");
      readings.print(t.sec); // Seconds
      readings.print(",");
      readings.println(OxygenCont); // Using the characteristic curve provided with the sensor to output the Oxygen % HP: linear from zero
      readings.close(); // close the file to save the data on the SD card
    }
    else // if the file didn't open, print an error
    {
      lcd.println("FILE NOT OPEN");
      delay(6000);
    }

    // Printing the results on the LCD screen with time and date:
    lcd.print("Oxygen: "); lcd.print(OxygenCont); lcd.print("%");

  } // end interval
} // end loop

I tried this. Same problem after 32768 bytes (size on disk)

As soon as I can I will run another one with the lines commented out.

Any further idea in the meanwhile guys?

This looks an "intersting" situation to investigate all together :slight_smile:

For the sensorValue range of 0 to 1023, you'll get OxygenCont values from 0 to 51.56.

The minimal test code below should determine if the abnormal peak is coming from the sensor. If you still see the problem, then the problem is in your hardwware (not code).

Use Serial Plotter to view graph ... reading #1320 is 5hrs 30min. Note that the graph scrolls and only shows about 400 readings (1hr 40min) so you would have to check it every hour or so.

Test using Serial Monitor to show all data as you can scroll back to the beginning.

// ME2-O2 Sensor Test

const word interval = 15000;  // reading interval (15 sec)
unsigned long previousMillis = 0;
unsigned long currentMillis;

float sensorValue;
float sensorVoltage;
float SensorVoltageADJ;
float OxygenCont;

void setup() {
  Serial.begin(115200);
}

void loop()
{
  currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    sensorValue = analogRead(A2);
    sensorVoltage = (sensorValue / 1024.0) * 5.0; // It adapts the output with a fraction of 0-5V input from Arduino Voltage. In V.
    SensorVoltageADJ = sensorVoltage / 344.0 * 1000.0; // I am getting rid of the OPAmp. We can use this in the characteristic curve provided with the sensor. In mV.
    OxygenCont = SensorVoltageADJ * 20.95 / 5.90; // Calibration with reference to standard condition (linear interpolation)
    Serial.println(OxygenCont); // Using the characteristic curve provided with the sensor to output the Oxygen % HP: linear from zero
  } // end interval
} // end loop

Thanks I will do it (sorry for the late reply but I was away).