!!  Probably is easier to use a NODEMCU board or a Wemos Mini D1 due to built in USB !!

To make this Air Quality Sensor i’ve used Shinyei PPD42NS sensor and Adafruit Huzzah Esp8266 board.

The first thing that you need is an adapter for the Huzzah to connect it to your PC/Mac with USB cable.

ftdi cable

ftdi cable

I’ve used this cheap one bought on eBay: FT232RL FTDI USB to TTL Serial Adapter

FT232RL FTDI USB to TTL Serial Adapter

FT232RL FTDI USB to TTL Serial Adapter

To use the Huzzah ESP8266 with Arduino IDE read this official link: Using Arduino IDE – Adafruit Huzzah Esp8266

The small problem is that Huzzah ESP8266 has 3.3V logic level, due to that to connect the sensor i’ve used a Logic Level Converter because the Shinyei PPD42NS works at 5V.

Logic Level Converter

Logic Level Converter

Now you only need to hookup the parts correctly, in this image i’ve used the same ports of the code.

wiring shinyei pdd42ns

If you want to use Thingspeak you need to setup and create an account, it’s free and easy but you have the limit of one value update every 20 second.
So i’ve set to update the read of the Shinyei every 60 second, this also gave better accuracy to the readings.
Also check the Thingspeak API key of your channel, this key must be copied in the code for update your channel.

Then i’ve put everything in a electric plastic box:

To have a better reading i’ve also mounted a fan to circulate some air near the sensor.




#include < ESP8266WiFi.h > // replace with your channel’s thingspeak API key and wifi information,
String apiKey = “PUT HERE YOUR API KEY”;
const char* ssid = “PUT HERE YOUR SSID WIFI”;
const char* password = “PUT HERE YOUR WIFI PASSWORD”;
const char* server = “”;
//Set variables for PM10 and PM2,5 readings
unsigned long starttime;
unsigned long sampletime_ms = 60000; // TIME BETWEEN MEASURES AND UPDATES
unsigned long triggerOnP2;
unsigned long triggerOffP2;
unsigned long pulseLengthP2;
unsigned long durationP2;
boolean valP2 = HIGH;
boolean triggerP2 = false;
float ratioP2 = 0;
float countP2;
float concLarge;
unsigned long triggerOnP1;
unsigned long triggerOffP1;
unsigned long pulseLengthP1;
unsigned long durationP1;
boolean valP1 = HIGH;
boolean triggerP1 = false;
float ratioP1 = 0;
float countP1;
float concSmall;
WiFiClient client;
void setup() {
pinMode(4, INPUT); //PM2,5
pinMode(5, INPUT);// PM10
WiFi.begin(ssid, password);
Serial.print(“Connecting to “);
while (WiFi.status() != WL_CONNECTED) {
Serial.println(“WiFi connected”);
void measure(){
valP1 = digitalRead(4); // Small ( pm2.5)
valP2 = digitalRead(5); // Large ( pm10 )
if(valP1 == LOW && triggerP1 == false){
triggerP1 = true;
triggerOnP1 = micros();
if (valP1 == HIGH && triggerP1 == true){
triggerOffP1 = micros();
pulseLengthP1 = triggerOffP1 – triggerOnP1;
durationP1 = durationP1 + pulseLengthP1;
triggerP1 = false;
if(valP2 == LOW && triggerP2 == false){
triggerP2 = true;
triggerOnP2 = micros();
if (valP2 == HIGH && triggerP2 == true){
triggerOffP2 = micros();
pulseLengthP2 = triggerOffP2 – triggerOnP2;
durationP2 = durationP2 + pulseLengthP2;
triggerP2 = false;
if ((millis() – starttime) > sampletime_ms) {
// Integer percentage 0=>100
ratioP1 = durationP1/(sampletime_ms*10.0); // Integer percentage 0=>100
ratioP2 = durationP2/(sampletime_ms*10.0);
countP1 = 1.1*pow(ratioP1,3)-3.8*pow(ratioP1,2)+520*ratioP1+0.62;
countP2 = 1.1*pow(ratioP2,3)-3.8*pow(ratioP2,2)+520*ratioP2+0.62;
float PM10count = countP2;
float PM25count = countP1 – countP2;
//PM10 count to mass concentration conversion
double r10 = 2.6*pow(10,-6);
double pi = 3.14159;
double vol10 = (4/3)*pi*pow(r10,3);
double density = 1.65*pow(10,12);
double mass10 = density*vol10;
double K = 3531.5;
concLarge = (PM10count)*K*mass10;
//PM2.5 count to mass concentration conversion
double r25 = 0.44*pow(10,-6);
double vol25 = (4/3)*pi*pow(r25,3);
double mass25 = density*vol25;
concSmall = (PM25count)*K*mass25;
//——Send data to Thingspeak——
client.connect(server,80); // “” or
String postStr = apiKey;
postStr +=”&field1=”;
postStr += String(concLarge);
postStr += “\r\n”;
postStr +=”&field2=”;
postStr += String(concSmall);
postStr += “\r\n”;
client.print(“POST /update HTTP/1.1\n”);
client.print(“Connection: close\n”);
client.print(“X-THINGSPEAKAPIKEY: “+apiKey+”\n”);
client.print(“Content-Type: application/x-www-form-urlencoded\n”);
client.print(“Content-Length: “);
//—-Debug of Values on Serial Port—-
Serial.print(“PM10Conc: “);
Serial.print(“ug/m3 , PM2.5Conc: “);
Serial.println(” ug/m3″);
//Reset Values
durationP1 = 0;
durationP2 = 0;
starttime = millis();
void loop() {
measure(); // Recall of the measure program