Seeed-Studio / Seeed_Arduino_MultiGas

This library could be used to detect four different gas concentrations and display them on the terminal.
MIT License
8 stars 5 forks source link

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

Open moemuses opened 2 years ago

moemuses commented 2 years ago

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 commented 2 years ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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

atsclct commented 1 year ago

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 commented 1 year ago

Thank you very much...

Billy

On Mar 18, 2023, at 6:31 PM, Chuntao Liu @.***> wrote:

brmmm3, just saw your post here. This helped me to update the python code to convert voltage to Pam 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?

— Reply to this email directly, view it on GitHub https://github.com/Seeed-Studio/Seeed_Arduino_MultiGas/issues/5#issuecomment-1475016952, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFFO5NESUOVVA6WDPKGOIDW4YZUDANCNFSM5PPAYDKA. You are receiving this because you commented.

billy27607 commented 1 year ago

As far as the voltages go, I have not gotten that far into my experimentation. I am currently powering the device with 3.3 volts as that is what is provided by the grove connector on who terminal I am using for playing with it. The drivers they provide for this, and from my experience most everything, are awful. I generally end up writing new drivers for their stuff to get it to be useful.

I will let you know if I get to something useable.

Billy

On Mar 18, 2023, at 6:31 PM, Chuntao Liu @.***> wrote:

brmmm3, just saw your post here. This helped me to update the python code to convert voltage to Pam 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?

— Reply to this email directly, view it on GitHub https://github.com/Seeed-Studio/Seeed_Arduino_MultiGas/issues/5#issuecomment-1475016952, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFFO5NESUOVVA6WDPKGOIDW4YZUDANCNFSM5PPAYDKA. You are receiving this because you commented.

billy27607 commented 1 year ago

So I looked at the board schematics. It seems to me that regardless of that you provide the board for VCC it runs on 3.3v. So providing 5v to the module should not change what you get back in terms of readings. I am not sure what that really means for translating voltage to PPM by their charts.

Billy

On Mar 19, 2023, at 10:37 AM, Billy Willis @.***> wrote:

As far as the voltages go, I have not gotten that far into my experimentation. I am currently powering the device with 3.3 volts as that is what is provided by the grove connector on who terminal I am using for playing with it. The drivers they provide for this, and from my experience most everything, are awful. I generally end up writing new drivers for their stuff to get it to be useful.

I will let you know if I get to something useable.

Billy

On Mar 18, 2023, at 6:31 PM, Chuntao Liu @.***> wrote:

brmmm3, just saw your post here. This helped me to update the python code to convert voltage to Pam 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?

— Reply to this email directly, view it on GitHub https://github.com/Seeed-Studio/Seeed_Arduino_MultiGas/issues/5#issuecomment-1475016952, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFFO5NESUOVVA6WDPKGOIDW4YZUDANCNFSM5PPAYDKA. You are receiving this because you commented.

Jedimaster128 commented 1 year ago

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