0

I have been trying to interface the Figaro KE25 2 lead fuel cell type oxygen gas sensor with Arduino Uno. I am not getting the required output on the Serial Monitor for regular air (Oxygen concentration of 19%).

I have attached the sketch I have typed out, the output I have obtained on the Serial Monitor, the output(mV) vs Oxygen concentration (%) graph of the Figaro KE25 sensor I have obtained from the datasheet, the chemical reactions governing the working of the sensor,the circuit diagram I have used, and the construction of the sensor.

In this question; I will explain the sensor first, then my circuit, then my sketch, and lastly the problem I have with my output.

In electronics, the Anode is the positive electrode and the cathode is the negative electrode. In electrochemistry, the Anode is the Negative electrode and the Cathode is the positive electrode. Do check the image of the construction of the sensor I have provided obtained from the datasheet.[![image of the construction of the sensor][1]][1] Full Technical Datasheet

The Cathode, that is, the positive electrode is the sensing electrode (Oxygen gets consumed here and an electric current is generated). Since a current is being generated by the sensor (fuel cell), no power supply needs to be given to the sensor. Check the image of the chemical reactions provided (obtained from the full technical datasheet of the sensor). Chemical ReactionsTechnical Datasheet

The sensor in question is an analog voltage type sensor. Keeping this and everything I have mentioned above in mind, I have constructed the following circuit with Arduino Uno. Refer the picture I have put up. I have connected the Cathode (the sensing positive electrode, red in colour) to the Arduino's analog A0 pin and the Anode (negative electrode) to the Arduino's ground. Circuit

The sketch I have used is as follows:

 /*the analog pin of the alcohol sensor goes into analog pin A0 of the  
 Arduino*/

 void setup()
 {
     Serial.begin(9600); //sets the baud rate
 }

 void loop()
 {
     float v1;
     float oxy = 0.0;
     float v2 = 0.0;
     pinMode(A0,INPUT_PULLUP);

     /*To make sure that reading is zero when the circuit is open*/

     v1 = analogRead(A0); 

     /*reads the analog value from the Oxygen gas sensor and this value can
     be anywhere from 0 to 1023 (10 bit ADC used in Arduino Uno)*/ 

     if(v1>900) //To make sure that reading is zero when the circuit is open
     {
         v1=0;
     }
     v2 = (v1*3000)/992; 

     /*The above line calculates the output of the sensor in mV using the
     fact that a HIGH reading in an input pin of an Arduino Uno board is
     equivalent to a voltage of 3V or higher. When I displayed just 'v1',
     the lowest of the high values I got was 992. So I equated it to 3V
     (3000mV) and used the unitary method to find out what a non-HIGH v1 
     value would be in terms of mV.*/

     oxy = v2/0.6; 

     /*The above line calculates the Oxygen concentration from the mV output 
     of the sensor using the output(mV) vs concentration(%) graph from the 
     sensor's datasheet. The graph is a straight line passing through (0,0) 
     and hence the governing equation is of the form y = m * x. Where 'y' is 
     'v2' and 'x' is Oxygen conc. in %.*/

     Serial.print("Oxygen value: ");
     Serial.print(v1);
     Serial.print("    ");
     Serial.print(v2);
     Serial.print("mV    ");
     Serial.print(oxy);//prints the Oxygen value
     Serial.println("%");

     delay(1000);
 }

he output(mV) vs Oxygen concentration(%) graph provided in the datasheet is shown below. The straight line labeled as 'KE25' is to be considered. O/P(mV) vs. O2 conc.(%) Product information (datasheet)

he output I have obtained on the serial monitor is as follows: SerialMonitor

Such a high voltage from the sensor is not possible. My output reading is not possible at all. Where have I made a mistake and how do I correct it?

Update 1 : I changed the line pinMode(A0,INPUT_PULLUP) to pinMode(A0,INPUT). Now I am getting sensible values on my Serial Monitor. However, I am also getting many '0' outputs (many). I am also getting many random values (many). I need more information on how to interface a fuel cell type Oxygen sensor using Arduino Uno in terms of how to get a proper voltage reading across such a sensor. I have found no question on electrochemical, fuel cell type Oxygen gas sensor interfacing. All questions asked are regarding sensors that require a power supply. In case of fuel cell type Oxygen gas sensors, current is generated when Oxygen is sensed and hence, no power supply is required. Also, if anyone is into electrochemistry, how do I check the voltage output of a fuel cell type Oxygen gas sensor using a voltmeter? Do I use a DC voltmeter or an AC voltmeter?

Update 2: I've made changes to the code based on @EdgarB's suggestions. The output I am getting now is better but still wrong. I checked if the sensors I am using are functional using a multimeter (I confirmed if the method I am using is right, and it is), and they are. The Oxygen values I am getting now are still really low: 6-8%. I should be getting: 19-21%. I used a reference voltage of 600 mV and modified the code.

Modified code:

 void setup()

 {

     Serial.begin(9600);//sets the baud rate
     analogReference(INTERNAL);
 }

 void loop()
 {
     const float A_REF = 0.6e3;
     const float Scalc = (1/0.6);

     float v1;
     float oxy = 0.0;
     float v2 = 0.0;
     pinMode(A0, INPUT); 
     v1 = analogRead(A0);
     v2 = (v1 * A_REF) / 1024;
     oxy = v2 * Scalc;
     Serial.print("Oxygen value: ");
     Serial.print(v1);
     Serial.print("    ");
     Serial.print(v2,4);
     Serial.print("mV    ");
     Serial.print(oxy,4);//prints the Oxygen value
     Serial.println("%");
     delay(1000);
 }

Output: Output

  • If you remove the pinMode(A0,INPUT_PULLUP); does it improve the readings? – Majenko Jun 20 '18 at 11:18
  • Read this page: arduino.cc/en/Tutorial/AnalogInputPins - Especially the bit entitled "Pull-up resistors". – Majenko Jun 20 '18 at 16:11
  • @Majenko Thanks! That really helped. I changed pinMode(A0,INPUT_PULLUP); to pinMode(A0,INPUT);I had read that page before typing out my sketch, but I didn't pay much attention to the line that said: "Be aware however that turning on a pull-up will affect the values reported by analogRead()." Is this because in a short time interval like 1000 ms there isn't enough time for the voltage to drop to low and then rise again to the correct input given from the sensor? – athindra pavan Jun 21 '18 at 3:33
1

Your problem stems from having the internal pullup resistor for A0 turned on.

The sensor is a current generator with a load resistor (plus thermistor) built in to convert the current into a voltage. By adding a pullup resistor you turn it into a voltage divider across 5V with a small current being injected into the central node:

schematic

simulate this circuit – Schematic created using CircuitLab

Since the datasheet doesn't specify the load resistance nor the source current I have made up some values to demonstrate.

If we take just the current source and load resistance RL+RT and calculate the voltage drop across that we get:

V=R×I = 5000 × 0.00001 = 50mV

Which is a reasonable value. It's a lot of oxygen, but that's because these are made-up values.

Now what would be the voltage with the pullup resistor added? Well, this is where it gets a little more complex. At times like this it's easier to just simulate the circuit in something like the falstad simulator and I can tell you it's 757.1mV (If you want to learn how to do this kind of circuit analysis manually you should learn superposition theory).

A massive change.

Obviously you're not seeing a change that big - and that's because the pullup resistor is more than just a simple resistor. There's many more factors that influence the reading, including the output impedance of the network, as well as the rest of the internal ADC circuitry.

Suffice it to say, though, adding a pullup resistor is a bad idea when using the ADC.

But you want to keep the pullup resistor so you can know if the sensor is connected or not. That's not a problem. Just turn it on and off at will:

pinMode(A0, INPUT_PULLUP);
delay(1);
bool connected = analogRead(A0) < 900;
if (connected) {
    pinMode(A0, INPUT);
    delay(1);
    v1 = analogRead(A0);
}

It's recommended to wait a moment after changing the pullup resistor state before reading from the ADC to reduce electrical noise picked up by the ADC - hence the delay(1) calls.

1

You have three different issues here:

  1. improper use of the pullup resistor
  2. incorrect interpretation of the analog reading
  3. too small input signal

Pullup resistor

Majenko already covered the first issue, with very good advice.

Interpretation of the analog reading

Code Gorilla already explained the second issue. However, since you do not seem to understand, I will add my take here.

The formula to convert an analog reading to an actual voltage is

voltage = reading × analog_reference ÷ 1024.

Yes, it's 1024 rather the often seen (but erroneous) 1023. See the manufacturer's datasheet, which is the only authoritative reference. Doesn't make much of a difference anyway. The main issue is the analog reference voltage which, in the default configuration, is the same as the voltage powering the Arduino, i.e. 5 V.

In a comment, you wrote:

if a pin is configured as INPUT, then the voltage at the pin is 3V or higher

This is complete nonsense. The voltage on an input pin is whatever voltage is provided by the circuit connected to that pin. If you actually mean the threshold voltage for the pin reading HIGH, well, that threshold is typically 2.6 V (see the “typical characteristics” in the datasheet), but the exact value is not guaranteed. The number you quote is “the lowest value where the pin is guaranteed to be read as high”. In other words, the voltage threshold can be lower than 3 V, but not higher.

And all this is completely irrelevant to you. The input thresholds are a feature of the digital input circuitry attached to the pin. This circuit is responsible for deciding whether the pin is LOW or HIGH when you digitalRead() it. But you don't care about the digital value: you are using the pin as an analog input. The ADC converter is a different circuitry that has absolutely nothing to do with the digital input. You can even disable the digital input of the pin and still use the ADC.

Low signal

According to the graph you provide, the sensor should output 12.6 mV in normal air (21% O2). That's about 2.58 ADC steps. Thus you expect the reading to fluctuate between the digital values 2 and 3. This is really quite low for your ADC.

A simple way to mitigate this problem is to change the analog reference. The default is 5 V, but you can switch it to an internal 1.1 V reference that will significantly increase your resolution: in setup(), call

analogReference(INTERNAL);

Then you can compute the oxygen level as follows:

const float A_REF = 1.1e3;         // analog reference = 1,100 mV
const float SENSOR_CAL = 1 / 0.6;  // calibration: %(O2) / mV

void loop()
{
    int reading = analogRead(A0);
    float voltage = reading * (A_REF / 1024);
    float oxygen = voltage * SENSOR_CAL;
    // ...
}

Another way to mitigate the signal weakness is to average multiple readings. This can make your effective resolution better than the raw ADC resolution.

ADC offset

The formula

voltage = reading × analog_reference ÷ 1024

applies only to an ideal ADC. In practice, any ADC has some offset error and gain error. Taking them into account, the formula becomes:

voltage = (reading + offset_error) × analog_reference ÷ (1024 + gain_error)

In many cases these errors are too small to care about. However, since you are working near the resolution limit of your ADC, the offset error can be an important concern. The gain error is no issue here, so you can forget about it.

If you have a voltmeter, you can easily determine the offset error: measure a low voltage, like the one from your sensor, with both the voltmeter and the ADC. Ideally, you would average many ADC readings to get a better resolution. From the voltmeter measurement, you compute the average reading you would expect from your ADC, then you get

offset_error = expected_ADC_reading − actual_ADC_reading

Then you use this measured offset to correct your readings:

const float offset_error = ...;  // measured with the voltmeter

void loop()
{
    int reading = analogRead(A0);
    float voltage = (reading + offset_error) * (A_REF / 1024);
    // ...
}
  • I meant that if a pin is configured as INPUT and is at HIGH then the voltage at that pin is 3V or higher. I will read the rest of your answer and update on the status soon. – athindra pavan Jun 21 '18 at 12:22
  • @athindrapavan: Understood what you mean, but: 1. It's wrong (a pin can read HIGH even at 2.2 V) 2. It's irrelevant to an analog reading. – Edgar Bonet Jun 21 '18 at 12:31
  • First of all, thanks! That helped in getting a better, more precise output. Plus, I understand the assignable constants better now. But I'm still getting a wrong output in terms of the range of Oxygen percentage values I should be getting. I'm getting around 6-8% of Oxygen which is still too low. It has to be 19-21% in regular air. I checked if the sensors I am using are functional with a multimeter and they are. I'll update the question with the modified code and the output I am getting in some time. Please do check it. – athindra pavan Jun 25 '18 at 7:01
  • @athindrapavan: 1. The internal analog reference is 1.1 V, not 600 mV. You cannot just make up a number and expect that to work. 2. You may also suffer from ADC offset error. See the amended answer. – Edgar Bonet Jun 25 '18 at 8:02
  • @ EdgarB Your answer made a lot of sense and the setup is working now. Is the offset error value sensor dependent? That is if I use 2 new Figaro KE-25 sensors, then will it cause an error in one of the 2 sensors depending on which one I used to calculate the offset error? – athindra pavan Jun 25 '18 at 9:44
0

You calculation of v2 is incorrect.

v2 = (v1 * 3000) / 992;

The maximum value v1 will have is 1023 and this equates to 5v (not 3v). So to calculate the voltage v2 I think you should be doing:

v2 = (v1 / 1023) * 5000; 

But this will make your calculations even further out!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.