Stazione comunitaria statica per polveri sottili PM10 + WiFi da 30 euro

Seguite il tutorial : questo post

1) togliete il sensore di temperatura e inserite il Dust Sensor PM10 da 18 euro..

2) Rispetto al post “Rilevazione PM10 con #arduino e stazione wifi mobile da 50euro” non ho necessità del GPS e quindi risparmio anche sull’Arduino UNo

Praticamente tutta la stazione si basa su alimentazione, scheda ESP8266, un led e il sensore Dust Grove

espwifi_pm10_bbrev1

Il sensore deve essere rivolto verso l’alto “in piedi” quindi e non vanno coperte le due finestrelle come da figura mentre il fori centrale va chiuso con nastro:

  

Al solito vi fate un account su data.sparkfun.com e la scheda WiFi si collega in hotspot tramite il vostro cellurare oppure se presente, con un Wifi permanente.

Schermata 2015-09-06 alle 09.30.58

Eccovi il codice. Attenzione usate Arduino IDE ma dovete mettere ESP8266 come scheda!! (leggete il post che vi ho indicato all’inizio):

/*
* This sketch sends data via HTTP GET requests to data.sparkfun.com service.
*
* You need to get streamId and privateKey at data.sparkfun.com and paste them
* below. Or just customize this script to talk to other HTTP servers.
*
*/

#define LOCATIONID "6542124" // 6542124 location id for Lecce, 3165185 Trieste on openweathermap.org
#define APIID "" // free api key on api.openweathermap.org

#include < ESP8266WiFi.h>
bool dry=false;
bool rain=false;
float c=1.0;

int pin = 0; // GPIO 0 pin of Dust Sensor
// #define ESP8266_LED 2
int ESP8266_LED = 2;
const char* ssid = "Piersoft"; //inserire SID e chiave WEP
const char* password = "12345678";

const char* hostw="178.62.133.61"; //api.openweathermap.org

const char* host = "54.86.132.254"; //data.sparkfun.com
const char* publicKey = ""; //inserire public stream on data.sparkfun.com
const char* privateKey = ""; //inserire private key on data.sparkfun.com
const char* host1 = "184.106.153.149"; //api.thinspeak.com (for twitter)
const char* privateKey1=""; //inserire privatekey thinkspeak twitter

unsigned long duration;
unsigned long starttime;
unsigned long sampletime_ms = 30000; //30000 for 30 sec, 300000 for 5 minutes
unsigned long lowpulseoccupancy = 0;
float ratio = 0;
float concentration = 0;
float concentration25 = 0;
float concentrationmg = 0;
float concentrationmg25 = 0;
int humidit=0.0;
String main="";

float pm10corretto=0;
float pm25corretto=0;

void setup() {

Serial.begin(9600);

pinMode(pin,INPUT);
starttime = millis();
pinMode(ESP8266_LED, OUTPUT);
delay(2000);
pinMode(ESP8266_LED, LOW);

// We start by connecting to a WiFi network

Serial.println();
Serial.println();
Serial.print("ESP8266 Connecting to ");
Serial.println(ssid);

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {

delay(500);
Serial.print(".");

}

Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());

Serial.print("connecting to ");
Serial.println(hostw);

// Use WiFiClient class to create TCP connections
WiFiClient client;
const int httpPort = 80;
if (!client.connect(hostw, httpPort)) {
Serial.println("connection failed");
setup();
//return;
}

// We now create a URI for the request

String url="/data/2.5/weather";
url+="?id=";
url += LOCATIONID;
url +="&APPID=";
url +=APIID;

Serial.print("Requesting URL: ");
Serial.println(url);

// This will send the request to the server
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: "+hostw+"\r\nConnection: keep-alive\r\n\r\n");

delay(500);

// read response
String response = "";
while(client.available()){
String line = client.readStringUntil('\r');
// Serial.print(line);

if (line.indexOf("{") >=0){
response += (line);
}
if (line.indexOf("Clear") >=0 || line.indexOf("clear") >=0){
dry=true;
rain=false;
}
else if (line.indexOf("rain") >=0 || line.indexOf("Rain") >=0){
rain=true;
dry=false;
}

delay(1);
}

Serial.println();
Serial.print("Main: ");
Serial.println(getValuesFromKey(response, "main"));
main=getValuesFromKey(response, "main");

Serial.print("Temp: ");
Serial.print(getValuesFromKey(response,"temp").toInt()-272.15); // kelvin to celsius
Serial.println(" C°");

Serial.print("Humidity: ");
Serial.print(getValuesFromKey(response, "humidity"));
Serial.println(" %");

Serial.print("Pressure: ");
Serial.print(getValuesFromKey(response, "pressure"));
Serial.println(" hPa");
Serial.println();

humidit=(getValuesFromKey(response, "humidity").toInt());
correction(humidit);

}
void(* resetFunc) (void) = 0;

int value=0;

void loop() {
value++;
pinMode(ESP8266_LED, LOW);
if (value<=30) { 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
// secondo me nella formula seguente / 10 secondo le formule sui siti no..ma i valori sono troppo alti anche se coerenti
concentrationmg= concentration * 0.427 /10;
concentrationmg25= concentration * 0.0020800535;

// 0.427 proviene da :
// http://www.fijnstofmeter.com/documentatie/Data-Validation.pdf
// assumendo PM10 "The mass of a particle in the PM10 channel is 1.21E-4 μg"
// PM concentration μg/m3 --> concentration × 3531.5 × Particles Mass
// 3531.5 è la conversione tra feet3 e m3
// ergo 3531.5 x 1.21 / 10000 = 0.4273 per il PM10
// The mass of a particle in the PM2.5 channel is 5.89E-7 μg
// analogamente 3531.5 x 5.89 / 10000000 = 0.0020800535
// tabelle di correzione --> fattore c
// PM10 corretto = PM10 non corretto x humidit/100 x c
pm10corretto=concentrationmg;
pm25corretto=concentrationmg25;

if (dry==true || rain == true) {
pm10corretto=concentrationmg*c*humidit/100;
pm25corretto=concentrationmg25*c*humidit/100;
}

if (pm10corretto <= 0.1 ) resetFunc(); lowpulseoccupancy = 0; starttime = millis(); } }else{ WiFiClient client; const int httpPort = 80; Serial.print("connecting to "); Serial.println(host); if (!client.connect(host, httpPort)) { Serial.println("connection failed"); //setup(); return; } Serial.print(lowpulseoccupancy); Serial.print(","); Serial.print(ratio); Serial.print(", concentrazione particelle per 0.0.1 feet3: "); Serial.println(concentration); Serial.print(",PM10 ug/m3 non corretto: "); Serial.println(concentrationmg); Serial.print("PM10 corretto: "); Serial.println(pm10corretto); Serial.print(", PM2.5 ug/m3 non corretto: "); Serial.println(concentrationmg25); Serial.print("PM25 corretto: "); Serial.println(pm25corretto); // We now create a URI for the request String url2 = "/input/"; url2 += publicKey; url2 += "?private_key="; url2 += privateKey; url2 += "&pm10="; url2 += pm10corretto; url2 += "&pm2_5="; url2 += pm25corretto; url2 += "&hum="; url2 += humidit; url2 += "&ic="; url2 += c; url2 += "&status="; url2 += main; Serial.print("connecting to "); Serial.println(host); if (!client.connect(host, httpPort)) { Serial.println("connection failed"); // resetFunc(); return; } Serial.print("Requesting UR2L: "); Serial.println(url2); // This will send the request to the server client.print(String("GET ") + url2 + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"); pinMode(ESP8266_LED, HIGH); delay(200); pinMode(ESP8266_LED, LOW); delay(200); pinMode(ESP8266_LED, HIGH); delay(200); pinMode(ESP8266_LED, LOW); delay(200); pinMode(ESP8266_LED, HIGH); delay(200); pinMode(ESP8266_LED, LOW); delay(200); pinMode(ESP8266_LED, HIGH); delay(200); pinMode(ESP8266_LED, LOW); delay(200); pinMode(ESP8266_LED, HIGH); delay(200); pinMode(ESP8266_LED, LOW); delay(200); pinMode(ESP8266_LED, HIGH); delay(200); pinMode(ESP8266_LED, LOW); delay(200); while(client.available()){ String line = client.readStringUntil('\r'); Serial.print(line); } Serial.println(); Serial.println("closing connection Data"); if (pm10corretto > 50 ) { // la normativa prevede il max 50ug/m3 medie giornaliere da superare max 35 volte in una anno
WiFiClient client;
const int httpPort = 80;
Serial.print("connecting to ");
Serial.println(host1);
if (!client.connect(host1, httpPort)) {
Serial.println("connection Twitter failed");
// resetFunc();
return;
}

String url1 = "/apps/thingtweet/1/statuses/update?";
url1 += "api_key=";
url1 += privateKey1;
//cambiare @piersoft con proprio account se si vuole essere taggati
url1 += "[email protected] Concentrazione PM10: ";
url1 += pm10corretto;
url1 += " qualcuno perlomeno sta fumando.. ";

delay(10);
Serial.print("Requesting URL Twitter: ");
Serial.println(url1);
// This will send the request to the server

client.print(String("GET ") + url1 + " HTTP/1.1\r\n" +
"Host: " + host1 + "\r\n" +
"Connection: close\r\n\r\n");

pinMode(ESP8266_LED, HIGH);
delay(100);
pinMode(ESP8266_LED, LOW);
delay(100);
pinMode(ESP8266_LED, HIGH);
delay(100);
pinMode(ESP8266_LED, LOW);
delay(100);
pinMode(ESP8266_LED, HIGH);
delay(100);
pinMode(ESP8266_LED, LOW);
delay(100);
pinMode(ESP8266_LED, HIGH);
delay(100);
pinMode(ESP8266_LED, LOW);
delay(100);
pinMode(ESP8266_LED, HIGH);
delay(100);
pinMode(ESP8266_LED, LOW);
delay(100);
pinMode(ESP8266_LED, HIGH);
delay(100);
pinMode(ESP8266_LED, LOW);
delay(100);
pinMode(ESP8266_LED, HIGH);
delay(100);
pinMode(ESP8266_LED, LOW);
delay(100);
pinMode(ESP8266_LED, HIGH);
delay(100);
pinMode(ESP8266_LED, LOW);
delay(100);
pinMode(ESP8266_LED, HIGH);
delay(100);
pinMode(ESP8266_LED, LOW);
delay(100);
pinMode(ESP8266_LED, HIGH);
delay(100);
pinMode(ESP8266_LED, LOW);
delay(100);
pinMode(ESP8266_LED, HIGH);
delay(100);
pinMode(ESP8266_LED, LOW);
delay(100);
pinMode(ESP8266_LED, HIGH);
delay(100);
pinMode(ESP8266_LED, LOW);
delay(100);

//setup();
// Read all the lines of the reply from server and print them to Serial
while(client.available()){
String line = client.readStringUntil('\r');
Serial.print(line);
}

Serial.println();
Serial.println("closing connection Twitter");

}

delay(15*60000); // restart ogni 5 minuti
resetFunc();

}

}

String getValuesFromKey(const String response, const String sKey)
{
String sKey_ = sKey;

sKey_ = "\"" + sKey + "\":";

char key[sKey_.length()];

sKey_.toCharArray(key, sizeof(key));

int keySize = sizeof(key)-1;

String result = "";

int n = response.length();

for(int i=0; i < (n-keySize-1); i++) { char c[keySize]; for(int k=0; k fattore c
// PM10 corretto = PM10 non corretto x humidit/100 x c
Serial.print("humidity intera: ");
Serial.println(hum);

if (dry==true){
if (0<=hum && hum<=19) c=10.1; else if (20<=hum && hum<=24) c=8.75; else if (25<=hum && hum<=29) c=8; else if (30<=hum && hum<=34) c=8; else if (35<=hum && hum<=39) c=8; else if (40<=hum && hum<=44) c=7; else if (45<=hum && hum<=49) c=6; else if (50<=hum && hum<=54) c=5.75; else if (55<=hum && hum<=59) c=5.5; else if (60<=hum && hum<=64) c=5.5; else if (65<=hum && hum<=69) c=3.5; else if (70<=hum && hum <=74) c=3.5; else if (75<=hum && hum<=79) c=3.75; else if (80<=hum && hum <=84) c=2.25; else if (85<=hum && hum <=89) c=1.5; else if (90<=hum && hum<=94) c=0.825; else if (95<=hum && hum<=100) c=0.525; } else if (rain==true){ if (0<=hum && hum<=19) c=6.4; else if (20<=hum && hum<=24) c=6.4; else if (25<=hum && hum<=29) c=6.4; else if (30<=hum && hum<=34) c=6.4; else if (35<=hum && hum<=39) c=6.4; else if (40<=hum && hum<=44) c=6.3; else if (45<=hum && hum<=49) c=6.3; else if (50<=hum && hum<=54) c=5.7; else if (55<=hum && hum<=59) c=5.5; else if (60<=hum && hum<=64) c=4.2; else if (65<=hum && hum<=69) c=4.1; else if (70<=hum && hum<=74) c=3.2; else if (75<=hum && hum<=79) c=3.2; else if (80<=hum && hum<=84) c=2.1; else if (85<=hum && hum<=89) c=2.1; else if (90<=hum && hum<=94) c=0.8; else if (95<=hum&& hum<=100) c=0.5; } Serial.print("indice correzione: "); Serial.println(c); //float pm10=5; //prova correzione pm10 con valore fittizio //float pm10corretto=pm10*c*hum/100; //Serial.print("PM10 corretto: "); //Serial.println(pm10corretto); }

Nota metodologica:
1) il sensore è al buio (messo nastro isolante nel foro centrale) e inserito in una scatola con prese d'aria.
2) la formula di conversione tra particelle e microgrammi e feet al cubo e m3 è un'approssimazione come descritto su http://www.fijnstofmeter.com/documentatie/Data-Validation.pdf ed è relativa al PM10 --> 1,21E-4 (PM10) x 3531.5 x concentrazione particelle /100 --> 0.427 x concentrazione particelle / 100

particles2

particles

Quindi il codice usa le API di api.openweathermap.org per prendere le rilevazioni dell'umidità e del fatto che ci sia pioggia o sereno e viene applicata questa tabella correttiva:

Schermata 2015-09-18 alle 00.40.21

e quindi applicata la formula finale:
Schermata 2015-09-18 alle 00.40.29

nel codice le parti da personalizzare sono SSID e Password WEP, la key pubblica e privata di data.sparkfun.com, la key privata dell'app twitter collegata con proprio account thinkspeak, l'api key su openweathermap.org

Potrebbero interessarti anche...

5 Risposte

  1. ciro spat ha detto:

    grazie per il tutorial Piersoft.

    http://www.piersoft.it/pm10/ su questa pagina devo dire alcune cose che penso possano contribuire a rendere i dati rilevati masticabili e comprensibili per quante più persone possibili.

    Le concentrazioni per “pcs/0.01cf” non dicono niente a nessuno. Nemmeno a me che ho avuto a che fare da anni con i dati del PM10 rilevato dalle stazioni fisse a Palermo.
    E questo perchè la legge fissa a 35 microgrammi su metrocubo la media delle rilevazioni giornaliere il limite per la salute umana. Superato tale limite c’è pericolo per la salute umana.
    Quindi io per primo (un po addetto ai lavori) e la gente (perchè i giornali parlano di microgrammi su metrocubo come unità di misura del PM10) conoscono questo come parametro per capire se quel giorno sono stati esposti, o meno, a elevate dosi di particolato nella loro città. Quindi la consapevolezza della gente parte dal fatto di conoscere i dati di microgrammi su metrocubo e non “pcs/0.01cf”.

    Quindi domanda:
    non puoi mettere nella pagina http://www.piersoft.it/pm10/ un algoritmo che oltre a dare i dati per “pcs/0.01cf” fa dei calcoli e li da anche per microgrammi su metrocubo, così che tutti comprendono ?
    In tal maniera ci possiamo (noi cittadini, la stampa) andare a confrontare i dati delle nostre piccole stazioni low budget con quelli della rete di monitoraggio dell’ARPA ove esiste nelle nostre città.

    Che dici
    un abbraccio
    ciro

  2. Andy ha detto:

    Ciao complimenti per la spiegazione. Sto facendo prove con sketch presi in vari siti e il tuo mi sembra il più completo. Per quanto riguarda la conversione in mg/mc anche a me vengono valori molto alti, ma la tua decisione di dividere per 10 mi sembra un po troppo arbitraria..hai qualche dato a supporto? Inoltre non capisco come ricavi i valori del pm2.5… in teoria il valore va ricavato dal pin apposito del sensore,ma nel tuo schema c’è solo il pin del pm10. Ti ringrazio se vorrai rispondermi.

    • piersoft ha detto:

      Ciao questo sensore PM10 si è rilevato poco affidabile. Nel senso che misura tutto il pulviscolo. quindi anche il pm2.5 ect ma alla fine non hai la misurazione in ug/m3. sto aspettandone uno che legge nativamente in questa unità di misura. ti farò sapere

Rispondi