Air Quality Sensor


  • Hero Member

    Looks strange, could you supply your code?
    Btw the sensor draws a lot of current, the power supply should be able to deliver 1A. Did you burn in the sensor for 12-24 hours?



  • I have used the code from epiere. Baking I have not done. I use different power supplies (USB, Samsung and a large controllable power supply). I sometimes feel that contacts do not function properly. But the sensor draws but only about 100 mA = 0.1 A? Then it seems to be on branding. I have three new sensors. Mostly we read that it is only an attempt here is to measure the CO2 content. When I check the values with a measuring instrument with which agree the details here https://olimex.wordpress.com/2015/05/26/experimenting-with-gas-sensors-and-arduino/ Branding should work outdoors? Can you show me a chart or values?



  • This post is deleted!

  • Hero Member

    I used this library:
    https://github.com/GeorgK/MQ135

    Together with this description:
    https://hackaday.io/project/3475-sniffing-trinket/log/12363-mq135-arduino-library

    Simple to use and very good explanations.



  • I've read about. It sounds interesting.

    I stand a little hard with the development. Is there a skit for MQ135.h and MYSENSORS?

    I will test this afternoon.


  • Hero Member

    Hi @paqor,
    I wrote a little sketch which you could use as a start, it uses the MQ135 library.
    I also used a timer library, but this could be replaced by a simple sleep, too.

    RZERO should be changed when calibrating the sensor, this can be done at compile-time or during runtime by sending the value to the sensor via the gateway.

    Unfortunately I could not test the sketch yet, but it should help you getting started.
    If you have a second sensor which provides humidity and temperature then you can also get the corrected ppm from the library,...

    good luck!

    /*
    MQ135 MySensor
    A0      white
    D0      black
    GND     brown
    5V      red
    */
    
    #include <SPI.h>
    #include <MySensor.h>
    #include <MQ135.h>
    #include "Timer.h"
    
    Timer timer;
    
    // 30 seconds
    #define TEMP_UPDATE_INTERVAL 30000
    
    // MQ135
    #define CHILD_ID_CO2 0
    #define CHILD_ID_R0 1
    
    #define CO2_SENSOR_ANALOG_PIN 0
    
    /// Calibration resistance at atmospheric CO2 level
    #define RZERO 300.0
    #define EEPROM_R0 0
    
    MQ135 gasSensor = MQ135(CO2_SENSOR_ANALOG_PIN, RZERO);
    int lastC02;
    float lastR0;
    
    //-----------------------------------------------------------------------------
    // MySensor
    MySensor gw;
    MyMessage msgCO2(CHILD_ID_CO2, V_VAR1);
    MyMessage msgR0(CHILD_ID_R0, V_VAR1);
    
    //-----------------------------------------------------------------------------
    void setup()
    {
        gw.begin(incomingMessage, AUTO, true);
        gw.sendSketchInfo("MQ135 Sensor", "1.0");
        gw.present(CHILD_ID_CO2, S_AIR_QUALITY);
        gw.present(CHILD_ID_R0, S_CUSTOM);
        
        uint8_t R02 = gw.loadState(EEPROM_R0);
    
        // get R0 from EEPROM
        float R0 = R02 * 2;
    
    	// do a plausibility check
        if (R0 > 1.0 && R0 < 400.0) 
        {
            Serial.print(F("Setting R0 from EEPROM: "));
        }
        else
        {
            Serial.print(F("Setting default R0: "));
            R0 = RZERO;
        }
    
        Serial.print(R0);
        Serial.println(F(""));
    
        gasSensor.setR0(R0);
        
        timer.every(TEMP_UPDATE_INTERVAL, timerHandler);
    }
    
    
    bool MQ135Changed()
    {
        bool changed = false;
    
        lastR0 = gasSensor.getRZero();
        Serial.print(F("R0: "));
        Serial.println(lastR0);
    
        {
            float ppm = gasSensor.getPPM();
    
            Serial.print(F("CO2 ppm: "));
            Serial.print(ppm);
    
    
            int roundedPpm = (int)ppm;
            Serial.print(F(" --> "));
            Serial.println(roundedPpm);
    
            if (roundedPpm != lastC02)
            {
                lastC02 = roundedPpm;
                changed = true;
            }
        }
    
        return changed;
    }
    
    
    void timerHandler()
    {
        bool airQualityChanged = MQ135Changed();
        if (airQualityChanged)
        {
            gw.send(msgCO2.set(lastC02));
            gw.send(msgR0.set(lastR0, 2));
        }
    }
    
    
    void loop()
    {
        gw.process();
        timer.update();
    }
    
    
    // Gets the R0 value from the gw passes ot to the lib and stores it into the EEPROM.
    void incomingMessage(const MyMessage& message)
    {
        Serial.println(F("Incoming Message:"));
    
        if (message.isAck())
        {
            Serial.println(F("This is an ack from gateway"));
        }
    
        uint8_t sensor = message.sensor;
        if (sensor == CHILD_ID_R0)
        {
            float R0 = message.getFloat();
    
            Serial.print(F("Incoming R0: "));
            Serial.print(R0);
            Serial.println(F(""));
    
            gw.saveState(EEPROM_R0, (uint8_t)(R0/2));
            gasSensor.setR0(R0);
            gw.send(msgR0.set(R0, 2));
        }
    }
    
    


  • I have compiled from various sources themselves something. The result is https://gleisnetze.de/2015/12/25/das-erste-kleine-programm/ described herein. The most plausible values I received with the MQ135.h library. For that I have then added the transfer after MySensors / FHEM.

    Bildschirmfoto vom 2015-12-25 07-53-25.png



  • Hello

    In MiCS 2614 or in any gas sensor. what should be the value of R0... Because in datasheet they mentioned that value is measured in ambient condition. Any default value for that??


  • Hero Member

    @bhavika said:

    Hello

    In MiCS 2614 or in any gas sensor. what should be the value of R0... Because in datasheet they mentioned that value is measured in ambient condition. Any default value for that??

    The datasheed propose the "clean air calibration": "Sensing resistance in air R0 is measured under controlled ambient conditions, i.e. synthetic air at 23 ±5°C and 50 ± 10% RH. Sampling test."
    so you should go outside and mesure the defautl R0 value in your environment.


  • Hero Member

    MQ-135 with 10k results in a R0 of around 360 in my environment. So you could start experimenting with values around 300-500. I had to use a voltage regulator to get reliable values from the sensor.


  • Hero Member

    a little update on the MICS-6814:
    NO2: sensible
    CO: flat
    NH3 flat
    chartNO2.jpeg

    For the HCHO:
    charthcho.jpeg

    sorry for the flat part, some arduino issues at this time


  • Mod

    Welcome @betthorn, great to have you here.


  • Hero Member

    I have had some questions about converting from mg/m3 to ppm gases, here are the weight values and a proposed method:
    NH3 17.03 g/mol
    CO2 44.01
    CO 28.01
    H2S 34.08
    NO2 46.01
    NO 30.01
    O3 48.00
    C6H6 78.11
    C7H8 92.14

    you have:
    NO2 50 μg/m3 gives 26,5868821 ppm
    O3 27 μg/m3 = 0.027 mg/m3 -> (8,31441298,15)/(48101,325)*27=13,7617025 ppm

    correct me if I'm wrong


  • Hero Member

    2016 is where IoT for gas sensors are moving to more maturity, Cooking Hacks / Libellium proposed so far Fibaro sensors, but are now proposing more pricey one that are calibrated.

    See https://www.cooking-hacks.com/shop/sensors/gas


  • Hero Member

    CO2 with MH-Z14
    0_1455112054292_chart(7).png
    NO2 MICS-6814
    0_1455112065552_chart(8).png
    HCHO sensor
    0_1455112071878_chart(9).png



  • In MQ135 sensor's datasheet , which line on the curve is used to find out the concentration of benzene?


  • Hero Member

    @bhavika said:

    In MQ135 sensor's datasheet , which line on the curve is used to find out the concentration of benzene?

    MQ135 is sensitive to particle size, benzene is one of these. I rememer someone who used the chinese datasheet and found a chinese to translate it.



  • @epierre I am testing a stripped-down version of your code from your AirQuality-MQ135.ino. I stripped it to make it run just stand-alone.
    What I don't understand is how to do the calibration. When I run it in outside environment (with 10k resistor), I get these values:

    valr 223
    Vrl / Rs / ratio:13387 / 20875.00 / 0.00
    

    What would be the next step for calibration?


  • Hero Member

    @supersjimmie replace MQ135_DEFAULTRO by your value 20875 in the sketch



  • @epierre Thanks, I thought it would be something like that, but that didn't work.
    I then got

    valr 242
    Vrl / Rs / ratio:5464 / 8520.00 / 0.00
    

    Nothing like around 400ppm.


  • Hero Member

    yes for the code must be updated, the value is the CO2 (or something else) detected above the 399 in atmosphere

      gw.send(msg.set(MQ135_DEFAULTPPM+(int)ceil(valAIQ)));


  • Thanks, but when calibrating something strange happens when I put the module outside:

    valr 270
    Vrl   / Rs       / ratio  / ppm
    61355 / 95673.00 / 0.00 / 399.00
    valr 265
    Vrl   / Rs       / ratio  / ppm
    62928 / 98126.00 / 0.00 / 399.00
    valr 260
    Vrl   / Rs       / ratio  / ppm
    64561 / 100673.00 / 0.00 / 399.00
    valr 256
    Vrl   / Rs       / ratio  / ppm
    378 / 589.00 / 0.00 / 399.00
    valr 252
    Vrl   / Rs       / ratio  / ppm
    1773 / 2764.00 / 0.00 / 399.00
    

    As you can see, the valr decreases slowly, but suddenly Vlr and Rs jump over.
    Vlr is the value of 'val', which is: uint16_t val = ((float)22000*(1023-valr)/valr);
    uint16_t is 16bit, so max 65535. It looks like my environment needs more than that?
    (in fact, your code defines the defaultro as 68550, which is also too much for a uint16?)


  • Hero Member

    @supersjimmie said:

    Vlr is the value of 'val', which is: uint16_t val = ((float)22000*(1023-valr)/valr);

    yes and this is why I did rewrite it to https://github.com/empierre/arduino/blob/master/AirQuality-Multiple_Gas_Sensor1_4.ino but did not updated this one...



  • @epierre I striped-down that AirQuality-Multiple_Gas_Sensor1_4.ino to use only MQ135.
    When used inside, the analogRead is 473, so a reasonble value.
    But the MQCalibration function returns 0. Which means the MQResistanceCalculation also returns 0.

    MQResistenceCalculation only does one calc:
    return (long)((long)(1024 * 1000 * (long)rl_value)/raw_adc-(long)rl_value);
    rl_value = float RL4 = 0.990
    raw_adc = 473 (measured as said above)
    (1024 * 1000 * 0.990) / 473 - 0.990 = 2142.266
    I checked that raw_adc is filled with the correct value (to be sure it is not lost somewhere).

    What looks to fix this, I changed everything to float . So:
    return (float)((float)(1024 .* 1000. * (float)rl_value)/raw_adc-(float)rl_value);
    Which can be simplyfied as:
    return ((1024 * 1000*rl_value)/raw_adc-rl_value);
    I went to float, because the function is created as float MQResistenceCalculation.

    This now gives some ppm values that are within a reasonable range (about 2000 here).

    Now I have to figure out how to do calibration on this method again...


  • Hero Member

    looks like I have to rework something... but not in the todo list right now... Imperihome is now back on top, and Particle on lipo/solar also...



  • Hello,
    I don't know how to calculate 3 numbers of LPGCurve[]. Can you explain clearly for me? Thank you so much !!!

    0_1456336543192_Capture.PNG


  • Hero Member

    @tantt2810 as explained above, if the datasheet has a logarithmic scale, you can use the power regression to approximate the curve. One tool for example to do this calculus:http://www.xuru.org/rt/powr.asp

    for a sensor discussed above I read this on the datasheet:
    H2
    1.3 50
    0.8 100
    0.28 400
    0.16 1000
    0.05 4000

    The xuru website gives me this:

    y = 73.59123879 x-1.355617483
    Residual Sum of Squares: rss = 87393.44418

    and thus I have::
    H2Curve[3] = {73.5912, -1.355617};



  • @epierre Thank you so much !!!
    Have a nice day =)) !!!



  • @epierre

    hi

    i am new here.

    sorry i am a little confused by with these new formulate.

    is it the key for module?



  • Hello,
    I'm don't understand the recipe below. Why RL_Value(Load Resistance)(1023-raw_adc)/raw_adc)? Can you explain for me?
    Thank you so much !!!
    /
    ***************** MQResistanceCalculation ****************************************
    Input: raw_adc - raw value read from adc, which represents the voltage
    Output: the calculated sensor resistance
    Remarks: The sensor and the load resistor forms a voltage divider. Given the voltage
    across the load resistor and its resistance, the resistance of the sensor
    could be derived.
    ***********************************************************************************/
    float MQ2::MQResistanceCalculation(int raw_adc)
    {
    return ( ((float)RL_VALUE
    (1023-raw_adc)/raw_adc));
    }


  • Hero Member

    @jenbaker said:

    sorry i am a little confused by with these new formulate.

    is it the key for module?

    if you have a module the datasheet gives you the values, yes. Remember this is their """""generic"""" calibration, in fact they all copy the other so don't expect your MQ** would follow this by the letter...


  • Hero Member

    @tantt2810 said:

    Hello,
    I'm don't understand the recipe below. Why RL_Value(Load Resistance)(1023-raw_adc)/raw_adc)? Can you explain for me?
    Thank you so much !!!
    Input: raw_adc - raw value read from adc, which represents the voltage
    Output: the calculated sensor resistance
    Remarks: The sensor and the load resistor forms a voltage divider. Given the voltage
    across the load resistor and its resistance, the resistance of the sensor
    could be derived.
    float MQ2::MQResistanceCalculation(int raw_adc)
    RL_VALUE
    (1023-raw_adc)/raw_adc));

    in fact it is above described, you have a voltage, you want a resistance.
    https://learn.sparkfun.com/tutorials/voltage-dividers

    datasheet needs a value which is the Rs/Ro (called here RL) where
    Ro: sensor resistance at 100ppm of NH3 in the clean air.
    Rs:sensor resistance at various concentrations of gases

    here the formula is a simplification of this:

    float Vrl = val * ( 5.00 / 1024.0 ); // V
    float Rs = 20000 * ( 5.00 - Vrl) / Vrl ; // Ohm
    int ratio = Rs/Ro;
    ppm = 37143 * pow (ratio, -3.178);



  • @epierre Hi you,
    I think the Rs/Ro is not RL. In the datasheet RL=5kOhm. As I know it is a resistance on board Arduino, and we set it 5kOhm. Is it right? I also don't know why we set it equal 5kOhm. =))
    And why 20000 is in "float Rs = 20000 * ( 5.00 - Vrl) / Vrl ; // Ohm" ?
    Please help me clearly. Thank you so much !!!



  • Hello,
    I have read the sensitivity characteristics curve in datasheet of MQ135 and I don't know AL R, 3/4, 1/4* and +- mean? Can you help me, please !!!
    NH4 represent for NH3, doesn't it ?
    Thank you so much !!!



  • Hi everyone,
    I have an CO2 MG811 sensor. I don't know where tcm pin connect with ?
    Please help me. Thank you so much !!!!
    0_1457101257425_F08406-CO2-Carbon-Dioxide-Sensor-Module-font-b-MG811-b-font-Voltage-0-2V-Voltage-Output.jpg



  • Hello, I'm working with an MQ-136 sensor, and am interested in measuring H2S and/or SO2 concentrations. I've got a simple sketch written which gives me a fairly consistent value on the serial monitor, but I've tried to run the multiple gas sensor sketch both complete and I also tried picking apart the sketch just to include MQ-136 related variables and algorithms, but I can't get it to work. It's showing too many errors to list here. Does anyone have simple conversions from the serial outputs to usable values for various gases that this sensor can measure?



  • hello, how i read co2 ppm with my mq135?
    helpme please


  • Hero Member

    @tantt2810 said:

    Hi everyone,
    I have an CO2 MG811 sensor. I don't know where tcm pin connect with ?
    Please help me. Thank you so much !!!!

    this is an analogic sensor, so VCC/Gnd to power it, and readings on analogic Aout


  • Hero Member

    @tantt2810 sorry for not being more responsive, I left MQ/TGS behind me but I've not kept all my notes on mesuring this and that, that would require me to search for it again. But I appreciate if you can spot me some float/type error conversion in sketches !


  • Hero Member

    I was a bit disappointed when a project I contributed to on Kickstarter showed the inside of their boxes, I wonder how they will manage to satisfy customers with calibration... or their home made index as netatmo did
    0_1457255421408_sensly.png



  • Used the air quality script for MQ7 but throwed out all the conversions and calibrations. Just reading the plain input from the analogue pin.
    I targeted this as a smoke detector but it seems this sensor highly depends on the humidity of the surrounding air. Are there any existing formulas to correct for the humidity of the surounding air?

    And on one sensor i have some irratical data as shown below (blue). I wonder if it's a power supply (black and green is DHT) or a broken sensor?
    alt text


  • Hero Member

    @moskovskiy82 well looks like a strange sensor... try averaging several samples/// always remember to have it running several days before measuring for there are chemical left from china factories...



  • Hey all,

    I've got a question regarding the calibration function in https://github.com/empierre/arduino/blob/master/AirQuality-Multiple_Gas_Sensor1_4.ino :

    $ grep MQCalibr *
    AirQuality-Multiple_Gas_Sensor1_4.ino:  Ro0 = MQCalibration(MQ2_SENSOR,10,RL0,SmokeCurve);
    AirQuality-Multiple_Gas_Sensor1_4.ino:  Ro1 = MQCalibration(MQ6_SENSOR,10,RL1,LPGCurve);
    AirQuality-Multiple_Gas_Sensor1_4.ino:  Ro2 = MQCalibration(MQ131_SENSOR,10,RL2,O3Curve);
    AirQuality-Multiple_Gas_Sensor1_4.ino:  Ro3 = MQCalibration(TGS2600_SENSOR,10,RL3,C2H5OH_terCurve);
    AirQuality-Multiple_Gas_Sensor1_4.ino:  Ro4 = MQCalibration(MQ135_SENSOR,10,RL4,CO_secCurve);
    AirQuality-Multiple_Gas_Sensor1_4.ino:  Ro6 = MQCalibration(TGS2602_SENSOR,1,RL6,C7H8Curve);
    ....
    AirQuality-Multiple_Gas_Sensor1_4.ino:float MQCalibration(int mq_pin, double ppm, double rl_value,float *pcurve )
    

    Th function MQCalibration should calibrate the sensor in clean air, the calibration function uses the PPM concentration of the gas in a clean air environment.
    You already use the appropriate value for i.e. CO2 (around 399 PPM) in the MQ135 sketch - but why are you using those obscure values in the calibration (1, 10) here?

    Thanks in advance!


  • Hero Member

    @wreiner said:

    Th function MQCalibration should calibrate the sensor in clean air, the calibration function uses the PPM concentration of the gas in a clean air environment.
    You already use the appropriate value for i.e. CO2 (around 399 PPM) in the MQ135 sketch - but why are you using those obscure values in the calibration (1, 10) here?

    Well, I didn't know the concentration in clean air so I had to put in a value even random... I took the hypothesis there are very few in the air so low value.



  • @epierre
    Thank you. Your mean is Vcc->5V, GND->GND, Dout->Digital pin, Aout->Analog pin. Is it right?
    But I don't know the tcm pin connect with ? I have read datasheet but I don't find any info about tcm pin. Please help me. Thank you so much !!!



  • @epierre

    Readings of my MQ135 are going up and down like a yo-yo. Not sure how to read them really?
    What do you think?

    0_1457656109114_Screenshot (18).png



  • I am confused with MiCS 4514 sensor. What I had done is :

    using MiCS quick start evaluation board, measured ADC value
    calculated Vout and from that calculated R0.
    Rs/R0 concentration gives me high value, which is not possible, My data is as follows
    clean air file is used for the purpose of R0 value
    https://drive.google.com/file/d/0B8sF8a6FHoseWjhWOHRoYzIxakk/view?usp=sharing
    and Polluted file is used to calculate the actual pollution
    https://drive.google.com/file/d/0B8sF8a6FHoseR1BnclhCUHdibEU/view?usp=sharing

    I am confused. Please help me


  • Hero Member

    @tantt2810 said:

    @epierre
    Thank you. Your mean is Vcc->5V, GND->GND, Dout->Digital pin, Aout->Analog pin. Is it right?
    But I don't know the tcm pin connect with ? I have read datasheet but I don't find any info about tcm pin. Please help me. Thank you so much !!!

    do not use the digital pin !!! it only reacts with the potentiometer in on/off mode, so useless... you need to power the device with Vcc/Gnd, and read the output with Aout (A stands for analog). The last one forget it.


  • Hero Member

    @alexsh1 is that raw ADC output or with a calculation ? normaly this should go up or down but not in sucha drastic way



  • @epierre

    This is a value gw.send(msg.set(MQ135_DEFAULTPPM+(int)ceil(valAIQ))) reported to Domoticz.
    It does not make any sense to me at all.



  • @epierre
    Thanks you so much !!! Good job, Good boy =)).


  • Hero Member

    @alexsh1 said:

    @epierre

    This is a value gw.send(msg.set(MQ135_DEFAULTPPM+(int)ceil(valAIQ))) reported to Domoticz.
    It does not make any sense to me at all.

    same for mle, sometime it is good to have two piece to compare their behavior... I have 3 o them ...



  • @epierre do you think it is a faulty sensor though?



  • Hello,

    Can I use pinMode(digitalpin, OUTPUT) to turn off sensor? Or Is there any way to wirte a function that turn off sensor?

    I want to remote it on web to turn on or off it.

    Thank you so much !!!



  • For those who are trying to use an MQ135 module from e-bay or other sellers, on a small pcb with a chip, LED and potmeter:
    It took me a while until I figured out that the little chip has a very big influence on the value that you read on the A-out pin.
    Only after removing the connection between the A-out pin and the little chip, I got normal measurements.

    I also replaced the small resistor marked "102" with a 10k resistor, to make measurements more compatible with code that uses a 10k pull down resistor. But that was already a known issue, found on many other places.


  • Hero Member

    @tantt2810 said:

    Hello,

    Can I use pinMode(digitalpin, OUTPUT) to turn off sensor? Or Is there any way to wirte a function that turn off sensor?

    I want to remote it on web to turn on or off it.

    Thank you so much !!!

    well no, powering is through Vcc/GND, so you whould use a switch on this. Digital pins do not gives enough power for such sensors.


  • Hero Member

    @alexsh1 said:

    @epierre do you think it is a faulty sensor though?

    coud be, hard to know without possibility to compare to another one.



  • @epierre

    This is what I discovered. When nobody is around the sensor 'settles' on a particular value. However, when there is anyone around in the room, the sensor reading goes up and down - see the snapshot attached. Any idea what the problem may be?

    0_1459957501388_Untitled.jpg


  • Hero Member

    @alexsh1 hard to say, could be something moving inside if someone makes vibration around and thus this strange reaction... the down curve at the end is correct, the in between not



  • Hi,
    Can you check the reception level?
    I had same behaviour with an old temperature/humidity module; need to move it to another place in the room, and works perfectly now 🙂


  • Hero Member

    @Elfnoir said:

    ; need to move it to another place in the room, and works perfectly now

    temp/hum are "less" sensitive on environment, here the sensors heats up to 40-60° (or more) so temp/hum is not the most impacting (I can be wrong anyway 😉



  • @epierre I wonder if you looked at this sensor Figaro TGS8100:

    http://www.figarosensor.com/products/entry/tgs8100.html

    Looks like it is perfect for a low-powered platform.


  • Hero Member

    @alexsh1 said:

    @epierre I wonder if you looked at this sensor Figaro TGS8100:

    http://www.figarosensor.com/products/entry/tgs8100.html

    Looks like it is perfect for a low-powered platform.

    as a replacement for MQ in low power yes could be... the bad part is how to get one ?



  • @epierre said:
    the bad part is how to get one ?

    How about http://www.soselectronic.com/?str=371&artnum=170939&name=figaro-tgs8100 ?



  • Hi, It all make sense, I am just confused that in your code once your multiply and once you divide...

    case CO:
    {
    if(ratio1 < 0.01) ratio1 = 0.01;
    if(ratio1 > 3) ratio1 = 3;
    //c = pow(10, 0.6) / pow(ratio1, 1.2);
    ** c = pow(ratio1, -1.179)4.385; //mod by jack*
    break;
    }
    case NO2:
    {
    if(ratio2 < 0.07) ratio2 = 0.07;
    if(ratio2 > 40) ratio2 = 40;
    //c = ratio2 / pow(10, 0.8);
    ** c = pow(ratio2, 1.007)/6.855; //mod by jack**
    break;
    }
    SO, what is the actual formula?

    Vir

    @epierre said:

    I'm now testing the MICS-6814 (3 sensors in one) given for :
    Carbon monoxide CO 1 -1000ppm
    **Nitrogen dioxide NO2 0.05 –10ppm **
    Ethanol C2H5OH 10 –500ppm
    Hydrogen H2 1 –1000ppm
    Ammonia NH3 1 –500ppm
    Methane CH4 >1000ppm
    Propane C3H8 >1000ppm
    Iso-butane C4H10 >1000ppm

    Datasheet maionly speaks on CO, NO2 and NH3:
    http://www.seeedstudio.com/wiki/images/1/10/MiCS-6814_Datasheet.pdf

    Here is are scripts:
    http://www.seeedstudio.com/wiki/Grove_-_Multichannel_Gas_Sensor

    http://www.seeedstudio.com/wiki/images/1/10/MiCS-6814_Datasheet.pdf

    Some readings:

    The concentration of NH3 is 0.99 ppm
    The concentration of CO is 1.20 ppm
    The concentration of NO2 is 0.15 ppm
    The concentration of C3H8 is 1000.04 ppm
    The concentration of C4H10 is 999.98 ppm
    The concentration of CH4 is 2991.14 ppm
    The concentration of H2 is 1.09 ppm
    The concentration of C2H5OH is 1.40 ppm
    

    I guess I'll make a script soon...



  • What does 37143 represents, Rs? and -3.178 is a second value from your regression method, but what about the first value?
    Can you elaborate on ppm line, please
    Vir

    here the formula is a simplification of this:

    float Vrl = val * ( 5.00 / 1024.0 ); // V
    float Rs = 20000 * ( 5.00 - Vrl) / Vrl ; // Ohm
    int ratio = Rs/Ro;
    ppm = 37143 * pow (ratio, -3.178);



  • @bhavika said:

    I am confused with MiCS 4514 sensor. What I had done is :

    using MiCS quick start evaluation board, measured ADC value
    calculated Vout and from that calculated R0.
    Rs/R0 concentration gives me high value, which is not possible, My data is as follows
    clean air file is used for the purpose of R0 value
    https://drive.google.com/file/d/0B8sF8a6FHoseWjhWOHRoYzIxakk/view?usp=sharing
    and Polluted file is used to calculate the actual pollution
    https://drive.google.com/file/d/0B8sF8a6FHoseR1BnclhCUHdibEU/view?usp=sharing

    I am confused. Please help me

    I am confused by the formula you are using, could you please explain?
    Pollution NO2 = (POWER((RS/R0)*(1/POWER(10,0.8068)),(1/1.01)))

    Pollution CO = POWER((RS/R0)*(1/POWER(10 , 0.5476)),(1/-0.8497)))

    is the actual formula like this?:

    pollution = POWER((ratio *(1/POWER(10, curve[0])),(1/[curve[1])))

    This is different than any other formulas discussed in this thread...
    Vir



  • so does 37164 represents the scaling factor and -3.178 the exponent?
    Vir
    @Vir said:

    What does 37143 represents, Rs? and -3.178 is a second value from your regression method, but what about the first value?
    Can you elaborate on ppm line, please
    Vir

    here the formula is a simplification of this:

    float Vrl = val * ( 5.00 / 1024.0 ); // V
    float Rs = 20000 * ( 5.00 - Vrl) / Vrl ; // Ohm
    int ratio = Rs/Ro;
    ppm = 37143 * pow (ratio, -3.178);



  • @epierre I have the problem with the dust sketch (shinyei ppd42ns). The following code does not work in MySensors 1.5

    ...
    gw.send(msgPM10.set("ppm"));
      ...
    gw.send(msgPM25.set("ppm")); 
    ...
    
    2016-04-24 14:44:19.438 Error: MySensors: Unknown/Invalid sensor type (43)
    2016-04-24 14:44:19.438 Error: MySensors: Unknown/Invalid sensor type (43)
    

    So far both PM10 and PM25 are showing zero. However, if I upload the following code, it works fine (one channel though):

    // Watch video here: https://www.youtube.com/watch?v=a8r4CeQopfY
    
    /* 
     Connection:
     
     JST Pin 1 (Black Wire)  => Arduino GND
     JST Pin 3 (Red wire)    => Arduino 5VDC
     JST Pin 4 (Yellow wire) => Arduino Digital Pin 8
    
     Green Led connected to Arduino D7
     Yellow Led connected to Arduino D6
     Red Led connected to Arduino D5
     
     
    Dust Sensor possible application:
    - Applications of customer
    - Air quality sensor
    - Dustlessness workshop
    - Cigarette detector
    */
    
    /*
    Sensor is to create Digital (Lo Pulse) output to Particulate Matters (PM). Lo Pulse Occupancy time (LPO time) is in proportion
    to PM concentration. The output is for PM whose size is around 1 micro meter or larger. We can use the sensor to detect the dust in clean room.
    Minimum detect particle: 1um
    http://www.seeedstudio.com/wiki/Grove_-_Dust_Sensor
     Grove - Dust Sensor Demo v1.0
     Interface to Shinyei Model PPD42NS Particle Sensor
     Program by Christopher Nafis 
     Written April 2012
     
     http://www.seeedstudio.com/depot/grove-dust-sensor-p-1050.html
     http://www.sca-shinyei.com/pdf/PPD42NS.pdf
     */
    
    int pin = 8;
    unsigned long duration;
    unsigned long starttime;
    unsigned long sampletime_ms = 1000;//sampe 1s ;
    unsigned long lowpulseoccupancy = 0;
    float ratio = 0;
    float concentration = 0;
    int gLed = 7;
    int yLed = 6;
    int rLed = 5;
    
    void setup() {
      Serial.begin(9600);
      pinMode(8,INPUT);
      pinMode(gLed,OUTPUT);
      pinMode(yLed,OUTPUT);
      pinMode(rLed,OUTPUT);
      starttime = millis();//get the current time;
    }
    
    void loop() {
      duration = pulseIn(pin, LOW);
      lowpulseoccupancy = lowpulseoccupancy+duration;
    
      if ((millis()-starttime) > sampletime_ms)//if the sampel time == 30s
      {
        ratio = lowpulseoccupancy/(sampletime_ms*10.0);  // Integer percentage 0=>100
        concentration = 1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62; // using spec sheet curve
        Serial.print("concentration = ");
        Serial.print(concentration);
        Serial.print(" pcs/0.01cf  -  ");
        if (concentration < 1.0) {
         Serial.println("It's a smokeless and dustless environment"); 
         digitalWrite(gLed, HIGH);
         digitalWrite(yLed, LOW);
         digitalWrite(rLed, LOW);
      }
        if (concentration > 1.0 && concentration < 20000) {
         Serial.println("It's probably only you blowing air to the sensor :)"); 
         digitalWrite(gLed, HIGH);
         digitalWrite(yLed, LOW);
         digitalWrite(rLed, LOW);
        }
        
        if (concentration > 20000 && concentration < 315000) {
         Serial.println("Smokes from matches detected!"); 
         digitalWrite(gLed, LOW);
         digitalWrite(yLed, HIGH);
         digitalWrite(rLed, LOW);
        }
          if (concentration > 315000) {
         Serial.println("Smokes from cigarettes detected! Or It might be a huge fire! Beware!"); 
         digitalWrite(gLed, LOW);
         digitalWrite(yLed, LOW);
         digitalWrite(rLed, HIGH);
      }
        
        lowpulseoccupancy = 0;
        starttime = millis();
      }
    }
    
    


  • @epierre hi, could remove some doubts regarding the mq131 sensor?
    thanks,
    Anne


  • Hero Member

    @alexsh1 said:

    (shinyei ppd42ns)

    I corrected the following sketch which had bad yg/m3 to ppm conversion thanks to LouiS22 from Domoticz forum remark (type conversion errors leading to 0 values).

    https://github.com/empierre/arduino/blob/master/DustSensor_SamYoung_DSM501.ino

    I guess I'll have to do the same here (but I lack time...):
    DustSensor_Shinyei_PPD42NS.ino



  • @epierre Thanks - I'll have another go at Shinyei PPD42NS sketch once I have time. Ideally, I'd like to combine it with MH-Z14A sketch.


  • Hero Member

    @alexsh1 which is a good idea, also a temp/him is quite usefull though I didn't added the impact in the formulas so far.



  • @epierre I have combined the two sketches - works just fine, but need to find a spare temp sensor.The hardware setup looks messy so adding an extra i2c sensor will complicate it even more. I'll come back with the combined three-in-one sketch shortly.

    BTW, this

    gw.send(msgPM25.set("ppm"));
    

    still throws the following error. What version MySensors are you using?

     MySensors: Unknown/Invalid sensor type (43)
    


  • @epierre I started looking at the formula you have used in ppmv calculation. Why do you need ppmv? All values in EPA or Europe are in μg/m3.

    The ppmv equation is:

    ppmv = mg/m^3 * (0.08205*T)/M 
    

    T = atmospheric temperature in kelvins = 273.15 + °C
    M = molecular weight of the air pollutant = 28.97
    (http://www.engineeringtoolbox.com/molecular-mass-air-d_679.html - this is a good link)
    0.08205 = Universal Gas Law constant in atm·l/(mol·K)

    Your code for this equation is:

    ppmv=(float)(((concentrationPM25*0.0283168)/100) * ((0.08205*temp)/0.01))/1000;
    

    Now, I think it should be as follows:

    1. temp = °C + 273.15
    int temp=273.15 + 22;
    
    

    22C - is a typical temp inside though the intention is to use a sensor

    1. The amended ppmv equation is going to be:
    ppmv=(((concentrationPM25*0.0283168)/100) * ((0.08205*temp)/28.97))/1000;
    

    I have not changed 0.0283168 /100 - not sure that this is. And the whole thing is divided by 1000? why?

    IMPORTANT UPDATE:

    I have just received the following result:

    
    PM10: 7373
    
    send: 11-11-0-0 s=0,c=1,t=37,pt=7,l=5,sg=0,st=ok:1.744
    
    

    If I use the following web-site http://www.herramientasingenieria.com/onlinecalc/ppm-mg_m3.php and the result is
    mg/m3 = 7373 * 0.283168/100 = 2.08 mg/m3
    The molecular weight is 28.97 for dry air

    2.08 mg/m3 is equivalent to 1.74ppm for a gas with molecular weight=28.97 Pressure=1013.25, Temperature=22C 
    

    Success!
    Now I have found a spare BMP280 (temp and pressure sensor) which I can use here.
    Additionally, I need to use 24-h and 1 year averages - https://www3.epa.gov/airquality/particlepollution/2012/decfsstandards.pdf

    Stay tuned!


  • Hero Member

    @alexsh1 said:

    @epierre I started looking at the formula you have used in ppmv calculation. Why do you need ppmv? All values in EPA or Europe are in μg/m3.

    because domoticz only knows ppm... and many AIQ like use only that except for particles.

    1. temp = °C + 273.15
      int temp=273.15 + 22;
      22C - is a typical temp inside though the intention is to use a sensor

    not for me 😉

    1. The amended ppmv equation is going to be:
      ppmv=(((concentrationPM250.0283168)/100) * ((0.08205temp)/28.97))/1000;
    I have not changed 0.0283168 /100 - not sure that this is. And the whole thing is divided by 1000? why?
    

    was a volume conversion

    The molecular weight is 28.97 for dry air

    2.08 mg/m3 is equivalent to 1.74ppm for a gas with molecular weight=28.97 Pressure=1013.25, Temperature=22C 
    

    Success!

    success for the mysensors value ?

    MySensors: Unknown/Invalid sensor type (43)
    

    Domoticz doesn't recognize this command... I use 1.5.x but I have my own gateway to domoticz



  • @epierre Yes, all works fine now. I modified the combined sketch and here is the final result.

    0_1463152922051_Screenshot (21).jpg



  • Which one to use for detection of fire smoke? Currently have got several MQ-7 but they don't seem so good at it


  • Hero Member

    @moskovskiy82 said:

    Which one to use for detection of fire smoke? Currently have got several MQ-7 but they don't seem so good at it

    as discussed just above, a particle sensor could be good for smoke is a particle concentration, coupled with heat this would be a good indicator

    if (concentration > 315000) {
         Serial.println("Smokes from cigarettes detected! Or It might be a huge fire! Beware!"); 
    


  • What about mq2 or mq135? Any experience? As a gas sensor will be more suitable detecting early fire


  • Hero Member

    @moskovskiy82 said:

    What about mq2 or mq135? Any experience? As a gas sensor will be more suitable detecting early fire

    please see what it does, this is not a gas sensor this only detect particle size whatever the gas...



  • @epierre
    It still detects concentration. Both state CO detection. So in case of fire won't they detect the increase in concentration much faster that the particle sensor like Sharp’s GP2Y1010AU0F or alternative?



  • @moskovskiy82 For a fire smoke, you can use pretty much any gas or particle sensor - there are a quite few gases formed during the burning process. MQ2 is highly sensitivity and has a fast response time. I can recommend it for a fire detection usage. However, I have been disappointed in MQ* sensors in general - there are not accurate, require 24h heat-up time, consume a lot of power etc. The only advantage is the price.

    To detect fire to can use a flame sensor - http://www.instructables.com/id/Flame-detection-using-Arduino-and-flame-sensor/



  • Can someone tell me how to read this line?

    float mq135_ro = 10000.0;    // this has to be tuned 10K Ohm
    

    Do I have to messure the sensor and adjust the variable or do I have to tune the resistance? If I have to do the first thing, when do I have to messure it? In warm state and clean air with a multimeter?



  • @epierre Did you check out the airbeam, which is based on a more expensive sensor Shinyei PPD60PV?
    http://www.takingspace.org/airbeam-technical-specifications-operation-performance/

    They made a step-by-step manual about building the same on Shinyei PPD42NS. I took their code and stripped a few parts and this is what I am left with:

    #include  <SoftwareSerial.h>
    #include  <FlexiTimer2.h>
    
    int pin = 3;
    
    volatile  double  rawParticalCount;
    volatile  double  totalParticles  = 0;
    volatile  double  particleCountToDisplay  = 0;
    volatile  double  ratio = 0;
    volatile  uint16_t  timeCounter = 0;
    
    #define         numberOfPeaksRecording                            5
    volatile        uint32_t        previousPeaks[numberOfPeaksRecording];
    volatile        uint32_t        sumOfPreviousPeaks          = 0;
    volatile        uint32_t        instantGoal                         = 0;
    volatile        int32_t         delta                                     = 0;
    volatile        uint32_t        slowMovingAverage             = 0;
    volatile  boolean readyToSendData = false;
    
    void  setup() {
        Serial.begin(115200);
        pinMode(pin,INPUT);
        
        FlexiTimer2::set(1,1.0/10000,readPin);
        FlexiTimer2::start();
        
    }
    void  loop()  {
       
        
        if(readyToSendData){
                    Serial.print(rawParticalCount,  DEC);
                    Serial.print("  Raw Particle  Count (0-10000) ");
                    Serial.print(ratio, DEC);
                    Serial.print("  Ratio (0-100%)  ");
                    Serial.print(particleCountToDisplay,  DEC); 
                    Serial.print("  Particle  Count");
                    Serial.println("");
                                 
                    readyToSendData = false;
        }
        
    }
    
    void  readPin(){
        if(digitalRead(pin) ==  LOW){
            rawParticalCount++;
        }    
        timeCounter++;
        if  (timeCounter  ==  10000)
        {
            timeCounter=0;
               
            //Changes are made  here  based on  Chris Nafis's code: http://www.howmuchsnow.com/arduino/airquality/grovedust/
            ratio = rawParticalCount/100.0; 
            //Convert to  percentage, the shinyei reads 10milliseconds  to  90milliseconds  duration  for particles.  Basing on 10milliseconds, smallest  particle  assumingly  from  specification sheet.
            //FlexiTimer2,  reads 10,000  readings  per second, which would be  1 reading per 100 microseconds. 100 readings  would be  10  milliseconds. Since Shinyei runs  at minimal 10  millisecond range.  I divided 10,000  readings  by  100 to  get 100.
            //Good  example would be  rawPArticalCount  is  5000  half  of  the 10,000  readings were  active. 5000/100  would be  50  which translate to  50% low pulse occupancy.
            totalParticles  = (1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62);
            rawParticalCount  = 0;
              
                    //  shift counters  over, code  adapted from  template  provided  by  Mike  Taylor  and Joshua  Schapiro  from  Carnegie  Mellon  University's  CREATE  Lab
                    for (uint8_t  i = 0;  i < (numberOfPeaksRecording-1); i++)  {
                            previousPeaks[i]  = previousPeaks[i+1];
                    }
                    previousPeaks[numberOfPeaksRecording  - 1]  = totalParticles;
                    sumOfPreviousPeaks  = 0;
                    for (uint8_t  i = 0;  i < numberOfPeaksRecording; i++)  {
                            sumOfPreviousPeaks  +=  previousPeaks[i];
                    }
                    instantGoal = 2*sumOfPreviousPeaks;
                    
                    delta = instantGoal - slowMovingAverage;
                    
                    if  (delta  < -5000){
                            slowMovingAverage = slowMovingAverage - 250;
                    } else  if  (delta  < -2500){
                            slowMovingAverage = slowMovingAverage - 120;
                    } else  if(delta  < -1200){
                            slowMovingAverage = slowMovingAverage - 60;
                    } else  if(delta  < -500){
                            slowMovingAverage = slowMovingAverage - 25;
                    } else  if(delta  < -5){
                            slowMovingAverage = slowMovingAverage - 5;
                    } else  if(delta  < -1){
                            slowMovingAverage = slowMovingAverage - 1;
                    } else  if(delta  > 5000) {
                            slowMovingAverage = slowMovingAverage + 500;
                    } else  if(delta  > 2500){
                            slowMovingAverage = slowMovingAverage + 250;
                    } else  if(delta  > 1200){
                            slowMovingAverage = slowMovingAverage + 120;
                    } else  if(delta  > 500){
                            slowMovingAverage = slowMovingAverage + 50;
                    } else  if(delta  > 5){
                            slowMovingAverage = slowMovingAverage + 5;
                    } else  if(delta  > 1){
                            slowMovingAverage = slowMovingAverage + 1;
                    }
                    
                    particleCountToDisplay  = slowMovingAverage;
            readyToSendData = true;
        }
    }
    

    I have not adopted it for MySensors yet.
    I like moving average they use, but the values do not make sense to me:

    0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  53470.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 27.0000000000  Ratio (0-100%)  53970.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  54470.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 21.6200008392  Ratio (0-100%)  54970.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  55470.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 24.2800006866  Ratio (0-100%)  55970.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  56470.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 24.1200008392  Ratio (0-100%)  56970.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  57470.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 22.3199996948  Ratio (0-100%)  57970.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  58470.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 20.3600006103  Ratio (0-100%)  58970.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  59470.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 17.6299991607  Ratio (0-100%)  59970.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  60220.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 19.0499992370  Ratio (0-100%)  60720.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 0.0000000000  Ratio (0-100%)  60715.0000000000  Particle  Count
    0.0000000000  Raw Particle  Count (0-10000) 20.6599998474  Ratio (0-100%)  61215.0000000000  Particle  Count
    

  • Hero Member

    @alexsh1 said:

    I took their code and stripped a few parts and this is what I am left with:

    @alexsh1 the PPDN42 is for 1 micron and 2.5 micron , this one is for 0.5 micron

    airbeam has standard code: https://github.com/HabitatMap/AirCastingAndroidClient/blob/master/arduino/aircasting/aircasting_shinyeiPPD42NS.ino

    or I've not seen ?



  • @epierre this is not the air beam code. They introduced "do it yourself" consept similar to the airbeam but with a different sensor (shinyei ppd42ns)



  • Hey guys, I am struggeling to get the MQ135 to work. Is there a step by step howto? Or can someone answer my question from 3 days ago?
    I would appreciate it very much.



  • @rollercontainer I followed David Gironi method to calibrate - it is described as follows:

    Before you can use the sensor, it has to be calibrated. For this, connect the sensor to your circuit and leave it powered on for 12-24 h to burn it in. Then put it into outside air, preferably at 20°C/35% rel. hum. (humidity is not so crucial). Read out the calibration value as such

    float rzero = gasSensor.getRZero();
    Wait until the value has somewhat settled (30min-1h). Remember, this is an ADC measurement so you might not want to wait some time between reading the sensor and also do some averaging. Once you have determined your RZero, put it into the MQ135.h. Note: Different sensors will likely have different RZero!

    #define RZERO 76.63

    Congrats, you have calibrated the sensor and can now read the CO2 ppm value in your sketch

    float ppm = gasSensor.getPPM();



  • @alexsh1 Thx
    Is "RZERO" the same as "mq135_ro" or is it a seperate variable? (ro = R0 = RZERO?)



  • So, obvious these are two variables. Wherefore is the mq135_ro? Is it the load resistor from signal to ground?



  • @rollercontainer let's start from the basic - which MQ-135 do you have? A bare unit or with a logic controller and a variable resistor?



  • @alexsh1 I bought a breakout board and desoldered everything but the sensor and the 33Ohm resistor for the heating. Then I soldered a 10kOhm pulldown from analog signal out to ground.



  • @rollercontainer

    Apologies for the confusion. @epierre did not use this library, but I have used MQ135.h library, but have not adopted it for MySensors and I did not like the sensor's reading jumping up and down.

    In MQ135.h:

    // Calibration resistance at atmospheric CO2 level
    #define RZERO 394.5 //RZERO 76.63
    

    I think the principle of RZERO is the same as mq135_ro



  • @rollercontainer I have do not done any changes to my breakout board. Just tuning the variable resistor and that's it.

    One thing I noticed is that the sensor performs much better in enclosed premises (no windows and no doors). Alternatively, it has to be in the box or something. Any small light draft dramatically changing the readings.



  • @alexsh1 I dont think so, in epierres sketch both values appear:

    https://github.com/empierre/arduino/blob/master/AirQuality-MQ135.ino#L25
    https://github.com/empierre/arduino/blob/master/AirQuality-MQ135.ino#L33

    So, what the heck is mq135_ro? And what to tune to?



  • @epierre OK, I found a mistake in my Shinyei ppd42ns code. Basically, it produces pcs/0.01cf values (normally, anything from 2000 to 10000 per a 30 secs reading).

    This sensor use counting method to test dust concentration but not weight method, and the unit it pcs/L or pcs/0.01cf.
    

    This has to be converted into ug/m3 (the EPA standard)and then we can convert it into ppmv. I do not think dividing it by a volume factor is enough. Please see below updated code, which combines both ppd42ns and MH-Z14A sensors.

    /**
     * The MySensors Arduino library handles the wireless radio link and protocol
     * between your home built sensors/actuators and HA controller of choice.
     * The sensors forms a self healing radio network with optional repeaters. Each
     * repeater and gateway builds a routing tables in EEPROM which keeps track of the
     * network topology allowing messages to be routed to nodes.
     *
     * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
     * Copyright (C) 2013-2015 Sensnology AB
     * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
     *
     * Documentation: http://www.mysensors.org
     * Support Forum: http://forum.mysensors.org
     *
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License
     * version 2 as published by the Free Software Foundation.
     *
     *******************************
     * DESCRIPTION
     * 
     * Dust Sensor for Shinyei ppd42ns
     *  
     * 1 : COMMON(GND)
     * 2 : OUTPUT(P2)
     * 3 : INPUT(5VDC 90mA)
     * 4 : OUTPUT(P1)
     * 5 : INPUT(T1)・・・FOR THRESHOLD FOR [P2] - not used
     *
     * http://www.seeedstudio.com/wiki/images/4/4c/Grove_-_Dust_sensor.pdf
     * 
     *   connect the sensor as follows :
     *    Pin 4 of dust sensor PM1      -> Digital 6
     *    Pin 3 of dust sensor          -> +5V 
     *    Pin 2 of dust sensor PM25    -> Digital 3 
     *    Pin 1 of dust sensor          -> Ground
    * Contributor: epierre and alexsh1
    **/
      
    
    #include <MySensor.h>  
    #include <SPI.h>
    
    // Define a static node address, remove if you want auto address assignment
    #define NODE_ADDRESS_DUST   11
    #define NODE_ADDRESS_CO2    13
    
    #define CHILD_ID_DUST_PM10            0
    #define CHILD_ID_DUST_PM25            1
    #define CHILD_ID_DUST_PM10_MG_M3      2
    #define CHILD_ID_DUST_PM25_MG_M3      3
    
    #define DUST_SENSOR_DIGITAL_PIN_PM10  6
    #define DUST_SENSOR_DIGITAL_PIN_PM25  3
    
    #define CHILD_ID 0
    #define CO2_SENSOR_PWM_PIN 2
    
    unsigned long SLEEP_TIME = 30*1000; // Sleep time between reads (in milliseconds)
    //VARIABLES
    int val = 0;           // variable to store the value coming from the sensor
    float valDUSTPM25 =0.0;
    float lastDUSTPM25 =0.0;
    float valDUSTPM10 =0.0;
    float lastDUSTPM10 =0.0;
    unsigned long duration;
    unsigned long starttime;
    unsigned long endtime;
    unsigned long sampletime_ms = 30000;
    unsigned long lowpulseoccupancy = 0;
    float ratio = 0;
    long concentrationPM25 = 0;
    long concentrationPM10 = 0;
    float concentration = 0;
    float concentrationPM25_ugm3;
    float concentrationPM10_ugm3;
    
    int temp=273.5+22; //external temperature, if you can replace this with a DHT11 or better 
    float ppmvPM25;
    float ppmvPM10;
    
    float valAIQ =0.0;
    float lastAIQ =0.0;
    unsigned long duration_co2;
    long co2ppm;
    
    MySensor gw;
    MyMessage dustMsgPM10(CHILD_ID_DUST_PM10, V_LEVEL);
    MyMessage msgPM10(CHILD_ID_DUST_PM10_MG_M3, V_LEVEL);
    MyMessage dustMsgPM25(CHILD_ID_DUST_PM25, V_LEVEL);
    MyMessage msgPM25(CHILD_ID_DUST_PM25_MG_M3, V_LEVEL);
    
    MySensor gw1;
    MyMessage msg(CHILD_ID, V_LEVEL);
    
    void setup()  
    {
      gw.begin(NULL,NODE_ADDRESS_DUST,false);
    
      // Send the sketch version information to the gateway and Controller
      gw.sendSketchInfo("Dust Sensor PPD42NS", "1.5");
      
      // Register all sensors to gateway (they will be created as child devices)
      gw.present(CHILD_ID_DUST_PM10, S_DUST);
      //gw.send(msgPM10.set("ppm"));  
      gw.present(CHILD_ID_DUST_PM25, S_DUST);  
      //gw.send(msgPM25.set("ppm"));
      gw.present(CHILD_ID_DUST_PM10_MG_M3, S_DUST);
      gw.present(CHILD_ID_DUST_PM25_MG_M3, S_DUST);
      
      pinMode(DUST_SENSOR_DIGITAL_PIN_PM10,INPUT);
      pinMode(DUST_SENSOR_DIGITAL_PIN_PM25,INPUT);
    
      gw1.begin(NULL,NODE_ADDRESS_CO2,false);
      gw1.sendSketchInfo("AIQ Sensor CO2 MH-Z14A", "1.0");
      // Register all sensors to gateway (they will be created as child devices)
      gw1.present(CHILD_ID, S_AIR_QUALITY);  
      
      pinMode(CO2_SENSOR_PWM_PIN, INPUT);
     
      
    }
    
    void loop()      
    {    
    
      //get PM 2.5 density of particles over 2.5 μm.
      concentrationPM25=(long)getPM(DUST_SENSOR_DIGITAL_PIN_PM25);
      Serial.print("PM25: ");
      Serial.print(concentrationPM25);
      Serial.println(" pcs/0.01cf");
      concentrationPM25_ugm3 = conversion25(concentrationPM25);
      Serial.print("PM25: ");
      Serial.print(concentrationPM25_ugm3);
      Serial.println(" ug/m3");
      Serial.print("\n");   
      //ppmv=mg/m3 * (0.08205*Tmp)/Molecular_mass
      //0.08205   = Universal gas constant in atm·m3/(kmol·K)
      ppmvPM25=((concentrationPM25_ugm3) * ((0.08205*temp)/28.97));
     
      if ((concentrationPM25 != lastDUSTPM25)&&(concentrationPM25>0)) {
          gw.send(dustMsgPM25.set(ppmvPM25,2));
          gw.send(msgPM25.set(concentrationPM25_ugm3,2));
          lastDUSTPM25 = ceil(concentrationPM25);
      }
     //get PM 1.0 - density of particles over 1 μm.
      concentrationPM10=getPM(DUST_SENSOR_DIGITAL_PIN_PM10);
      Serial.print("PM10: ");
      Serial.print(concentrationPM10);
      Serial.println(" pcs/0.01cf");
      concentrationPM10_ugm3 = conversion10(concentrationPM10);
      Serial.print("PM10: ");
      Serial.print(concentrationPM10_ugm3);
      Serial.println(" ug/m3");
      Serial.print("\n");
      //ppmv=mg/m3 * (0.08205*Tmp)/Molecular_mass
      //0.08205   = Universal gas constant in atm·m3/(kmol·K)
      ppmvPM10=((concentrationPM10_ugm3) * ((0.08205*temp)/28.97));
      
      if ((ceil(concentrationPM10) != lastDUSTPM10)&&((long)concentrationPM10>0)) {
          gw.send(dustMsgPM10.set(ppmvPM10,2));
          gw.send(msgPM10.set(concentrationPM10_ugm3,2));
          lastDUSTPM10 = ceil(concentrationPM10);
      }
    
      while(digitalRead(CO2_SENSOR_PWM_PIN) == HIGH) {;}
      //wait for the pin to go HIGH and measure HIGH time
      duration_co2 = pulseIn(CO2_SENSOR_PWM_PIN, HIGH, 2000000);
      co2ppm = 5000 * ((duration_co2/1000) - 2)/1000;
      Serial.print("CO2: ");
      Serial.print(co2ppm);
      Serial.print(" ppm");
      Serial.print("\n");
      if ((co2ppm != lastAIQ)&&(abs(co2ppm-lastAIQ)>=10)) {
          gw1.send(msg.set((long)ceil(co2ppm)));
          lastAIQ = ceil(co2ppm);
      }
      //sleep to save on radio
      gw.sleep(SLEEP_TIME);
      gw1.sleep(SLEEP_TIME);
      
    }
    
    float conversion25(long concentrationPM25) {
      double pi = 3.14159;
      double density = 1.65 * pow (10, 12);
      double r25 = 0.44 * pow (10, -6);
      double vol25 = (4/3) * pi * pow (r25, 3);
      double mass25 = density * vol25;
      double K = 3531.5;
      return (concentrationPM25) * K * mass25;
    }
    
    float conversion10(long concentrationPM10) {
      double pi = 3.14159;
      double density = 1.65 * pow (10, 12);
      double r10 = 0.44 * pow (10, -6);
      double vol10 = (4/3) * pi * pow (r10, 3);
      double mass10 = density * vol10;
      double K = 3531.5;
      return (concentrationPM10) * K * mass10;
    }
    
    
    long getPM(int DUST_SENSOR_DIGITAL_PIN) {
    
      starttime = millis();
    
      while (1) {
      
        duration = pulseIn(DUST_SENSOR_DIGITAL_PIN, LOW);
        lowpulseoccupancy += duration;
        endtime = millis();
        
        if ((endtime-starttime) > sampletime_ms)
        {
        ratio = (lowpulseoccupancy-endtime+starttime)/(sampletime_ms*10.0);  // Integer percentage 0=>100
        concentration = 1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62; // using spec sheet curve
        Serial.print("lowpulseoccupancy:");
        Serial.print(lowpulseoccupancy);
        Serial.print("\n");
        Serial.print("ratio:");
        Serial.print(ratio);
        Serial.print("\n");
        //Serial.print("PPDNS42:");
        //Serial.println(concentration);
        //Serial.print("\n");
        
        lowpulseoccupancy = 0;
        return(concentration);    
        }
      }  
    }
    

    Please see the terminal printout below. The lowpulseoccupancy and ratio are correct. um/m3 to ppmv conversion is correct as well. I had to dive into physics and a bit of googling to find out how to convert pcs/0.01cf into ug/m3 - this is a tricky one!

    send: 11-11-0-0 s=1,c=1,t=37,pt=7,l=5,sg=0,st=ok:5.39
    send: 11-11-0-0 s=3,c=1,t=37,pt=7,l=5,sg=0,st=ok:6.45
    lowpulseoccupancy:3339978
    ratio:11.03
    PM10: 6751 pcs/0.01cf
    PM10: 10.53 ug/m3
    
    send: 11-11-0-0 s=0,c=1,t=37,pt=7,l=5,sg=0,st=ok:8.80
    send: 11-11-0-0 s=2,c=1,t=37,pt=7,l=5,sg=0,st=ok:10.53
    CO2: 960 ppm
    send: 13-13-0-0 s=0,c=1,t=37,pt=4,l=4,sg=0,st=ok:960
    lowpulseoccupancy:1983280
    ratio:6.51
    PM25: 3527 pcs/0.01cf
    PM25: 5.50 ug/m3
    
    send: 11-11-0-0 s=1,c=1,t=37,pt=7,l=5,sg=0,st=ok:4.60
    send: 11-11-0-0 s=3,c=1,t=37,pt=7,l=5,sg=0,st=ok:5.50
    lowpulseoccupancy:3430970
    ratio:11.34
    PM10: 7008 pcs/0.01cf
    PM10: 10.93 ug/m3
    
    send: 11-11-0-0 s=0,c=1,t=37,pt=7,l=5,sg=0,st=ok:9.13
    send: 11-11-0-0 s=2,c=1,t=37,pt=7,l=5,sg=0,st=ok:10.93
    CO2: 960 ppm
    lowpulseoccupancy:1789340
    ratio:5.86
    PM25: 3141 pcs/0.01cf
    PM25: 4.90 ug/m3
    
    send: 11-11-0-0 s=1,c=1,t=37,pt=7,l=5,sg=0,st=ok:4.09
    send: 11-11-0-0 s=3,c=1,t=37,pt=7,l=5,sg=0,st=ok:4.90
    lowpulseoccupancy:3271804
    ratio:10.80
    PM10: 6563 pcs/0.01cf
    PM10: 10.23 ug/m3
    
    send: 11-11-0-0 s=0,c=1,t=37,pt=7,l=5,sg=0,st=ok:8.55
    send: 11-11-0-0 s=2,c=1,t=37,pt=7,l=5,sg=0,st=ok:10.23
    CO2: 970 ppm
    send: 13-13-0-0 s=0,c=1,t=37,pt=4,l=4,sg=0,st=ok:970
    

    I still have not connected the BMP280 to get the pressure and temp readings into the equation.

    What do you think?





  • Hm, in the loop, mq135_ro will be read and used. As written in line 78, the MQ135_DEFAULTRO can be overwritten by the messured mq135_ro. Ok, but where is the 10kOhm voltage divider in this calculation? Included in the measurement of mq135_ro?
    https://github.com/empierre/arduino/blob/master/AirQuality-MQ135.ino#L78



  • @rollercontainer you are correct - mq135_ro is overwritten in the loop and I do not see a point in float mq135_ro = 10000.0; unless the mq135_ro function is deactivated during calibration. However, #define MQ135_DEFAULTRO 68550 this is the parameter you should change during the calibration process. Try to calibrate with the function mq135_ro and deactivating it though I am sure the result will be the same.


 

304
Online

8.6k
Users

9.4k
Topics

99.0k
Posts