Skip to content
This repository has been archived by the owner on Oct 15, 2024. It is now read-only.

What is the "EQ" value, and how to convert it to ppm #5

Closed
moemuses opened this issue Feb 27, 2022 · 10 comments
Closed

What is the "EQ" value, and how to convert it to ppm #5

moemuses opened this issue Feb 27, 2022 · 10 comments
Assignees
Labels
Seeed_Arduino_MultiGas Label for Seeed_Arduino_MultiGas UAY Unassigned yet

Comments

@moemuses
Copy link

In Arduino example "demo_background". the Serial print "eq" value. what is this value ?
i mean it is ppm or ppb or how to use it ?
thank you.

@lakshanthad
Copy link

lakshanthad commented Mar 3, 2022

Hello @moemuses,

"eq" values corresponds to the raw ADC values from the gas sensors. "V" values represent the corresponding voltage values for the ADC values.

Unfortunately, there is no straightforward way to calculate PPM values. You can check the wiki for more references:
https://wiki.seeedstudio.com/Grove-Multichannel-Gas-Sensor-V2

You might want to test a few gases and check the values you obtain from them.

Also, if you are familiar with Edge Impulse, you can have a look at the project below for reference:
https://makezine.com/projects/second-sense-build-an-ai-smart-nose

@positron96
Copy link

positron96 commented Feb 20, 2023

Hi. The wiki shows an example with TFT screen that treats these values as ppm : https://wiki.seeedstudio.com/Grove-Multichannel-Gas-Sensor-V2/#hardware-overview
It takes the values of gas.getGM102B(), for example, and draws it on the display with "ppm" label. Does it mean that the sensor now outputs ppm?

@brmmm3
Copy link

brmmm3 commented Feb 20, 2023

Yes I see this. But the measured raw values are completely wrong. They have to be corrected through a calibration table. The raw values depend on temperature and humidity. Isn't there a reference code with an example table of a calibrated sensor?

@brmmm3
Copy link

brmmm3 commented Feb 21, 2023

Currently I've implemented the following code to get better values, but the values are still not reliable. The parameter tables are from the datasheets:

// GM102B (NO2)
// Rs means resistance of sensor in 2ppm NO2 under different temp. and humidity.
// Rso means resistance of the sensor in 2ppm NO2 under 20°C/55%RH.

const float gm102b_rh_offset[4][7] PROGMEM = {
  { -10.0, 0.0, 10.0, 20.0, 30.0, 40.0, 50.0 }, // °C
  { 1.71, 1.58, 1.45, 1.39, 1.12, 1.00, 0.89 }, // Rs/R0 @ 30%RH
  { 1.49, 1.32, 1.28, 1.08, 0.99, 0.88, 0.71 }, // Rs/R0 @ 60%RH
  { 1.28, 1.15, 10.9, 0.90, 0.86, 0.71, 0.68 }  // Rs/R0 @ 85%RH
};

const float gm102b_u2gas[2][12] PROGMEM = {
  { 0.0, 0.21, 0.39, 0.7, 0.95, 1.15, 1.35, 1.45, 1.6, 1.69, 1.79, 1.81 }, // V
  { 0.0, 0.5, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }, // NO2 [ppm]
};

// GM302B (Ethanol=C2H5OH)
// Rs means resistance of sensor in 50ppm ethanol under different temp. and humidity.
// Rso means resistance of the sensor in 50ppm ethanol under 20°C/65%RH.

const float gm302b_rh_offset[4][13] PROGMEM = {
  { -10.0, -5.0, 0.0, 5.0, 10.0, 15.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0 },  // °C
  { 1.71, 1.61, 1.58, 1.50, 1.42, 1.30, 1.25, 1.18, 1.15, 1.12, 1.00, 0.92, 0.88 }, // Rs/R0 @ 30%RH
  { 1.45, 1.36, 1.33, 1.28, 1.20, 1.11, 1.08, 1.00, 0.98, 0.95, 0.85, 0.79, 0.73 }, // Rs/R0 @ 60%RH
  { 1.27, 1.20, 1.18, 1.10, 1.05, 0.95, 0.92, 0.88, 0.86, 0.81, 0.72, 0.69, 0.64 }  // Rs/R0 @ 85%RH
};

const float gm302b_u2gas[2][11] PROGMEM = {
  { 1.25, 1.5, 2.0, 2.25, 2.5, 3.1, 3.3, 3.6, 3.7, 3.8, 3.85 }, // Alcohol/Ethanol [V]
  { 0.0, 1.0, 3.5, 5.0, 10.0, 30.0, 50.0, 80.0, 100.0, 200.0, 500.0 } // VOC [ppm]
};

// GM502B (VOC)
// Rs means resistance of sensor in 150ppm CO gas under different temp. and humidity.
// Rso means resistance of the sensor in 150ppm CO gas under 20°C/55%RH.

const float gm502b_rh_offset[4][13] PROGMEM = {
  { -10.0, -5.0, 0.0, 5.0, 10.0, 15.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0 },  // °C
  { 1.71, 1.62, 1.54, 1.50, 1.42, 1.30, 1.25, 1.16, 1.14, 1.11, 1.00, 0.92, 0.88 }, // Rs/R0 @ 30%RH
  { 1.45, 1.38, 1.35, 1.28, 1.21, 1.11, 1.08, 1.00, 0.98, 0.96, 0.85, 0.79, 0.75 }, // Rs/R0 @ 60%RH
  { 1.25, 1.20, 1.18, 1.10, 1.05, 0.95, 0.92, 0.88, 0.86, 0.81, 0.73, 0.68, 0.62 }  // Rs/R0 @ 85%RH
};

const float gm502b_u2gas[2][9] PROGMEM = {
  { 2.52, 2.90, 3.20, 3.40, 3.60, 3.90, 4.05, 4.15, 4.20 }, // Alcohol [V]
  { 0.0, 1.0, 3.5, 5.0, 10.0, 30.0, 50.0, 80.0, 100.0 }  // VOC [ppm]
};

// GM702B (CO)
// Rs means resistance of sensor in 150ppm CO gas under different temp. and humidity.
// Rso means resistance of the sensor in 150ppm CO gas under 20°C/55%RH

const float gm702b_rh_offset[4][7] PROGMEM = {
  { -10.0, 0.0, 10.0, 20.0, 30.0, 40.0, 50.0 }, // °C
  { 1.71, 1.58, 1.45, 1.38, 1.13, 1.01, 0.88 }, // Rs/R0 @ 30%RH
  { 1.47, 1.32, 1.28, 1.08, 0.98, 0.88, 0.72 }, // Rs/R0 @ 60%RH
  { 1.28, 1.15, 1.08, 0.90, 0.87, 0.71, 0.68 }  // Rs/R0 @ 85%RH
};

const float gm702b_u2gas[2][9] PROGMEM = {
  { 0.25, 0.65, 0.98, 1.35, 1.8, 1.98, 2.1, 2.38, 2.42 }, // V
  { 0.0, 5.0, 10.0, 20.0, 50.0, 100.0, 160.0, 500.0, 1000.0 }  // CO [ppm]
};

bool setupMultiGas(int16_t y) {
  // If you have changed the I2C address of gas sensor, you must to be specify the address of I2C.
  //The default addrss is 0x08;
  multigas.begin(Wire, 0x08); // use the hardware I2C
  //gas.begin(MyWire, 0x08); // use the software I2C
  //gas.setAddress(0x64); change thee I2C address
  return true;
}

float u_corr_rh(float u, float temp, float humidity, float* u_corr, size_t size) {
  size_t hum_idx1;
  size_t hum_idx2;
  float ref_hum1, ref_hum2;
  if (humidity <= 30.0) {
    hum_idx1 = 1;
    hum_idx2 = 1;
    ref_hum1 = 30.0;
    ref_hum1 = 60.0;
  } else if (humidity <= 60.0) {
    hum_idx1 = 1;
    hum_idx2 = 2;
    ref_hum1 = 30.0;
    ref_hum2 = 60.0;
  } else if (humidity <= 85.0) {
    hum_idx1 = 2;
    hum_idx2 = 3;
    ref_hum1 = 60.0;
    ref_hum2 = 85.0;
  } else {
    hum_idx1 = 3;
    hum_idx2 = 3;
    ref_hum1 = 60.0;
    ref_hum2 = 85.0;
  }
  size_t hum_off1 = size * hum_idx1;
  size_t hum_off2 = size * hum_idx2;
  // First get Rs/R0
  float old_rsr01 = *(u_corr + hum_off1);
  float old_rsr02 = *(u_corr + hum_off2);
  float rsr01 = old_rsr01;
  float rsr02 = old_rsr02;
  float old_temp = u_corr[0];
  if (temp >= old_temp) {
    for (size_t i = 1; i < size; i++) {
      float new_temp = *(u_corr + i);
      rsr01 = *(u_corr + hum_off1 + i);
      rsr02 = *(u_corr + hum_off2 + i);
      //Serial.println("*i=" + String(i) + "  old_temp=" + String(old_temp, 2) + "  new_temp=" + String(new_temp, 2) + "  rsr01=" + String(rsr01, 2) + "  rsr02=" + String(rsr02, 2));
      if (temp <= new_temp) {
        //Serial.println("-- " + String(temp - old_temp, 2) + "  " + String(new_temp - old_temp, 2) + "  " + String(rsr01 - old_rsr01, 2) + "  " + String(rsr02 - old_rsr02, 2));
        old_rsr01 += (temp - old_temp) / (new_temp - old_temp) * (rsr01 - old_rsr01);
        old_rsr02 += (temp - old_temp) / (new_temp - old_temp) * (rsr02 - old_rsr02);
        break;
      }
      old_temp = new_temp;
      old_rsr01 = rsr01;
      old_rsr02 = rsr02;
    }
  }
  float fact = (old_rsr01 + (humidity - ref_hum1) / (ref_hum2 - ref_hum1) * (old_rsr02 - old_rsr01));
  //Serial.println("old_rsr01=" + String(old_rsr01, 2) + "  old_rsr02=" + String(old_rsr02, 2) + "  ref_hum1=" + String(ref_hum1, 2) + "  ref_hum2=" + String(ref_hum2, 2));
  //Serial.println("fact=" + String(fact));
  return u / fact;
}

float u2ppm(float u, float* u2gas, size_t size) {
  float old_ppm = *(u2gas + size);
  float old_u = u2gas[0];
  if (u <= old_u) {
    return old_ppm;
  }
  for (size_t i = 1; i < size; i++) {
    float new_u = *(u2gas + i);
    float ppm = *(u2gas + size + i);
    //Serial.println("i=" + String(i) + "  new_u=" + String(new_u, 2) + "  ppm=" + String(ppm, 2));
    if (u <= new_u) {
      //Serial.println("++ " + String(u - old_u, 2) + "  " + String(new_u - old_u, 2) + "  " + String(ppm - old_ppm, 2));
      return old_ppm + (u - old_u) / (new_u - old_u) * (ppm - old_ppm);
    }
    old_u = new_u;
    old_ppm = ppm;
  }
  return old_ppm;
}

float getNO2ppm(uint32_t raw, float temp, float humidity) {
  float no2_u = multigas.calcVol(raw);
  float no2_corr = u_corr_rh(no2_u, temp, humidity, (float *)gm102b_rh_offset, 7);
  return u2ppm(no2_corr, (float *)gm102b_u2gas, 12);
}

float getC2H5OHppm(uint32_t raw, float temp, float humidity) {
  float c2h5oh_u = multigas.calcVol(raw);
  float c2h5oh_corr = u_corr_rh(c2h5oh_u, temp, humidity, (float *)gm302b_rh_offset, 13);
  return u2ppm(c2h5oh_corr, (float *)gm302b_u2gas, 11);
}

float getVOCppm(uint32_t raw, float temp, float humidity) {
  float voc_u = multigas.calcVol(raw);
  float voc_corr = u_corr_rh(voc_u, temp, humidity, (float *)gm502b_rh_offset, 13);
  return u2ppm(voc_corr, (float *)gm502b_u2gas, 9);
}

float getCOppm(uint32_t raw, float temp, float humidity) {
  float co_u = multigas.calcVol(raw);
  float co_corr = u_corr_rh(co_u, temp, humidity, (float *)gm702b_rh_offset, 7);
  return u2ppm(co_corr, (float *)gm702b_u2gas, 9);
}

@billy27607
Copy link

brmmm3, is there any chance that you could provide the full sketch that you are using to test the sensor?

@atsclct
Copy link

atsclct commented Mar 18, 2023

brmmm3, just saw your post here. This helped me to update the python code to convert voltage to Pmm at https://github.com/atsclct/atsc_sensors.
One question: voltage values for 302b and 502b are greater than 3.3v. From what I tested, the voltage after calc_vol is difficult to reach 1.25 and 2.52 for these two units. Do we have to connect to 5 V to use these values?

@billy27607
Copy link

billy27607 commented Mar 18, 2023 via email

@billy27607
Copy link

billy27607 commented Mar 19, 2023 via email

@billy27607
Copy link

billy27607 commented Mar 19, 2023 via email

@Jedimaster128
Copy link

Jedimaster128 commented Jul 28, 2023

Testv2.zip
I've converted the code above into a working Arduino-file. Please see attached. I have no clue if the reported ppm levels are correct. The VOC-sensor (GM502b) however for me gives a voltage output which is below interpolation range of the script (1.7V). So that's for sure a bit strange... Any clue?

GM102B: 133 = 0.43V
-> NO2: 1.02 ppm

GM302B: 413 = 1.33V
-> C2H5OH: 0.00 ppm

GM502B: 498 = 1.61V
VOC: 0.00 ppm

GM702B: 223 = 0.72V
CO: 5.24 ppm

@MatthewJeffson MatthewJeffson added UAY Unassigned yet Seeed_Arduino_MultiGas Label for Seeed_Arduino_MultiGas labels Oct 9, 2024
@Lesords Lesords self-assigned this Oct 14, 2024
@baorepo baorepo closed this as completed Oct 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Seeed_Arduino_MultiGas Label for Seeed_Arduino_MultiGas UAY Unassigned yet
Projects
Status: Done
Development

No branches or pull requests

10 participants