Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Exploring Air Quality Monitoring Using Intel® Edison

14 May 2015 1  
This article shows a method of exploring air quality monitoring by measuring carbon dioxide, volatile organic compounds (VOC), and dust levels using the Arduino* ecosystem and sending the data to a cloud service provider.

This article is in the Product Showcase section for our sponsors at CodeProject. These articles are intended to provide you with information on products and services that we consider useful and of value to developers.

Get access to the new Intel® IoT Developer Kit, a complete hardware and software solution that allows developers to create exciting new solutions with the Intel® Galileo and Intel® Edison boards. Visit the Intel® Developer Zone for IoT.

Air quality monitoring is an interesting topic to explore with the rises in pollution, allergy sensitivity, awareness of health & fitness, and technology innovation. The consumer marketplace has seen innovative products released bringing more awareness to air quality monitoring in the home. One such product is the smart scale. These smart scales monitor a variety of health related parameters and also the air quality. The air quality is sent to the cloud and an app can alert you to the changes in the air quality so you will know when an area needs ventilation with fresh air. Having an awareness of the air quality could allow for an improved quality of life. This article shows a method of exploring air quality monitoring by measuring carbon dioxide, volatile organic compounds (VOC), and dust levels using the Arduino* ecosystem and sending the data to a cloud service provider.

The Intel® Edison platform is a natural fit for starting a new prototype or migrating an existing one given its fast processor, large memory size, and integrated connectivity for WiFi and Bluetooth. The Arduino ecosystem provides a capable set of hardware and firmware libraries to experiment with using the Intel® Edison Compute Module and Intel® Edison Arduino Breakout Board.

To learn more about the Intel Edison platform, please see the link below:

http://www.intel.com/content/www/us/en/do-it-yourself/edison.html

Hardware Components:

This project uses the following hardware components for the air quality monitoring system:

  • Intel® Edison Compute Module
  • Intel® Edison Arduino Breakout Board
  • Common Cathode RGB LED + 3 x 1kΩ resistors
  • GP2Y1010AU0F Optical Dust Sensor + 150Ω resistor + 220 µF electrolytic capacitor
  • MQ-135 Gas Sensor
  • K-30 CO2 Sensor
  • PIR Motion Sensor

Image 1

Figure 1 - Hardware Diagram

Theory of Operation

Figure 1 shows the hardware component connections to the Intel® Edison Arduino Breakout Board. The system uses an RGB LED as a simple visual indication system for displaying the air quality.

To determine the total air quality of an area, three sensors are used:

  1. An optical dust sensor is used to measure the dust in the area.
  2. A gas sensor is used to measure the Volatile Organic Compounds (VOC) such as smoke.
  3. A CO2 sensor is used to measure the carbon dioxide levels with an I2C interface.

In addition, a motion sensor is used for helping the system get the best representation of the total air quality in an area, by filtering out temporary increases in dust concentration caused by movement, and temporary increases in CO2 concentration caused by a person breathing close to the sensors.

When there is no motion detected, the firmware reads the air quality sensors, analyzes the sensor data, updates the visual indication system, and sends the air quality data to the cloud. The details of the system are further discussed in the Firmware section.

To learn more about the sensors, please see the data sheets at the links below:

http://www.kosmodrom.com.ua/pdf/MQ135.pdf

https://www.sparkfun.com/datasheets/Sensors/gp2y1010au_e.pdf

http://www.co2meter.com/collections/co2-sensors/products/k-30-co2-sensor-module

http://www.ladyada.net/media/sensors/PIRSensor-V1.2.pdf

Configuring the I2C Clock Frequency

It is important to note that at the time of this writing, the default I2C clock frequency on Intel® Edison is above 100kHZ which is outside the specification of the K-30 CO2 sensor. The K-30 CO2 sensor supports a maximum I2C clock frequency (SCL) of 100kHz. The Intel® Edison I2C clock frequency can be changed to 100kHZ following a few steps:

-Ensure that the latest Intel® Edison Yocto firmware image is installed:

http://www.intel.com/support/edison/sb/CS-035180.htm

-Open an Edison Linux terminal and login as root:

https://software.intel.com/en-us/articles/getting-started-with-the-intel-edison-board-on-windows

-cd /sys/devices/pci0000:00/0000:00:09.1/i2c_dw_sysnode

-echo std > mode

-cat mode

To learn more about the Intel® Edison compute module and the I2C peripheral, please see the link below:

http://www.intel.com/support/edison/sb/CS-035274.htm?wapkw=intel+edison+compute+module+hardware+guide

Firmware

The following code shows the includes, macros, and functions for the air quality system. Functions for Initialization, Main Loop, Reading Motion Sensor, Reading Air Quality Sensors, Analyzing Total Air Quality, Updating Visual Indication LED, and Sending Data to a Cloud Service Provider are discussed.

Includes:

C++
#include <Wire.h>

Macros:

C++
//Pin Defines
#define gasSensorPin A1
#define dustSensorPin A0
#define dustSensorLEDPin 2
#define redRGBLEDPin 3
#define greenRGBLEDPin 4
#define blueRGBLEDPin 5
#define motionSensorPin 6

//Air Quality Defines
#define AIR_QUALITY_OPTIMAL 2
#define AIR_QUALITY_GOOD    1
#define AIR_QUALITY_BAD     0
#define AIR_QUALITY_UNKNOWN -1
#define MAX_SENSOR_READINGS        10
#define SENSOR_READING_DELAY 1000

//Motion Sensor Defines  
#define MOTION_NOT_DETECTED 0
#define MOTION_DETECTED     1
#define MOTION_DELAY_TIME   1000

//Dust Sensor Timing Parameters (from p.5 of datasheet)
#define SAMPLE_DELAY        280  //Sampling
#define PULSEWIDTH_DELAY    40   //Pw
#define PERIOD_DELAY        9680 //T

//Gas Sensor Thresholds 
#define GAS_SENSOR_OPTIMAL 140
#define GAS_SENSOR_GOOD    200

//Dust Sensor Thresholds 
#define DUST_SENSOR_OPTIMAL 125
#define DUST_SENSOR_GOOD    250

//CO2 Sensor Thresholds 
#define CO2_SENSOR_OPTIMAL 800
#define CO2_SENSOR_GOOD    2000

Functions:

Initialization: This function initializes the serial debug interface, the I/O pins, and the I2C interface.

C++
void setup() {
  Serial.begin(9600);
  pinMode(gasSensorPin, INPUT);
  pinMode(dustSensorPin, INPUT);
  pinMode(dustSensorLEDPin, OUTPUT);
  pinMode(redRGBLEDPin, OUTPUT);
  pinMode(greenRGBLEDPin, OUTPUT);
  pinMode(blueRGBLEDPin, OUTPUT);
  pinMode(motionSensorPin, INPUT);
  Wire.begin();
}

Main Loop: The main loop initializes the system, checks for motion, reads the air quality sensors, analyzes the total air quality, updates the indication LED, and sends the data to a cloud service.

C++
void loop() {
  // -- Init
  int airQuality = 0;
  int motion = 0;
  int sensorAirQuality[3] = {0,0,0}; //0-Gas Sensor, 1-CO2 Sensor, 2-DustSensor
  Serial.println("");
  
  // -- Check for motion
  motion = readMotionSensor();
  
  if (motion == MOTION_NOT_DETECTED) {
    // -- Read Air Quality Sensors
    readAirQualitySensors(sensorAirQuality);
    
    // -- Analyze Total Air Quality
    airQuality = analyzeTotalAirQuality(sensorAirQuality[0],sensorAirQuality[1],sensorAirQuality[2]);
    
    // -- Update Indication LED
    updateIndicationLED(airQuality);
    
    // -- Update Air Quality Value for Cloud Datastream
    updateCloudDatastreamValue(CHANNEL_AIR_QUALITY_ID, airQuality);
  
    // -- Send Data To Cloud Service
    sendToCloudService();
  }
}

Reading Motion Sensor: The motion sensor is read by sampling the sensor’s digital output pin. If motion is detected, the sensor output pin will go HIGH. The function attempts to filter glitches and returns whether motion was detected or not.

C++
int readMotionSensor() {
  // -- Init
  int motionSensorValue = MOTION_NOT_DETECTED;
  int motion = MOTION_NOT_DETECTED;
  
  Serial.println("-Read Motion Sensor");
  
  // -- Read Sensor
  motionSensorValue = digitalRead(motionSensorPin);
  
  // -- Analyze Value
  if (motionSensorValue == MOTION_DETECTED) {
    delay(MOTION_DELAY_TIME);  
    motionSensorValue = digitalRead(motionSensorPin);
    
    if (motionSensorValue == MOTION_DETECTED) {
      motion = MOTION_DETECTED;
      Serial.println("--Motion Detected");
      updateIndicationLED(AIR_QUALITY_UNKNOWN);
    }
  }
  return motion;
}

Reading Air Quality Sensors: This function calls the individual gas, co2, and dust sensor functions. The function takes a pointer to integer array for storing the air quality results for each sensor.

C++
void readAirQualitySensors(int* sensorAirQuality)
{
  Serial.println("-Read Air Quality Sensors");
  
  sensorAirQuality[0] = readGasSensor();
  sensorAirQuality[1] = readCO2Sensor();
  sensorAirQuality[2] = readDustSensor();
}

Reading Gas Sensor: The gas sensor can detect gases such as NH3, NOx, alcohol, Benzene, and smoke. The gas sensor contains an analog voltage output that is proportional to the gas levels in the air. An A/D conversion is performed to read this sensor. The function reads the sensor, averages the readings, analyzes the sensor data, and returns the air quality for this sensor.

C++
int readGasSensor() {
  // -- Init
  int airQuality = 0;
  int gasSensorValue = 0;
  
  // -- Read Sensor
  for (int i=0; i < MAX_SENSOR_READINGS; i++) {
    gasSensorValue += analogRead(gasSensorPin);
    delay(SENSOR_READING_DELAY);
  }
  gasSensorValue /= MAX_SENSOR_READINGS; //Average the sensor readings
  
  // -- Update Cloud Datastream
  Serial.print("--gasSensorValue = ");
  Serial.println(gasSensorValue);
  updateCloudDatastreamValue(CHANNEL_GAS_SENSOR_ID, gasSensorValue);
  
  // -- Analyze Value
  if (gasSensorValue < GAS_SENSOR_OPTIMAL) {
    airQuality = AIR_QUALITY_OPTIMAL;
  }
  else if (gasSensorValue < GAS_SENSOR_GOOD) {
    airQuality = AIR_QUALITY_GOOD;
  }
  else {
    airQuality = AIR_QUALITY_BAD;
  }

  return airQuality;
}

Reading Dust Sensor: The dust sensor contains an optical sensing system that is energized using a digital output pin. An A/D conversion is then performed to sample the sensor’s analog voltage output that is proportional to the dust in the air. This function reads the sensor, averages the readings, analyzes the sensor data, and returns the air quality for this sensor.

C++
int readDustSensor() {
  // -- Init
  int airQuality = 0;
  int dustSensorValue = 0;
  
  
  // -- Read Sensor
  for (int i=0; i < MAX_SENSOR_READINGS; i++) {
    digitalWrite(dustSensorLEDPin,LOW);  //Enable LED
    delayMicroseconds(SAMPLE_DELAY);
    dustSensorValue += analogRead(dustSensorPin);
    delayMicroseconds(PULSEWIDTH_DELAY);
    digitalWrite(dustSensorLEDPin,HIGH); //Disable LED
    delayMicroseconds(PERIOD_DELAY);
    delay(SENSOR_READING_DELAY);
  }
  dustSensorValue /= MAX_SENSOR_READINGS; //Average the sensor readings
  
  // -- Update Cloud Datastream
  Serial.print("--dustSensorValue = ");
  Serial.println(dustSensorValue);
  updateCloudDatastreamValue(CHANNEL_DUST_SENSOR_ID, dustSensorValue);
  
  // -- Analyze Value
  if (dustSensorValue < DUST_SENSOR_OPTIMAL) {
    airQuality = AIR_QUALITY_OPTIMAL;
  }
  else if (dustSensorValue < DUST_SENSOR_GOOD) {
    airQuality = AIR_QUALITY_GOOD;
  }
  else {
    airQuality = AIR_QUALITY_BAD;
  }  
  
  return airQuality;
}

Reading CO2 Sensor: The CO2 sensor returns a CO2 concentration level in parts per million (ppm). The CO2 sensor is read through the I2C interface. This function reads the sensor, averages the readings, analyzes the sensor data, and returns the air quality for this sensor.

C++
int readCO2Sensor() {
  // -- Init
  int airQuality = 0;
  int co2SensorValue = 0;
  int tempValue=0;
  int invalidCount=0;
  
  // -- Read Sensor
  for (int i=0; i < MAX_SENSOR_READINGS; i++) {
    tempValue = readCO2();  // see http://cdn.shopify.com/s/files/1/0019/5952/files/Senseair-Arduino.pdf?1264294173 for this function
    (tempValue == 0) ? invalidCount++ : co2SensorValue += tempValue;
    delay(SENSOR_READING_DELAY);
  }
  
  if (invalidCount != MAX_SENSOR_READINGS) {
    co2SensorValue /= (MAX_SENSOR_READINGS - invalidCount); //Average the sensor readings
  }
  
  // -- Update Cloud Datastream
  Serial.print("--co2SensorValue = ");
  Serial.println(co2SensorValue);
  updateCloudDatastreamValue(CHANNEL_CO2_SENSOR_ID, co2SensorValue);
  
  // -- Analyze Value
  if (co2SensorValue < CO2_SENSOR_OPTIMAL) {
    airQuality = AIR_QUALITY_OPTIMAL;
  }
  else if (co2SensorValue < CO2_SENSOR_GOOD) {
    airQuality = AIR_QUALITY_GOOD;
  }
  else {
    airQuality = AIR_QUALITY_BAD;
  }  
  
  return airQuality;
}

Analyzing Total Air Quality: This function determines the total air quality for the area by analyzing the gas, co2, and dust air quality values passed to this function. The function returns the total air quality level for the area.

C++
int analyzeTotalAirQuality(int gasAirQuality, int co2AirQuality, int dustAirQuality) {
    int airQuality = 0;
    Serial.println("-Analyze Total Air Quality");
    if (gasAirQuality==AIR_QUALITY_BAD    \
        || dustAirQuality==AIR_QUALITY_BAD \
        || co2AirQuality==AIR_QUALITY_BAD) {
      Serial.println("--Air Quality Is BAD");
      airQuality = AIR_QUALITY_BAD;
    }
    else if (gasAirQuality == AIR_QUALITY_OPTIMAL \
             && dustAirQuality == AIR_QUALITY_OPTIMAL \
             && co2AirQuality==AIR_QUALITY_OPTIMAL) {
      Serial.println("--Air Quality Is OPTIMAL");
      airQuality = AIR_QUALITY_OPTIMAL;
    }
    else  {
      Serial.println("--Air Quality Is Good");
      airQuality = AIR_QUALITY_GOOD;
    }
    return airQuality;
}

Updating Visual Indication LED: This function updates the indication LED to the appropriate color for the air quality value that is passed to this function. The LED turns blue for optimal air quality levels, green for good air quality levels, and red for bad air quality levels. The LED turns magenta if motion is detected.

C++
void updateIndicationLED(int airQuality) {
  Serial.println("-Update Indication LED"); 
  // --Turn off all colors
  digitalWrite(redRGBLEDPin,LOW);
  digitalWrite(greenRGBLEDPin,LOW);
  digitalWrite(blueRGBLEDPin,LOW);
      
  // --Update Indication LED
  if (airQuality == AIR_QUALITY_UNKNOWN) {
    digitalWrite(redRGBLEDPin,HIGH);
    digitalWrite(greenRGBLEDPin,HIGH);
    digitalWrite(blueRGBLEDPin,HIGH);
  }
  else if (airQuality == AIR_QUALITY_OPTIMAL) {
    digitalWrite(blueRGBLEDPin, HIGH);
  }
  else if (airQuality == AIR_QUALITY_GOOD) {
    digitalWrite(greenRGBLEDPin, HIGH);
  }
  else {
    digitalWrite(redRGBLEDPin, HIGH);
  }
}

Sending Data to a Cloud Service Provider:

To connect Intel® Edison to a WiFi network, please see the link below:

http://www.intel.com/support/edison/sb/CS-035342.htm

Image 2

Figure 2 - xively.com feed

In this example, xively.com is used as the cloud service provider that the air quality data is sent to. Figure 2 shows an example feed with four channels. The channels are further discussed in the Functions section. Integration with xively.com requires the Http Client and Xively libraries added to the Arduino IDE. Please see the link below to learn more about xively.com, creating an account, Arduino tutorials, and library integration with the Arduino IDE.

https://xively.com/dev/tutorials/arduino_wi-fi/

The following code shows an example of the includes, macros, and functions that can be added to the air quality system to add xively.com support.

Includes:

C++
#include <WiFi.h>
#include <HttpClient.h>
#include <Xively.h>

Macros:

//Xively.com Defines
#define XIVELY_FEED <enter your feed number here>
#define XIVELY_KEY <enter your key string here>
#define XIVELY_HTTP_SUCCESS 200
#define CHANNEL_AIR_QUALITY "AIR_QUALITY"
#define CHANNEL_AIR_QUALITY_ID    0
#define CHANNEL_GAS_SENSOR "GAS_SENSOR"
#define CHANNEL_GAS_SENSOR_ID     1
#define CHANNEL_CO2_SENSOR "CO2_SENSOR"
#define CHANNEL_CO2_SENSOR_ID     2
#define CHANNEL_DUST_SENSOR "DUST_SENSOR"
#define CHANNEL_DUST_SENSOR_ID    3
#define MAX_CHANNELS              4

Global Variables:

C++
//Xively Datastream
XivelyDatastream datastreams[] = {
    XivelyDatastream(CHANNEL_AIR_QUALITY, strlen(CHANNEL_AIR_QUALITY), DATASTREAM_FLOAT),
    XivelyDatastream(CHANNEL_GAS_SENSOR, strlen(CHANNEL_GAS_SENSOR), DATASTREAM_FLOAT),
    XivelyDatastream(CHANNEL_CO2_SENSOR, strlen(CHANNEL_CO2_SENSOR), DATASTREAM_FLOAT),
    XivelyDatastream(CHANNEL_DUST_SENSOR, strlen(CHANNEL_DUST_SENSOR), DATASTREAM_FLOAT)
  };

//Xively Feed
XivelyFeed feed(XIVELY_FEED, datastreams, MAX_CHANNELS);
  
//Xively Client
WiFiClient client;
XivelyClient xivelyclient(client);

Functions:

Updating the data stream: This function is called to update the values for a xively.com channel datastream. The function is passed the channelID, and the datastream value. In this system as shown in Figure 2, four datastreams are used. The datastreams are updated with raw sensor data from the gas, co2, and dust sensor functions. In addition, a datastream is also updated in the main loop with the total air quality value.

C++
void updateCloudDatastreamValue(int channelID, int value) {
  // -- Update the Datastream Value
  datastreams[channelID].setFloat(value);
}

Sending the Datastreams to Xively: This function performs a PUT operation to a xively.com feed. The function returns the status of successful or the error code. The main loop calls this function.

C++
void sendToCloudService() {
  int status=0;
  Serial.println("-Send To Cloud Service");

  // -- Upload the Datastream to Xively
  status = xivelyclient.put(feed, XIVELY_KEY);
  
  // -- Verify Transaction
  if (status == XIVELY_HTTP_SUCCESS) {
   Serial.println("--HTTP OK");
  }
  else {
    Serial.print("--ERROR: "); 
    Serial.println(status);
  }
}

Summary

Hope you enjoyed exploring air quality monitoring with the Intel Edison platform. Challenge yourself to add additional indication showing the status of each sensor, to add enhancements to the cloud service experience with alert triggers when the air quality changes, and also look for opportunities to integrate air quality monitoring with other systems.

Image 3

About the Author

Mike Rylee is a Software Engineer at Intel Corporation with a background in developing embedded systems and apps on Android*, Windows*, iOS*, and Mac*. He currently works on enabling for Android and the Internet of Things.

++This sample source code is released under the Intel Sample Source License

Notices No license (express or implied, by estoppel or otherwise) to any intellectual property rights is granted by this document.

Intel disclaims all express and implied warranties, including without limitation, the implied warranties of merchantability, fitness for a particular purpose, and non-infringement, as well as any warranty arising from course of performance, course of dealing, or usage in trade.

This document contains information on products, services and/or processes in development. All information provided here is subject to change without notice. Contact your Intel representative to obtain the latest forecast, schedule, specifications and roadmaps.

The products and services described may contain defects or errors known as errata which may cause deviations from published specifications. Current characterized errata are available on request.

Copies of documents which have an order number and are referenced in this document may be obtained by calling 1-800-548-4725 or by visiting www.intel.com/design/literature.htm.

Intel and the Intel logo are trademarks of Intel Corporation in the U.S. and/or other countries.

*Other names and brands may be claimed as the property of others

© 2015 Intel Corporation.

Intel® Developer Zone for IoT

Start inventing today with the Intel® IoT Developer Program which offers knowledge, tools, kits and a community of experts to quickly and easily turn your innovative ideas into IoT Solutions.

Dream it, Build it with the Intel® IoT Developer Kit for Intel® Edison and Intel® Galileo platforms. These kits are versatile, performance-optimized and fully integrated end-to-end IoT solutions supporting a variety of programming environments, tools, security, cloud connectivity and hardware.

For more resources and to learn how the new Intel® IoT Developer Kit v1.0 can help streamline your IoT projects:

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here