The idea of this project is to make an inexpensive WIFI enabled air particle sensor that can log it's historical data on the web for others to use. The data can also be used to send SMS alerts to your cell phone to let you know when the air quality is poor.


With allergies and asthma I'm interested in both the indoor and outdoor air quality. I heat with a Quadrafire woodstove. It is suppose to be a clean stove. I was interested in the impact on both my inside air (ie. ash/dust) and outside air (smoke). New York State monitors the air quality at several locations around the State. Certified Allergy & Asthma Consultants in Albany NY published daily pollen counts.

What I learned today

Technology is changing very quickly in the IoT environment. I learned a ton of new things. The mentors are the Tech Valley Center Of Gravity Makerspace were wonderful! Thanks to Jeff Brunson from Sparkfun, Scot Straight from Makerfront, and Tom Tongue from COG.

How it Works

  • The PPD42NS sensor outputs a signal on Pin 6. You measure how long the digital signal on pin is low during the 30 second sample time. This ratio is used in a transfer function to calculate the particles per 0.01 cubic feet.
  • The air particle concentration is pushed to the Internet in two different ways. The first is thru a spark.variable which is a global variable on the cloud. It can be seen by applications like IFTTT. The second location is The Phant library is used to post the data. The data can be retrieved in several formats: JSON, CSV, MySQL, PostgreSQL, Atom.
  • The site reads the site and creates rolling graphs.
  • The text message alerts are generated using IFTTT. There are connection objects for the particle and SMS.



 Interface to Shinyei Model PPD42NS Particle Sensor
 Program by Christopher Nafis 
 Written January 2016
 JST Pin 1 (Black Wire)  => Photon GND
 JST Pin 3 (Red wire)    => Photon 5VDC
 JST Pin 4 (Yellow wire) => Photon Digital Pin 6
#include "math.h"
#include "SparkFunPhant/SparkFunPhant.h"

int pin = 6;
unsigned long duration;
unsigned long starttime;
unsigned long sampletime_ms = 30000;
unsigned long lowpulseoccupancy = 0;
float ratio = 0;
double concentration = 0;

const char server[] = ""; // Phant destination server
const char publicKey[] = "your public key"; // Phant public key
const char privateKey[] = "your private key"; // Phant private key
Phant phant(server, publicKey, privateKey); // Create a Phant object

const int POST_RATE = 30000; // Time between posts, in ms.
unsigned long lastPost = 0; // global variable to keep track of last post time

void setup() {
  Spark.variable("partcons", &concentration, DOUBLE);
  starttime = millis();

void loop() {
  duration = pulseIn(pin, LOW);
  lowpulseoccupancy = lowpulseoccupancy+duration;

  if ((millis()-starttime) > sampletime_ms)
    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(", ");
    Serial.print(", ");
    lowpulseoccupancy = 0;
    // If it's been POST_RATE ms (default 30 seconds), try to post again.
    if (lastPost + POST_RATE < millis())
        // If the post succeeds, update lastPost so we don't post for
        // another 30 seconds.
        if (postToPhant() > 0)
            lastPost = millis();
        // If the post fails, delay 1s and try again.
    starttime = millis();

int postToPhant()
    // Use phant.add(, ) to add data to each field.
    // Phant requires you to update each and every field before posting,
    // make sure all fields defined in the stream are added here.

    phant.add("air", concentration);
    TCPClient client;
    char response[512];
    int i = 0;
    int retVal = 0;
    if (client.connect(server, 80)) // Connect to the server
        // Post message to indicate connect success
        // will return a string formatted as an HTTP POST.
        // It'll include all of the field/data values we added before.
        // Use client.print() to send that string to the server.
        // Now we'll do some simple checking to see what (if any) response
        // the server gives us.
        while (client.available())
            char c =;
            Serial.print(c);    // Print the response for debugging help.
            if (i < 512)
                response[i++] = c; // Add character to response string
        // Search the response string for "200 OK", if that's found the post
        // succeeded.
        if (strstr(response, "200 OK"))
            Serial.println("Post success!");
            retVal = 1;
        else if (strstr(response, "400 Bad Request"))
        {   // "400 Bad Request" means the Phant POST was formatted incorrectly.
            // This most commonly ocurrs because a field is either missing,
            // duplicated, or misspelled.
            Serial.println("Bad request");
            retVal = -1;
            // Otherwise we got a response we weren't looking for.
            retVal = -2;
    {   // If the connection failed, print a message:
        Serial.println("connection failed");
        retVal = -3;
    client.stop();  // Close the connection to server.
    return retVal;  // Return error (or success) code.

Some of my other Projects

Built With

  • makerfront-ie-pro-3d-printer
  • photon
  • shinyei-model-ppd42ns-dust-sensor
Share this project: