/////////////////// © Tykhon, 2019 ////////////////////////////////////
#include "Wire.h"
#include <SoftwareSerial.h>
#include <TFT.h>
/////////////////// for DSM501 sensor ////////////////////////////////////
int pinV2 = 6;
int pinV1 = 5;
unsigned long durationV1;
unsigned long durationV2;
unsigned long starttimeV1;
unsigned long starttimeV2;
unsigned long lowpulseoccupancyV1 = 0;
unsigned long lowpulseoccupancyV2 = 0;
float one_to_two_point_five;
float ratioV1 = 0;
float ratioV2 = 0;
boolean flagV1 = true;
boolean flagV2 = true;
boolean V1 = false;
boolean V2 = false;
float concentrationV1 = 0;
float concentrationV2 = 0;
int allV1, allV2, goodV1, goodV2;
/////////////////// for Sharp sensor ////////////////////////////////////
unsigned long LastCheckSharp;
const int sharpLEDPin = 4; // Arduino digital pin 2 connect to sensor LED.
const int sharpVoPin = A6; // Arduino analog pin A6 connect to sensor Vo.
static unsigned long VoRawTotal = 0;
int VoRawCount = 0;
static float Voc = 0.45;
float dustDensity = 0.0;
float Vo = 0.0;
float Dust = 0.0;
/////////////////// for general purposes ////////////////////////////////
unsigned long starttime;
unsigned long endtime;
unsigned long sampletime_ms = 30000;
unsigned long now;
unsigned long loops;
#define DS1307_ADDR 0x68 // RTC address
/////////////////// for LCD ////////////////////////////////////////////
#define TFT_CS 10
#define TFT_RST 9
#define TFT_DC 8
int line_25[85] = {};
int line_10[85] = {};
byte lineIndex;
TFT TFTscreen = TFT(TFT_CS, TFT_DC, TFT_RST);
char one_to_two_point_fivec[5], Dustc[5], Pm25fc[5], Pm10fc[5];
////////////////// for SDS 011 sensor //////////////////////////////////
int rxPin = 3;
int txPin = 2;
SoftwareSerial mySerial(3, 2); //RX, TX
unsigned int Pm25 = 0;
unsigned int Pm10 = 0;
float Pm25f = 0.0;
float Pm10f = 0.0;
int sdspoll = 0;
unsigned int Pm25sum = 0;
unsigned int Pm10sum = 0;
unsigned long lastsds;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup()
{
Serial.begin(9600);
mySerial.begin(9600);
pinMode(pinV1,INPUT);
pinMode(pinV2,INPUT);
pinMode(sharpLEDPin, OUTPUT);
starttime = millis();
Wire.begin();
TFTscreen.begin();
TFTscreen.setRotation(2);
TFTscreen.background(20,0,0);
TFTscreen.stroke(200,200,200);
TFTscreen.setTextSize(2);
TFTscreen.text("DSM", 2, 5);
TFTscreen.text("Sharp", 2, 35);
TFTscreen.stroke(0,250,250);
TFTscreen.text("Pm10", 2, 65);
TFTscreen.stroke(180,10,250);
TFTscreen.text("Pm2.5", 2, 95);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////// for date and time //////////////////////////////////
byte bcdToDec(byte val) {
return ( (val/16*10) + (val%16) );
}
/////////////////// returns date and time //////////////////////////////////
String getdate(){
Wire.beginTransmission(DS1307_ADDR);
byte zero = 0x00;
Wire.write(zero);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDR, 7);
int secondint = bcdToDec(Wire.read());
int minuteint = bcdToDec(Wire.read());
int hour = bcdToDec(Wire.read() & 0b111111); //24 hour time
int weekDay = bcdToDec(Wire.read()); //0-6 -> sunday - Saturday
int monthDay = bcdToDec(Wire.read());
int month = bcdToDec(Wire.read());
int year = bcdToDec(Wire.read());
String second = String(secondint); if (secondint < 10) {second ="0"+second;};
String minute = String(minuteint); if (minuteint < 10) {minute ="0"+minute;};
return String(hour)+":"+minute+":"+second+" "+String(monthDay)+"/"+String(month)+"/"+String(year); ///
}
//////////////////// draws graph ////////////////////////////////////////
void tft_graph(){
byte k = 30;
TFTscreen.stroke(20,0,0);
TFTscreen.fill(20,0,0);
TFTscreen.rect(0, TFTscreen.height()-45, TFTscreen.width(), 45);
TFTscreen.stroke(250,180,10);
TFTscreen.line(5, 90+k, 5, 121+k);
TFTscreen.line(5, 121+k, 90, 121+k);
int maxvalue = 0;
for (int i = 0; i < (sizeof(line_10)/sizeof(int)); i++){
maxvalue = max(maxvalue, line_10[i]);
maxvalue = max(maxvalue, line_25[i]);
};
int g = 5+(sizeof(line_10)/sizeof(int));
for (int i = lineIndex-1; i >= 0; i--){
int x1 = g;
g--;
int y1 = map(line_10[i],maxvalue,0,90+k,120+k);
int y2 = map(line_25[i],maxvalue,0,90+k,120+k);
TFTscreen.stroke(10,180,250);
TFTscreen.point(x1, y1);
TFTscreen.stroke(180,10,250);
TFTscreen.point(x1, y2);
};
for (int i = (sizeof(line_10)/sizeof(int))-1; i >= lineIndex; i--){
int x1 = g;
g--;
int y1 = map(line_10[i],maxvalue,0,90+k,120+k);
int y2 = map(line_25[i],maxvalue,0,90+k,120+k);
TFTscreen.stroke(10,180,250);
TFTscreen.point(x1, y1);
TFTscreen.stroke(180,10,250);
TFTscreen.point(x1, y2);
};
}
///////////////////// prints data on screen ///////////////////////////
void tft_output(){
TFTscreen.stroke(20,0,0);
TFTscreen.text(one_to_two_point_fivec, 70, 5);
TFTscreen.text(Dustc, 70, 35);
TFTscreen.text(Pm10fc, 70, 65);
TFTscreen.text(Pm25fc, 70, 95);
String one_to_two_point_fives = String(one_to_two_point_five);
String Dusts = String(Dust);
String Pm10fs = String(Pm10f);
String Pm25fs = String(Pm25f);
one_to_two_point_fives.toCharArray(one_to_two_point_fivec,5);
Dusts.toCharArray(Dustc,5);
Pm10fs.toCharArray(Pm10fc,5);
Pm25fs.toCharArray(Pm25fc,5);
TFTscreen.stroke(255,255,255);
TFTscreen.text(one_to_two_point_fivec, 70, 5);
TFTscreen.text(Dustc, 70, 35);
TFTscreen.text(Pm10fc, 70, 65);
TFTscreen.text(Pm25fc, 70, 95);
}
////////////////// reads serial port and decodes data //////////////////////
void ProcessSerialData()
{
uint8_t mData = 0;
uint8_t i = 0;
uint8_t mPkt[10] = {0};
uint8_t mCheck = 0;
while (mySerial.available() > 0)
{
// from www.inovafitness.com
// packet format: AA C0 PM25_Low PM25_High PM10_Low PM10_High 0 0 CRC AB
mData = mySerial.read(); delay(2);//wait until packet is received
if (mData == 0xAA) //head1 ok
{
mPkt[0] = mData;
mData = mySerial.read();
if (mData == 0xc0) //head2 ok
{
mPkt[1] = mData;
mCheck = 0;
for (i = 0; i < 6; i++) //data recv and crc calc
{
mPkt[i + 2] = mySerial.read();
delay(2);
mCheck += mPkt[i + 2];
}
mPkt[8] = mySerial.read();
delay(1);
mPkt[9] = mySerial.read();
if (mCheck == mPkt[8]) //crc ok
{
mySerial.flush();
Pm25 = (uint16_t)mPkt[2] | (uint16_t)(mPkt[3] << 8);
Pm10 = (uint16_t)mPkt[4] | (uint16_t)(mPkt[5] << 8);
if (Pm25 > 9999)
Pm25 = 9999;
if (Pm10 > 9999)
Pm10 = 9999;
return;
}
}
}
}
}
////////////////// sends SLEEP command to SDS011 //////////////////////
void SDS011sleep()
{
while (mySerial.available() > 0)
{
uint8_t sleep_command[] = {0xAA, 0xB4, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x05, 0xAB};
for (uint8_t i = 0; i < 19; i++) {
mySerial.write(sleep_command[i]);
}
mySerial.flush();
}
}
////////////////// sends WAKEUP command to SDS011 //////////////////////
void SDS011wakeup()
{
while (mySerial.available() > 0)
{
uint8_t wakeup_command[] = {0xAA, 0xB4, 0x06, 0x01, 0x01, 0xC5};
for (uint8_t i = 0; i < 6; i++) {
mySerial.write(wakeup_command[i]);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{
loops +=1;
now = millis();
////////////// Sharp ////////////////////////
if ((now - LastCheckSharp) >= 10) {
LastCheckSharp = now;
digitalWrite(sharpLEDPin, LOW);
delayMicroseconds(200); // was 280, but real 320mks at 200 setting
int VoRaw = analogRead(sharpVoPin);
digitalWrite(sharpLEDPin, HIGH);
VoRawTotal += VoRaw;
VoRawCount++;
};
//////////// DSM 501 ////////////////////////
V1 = digitalRead(pinV1);
if ((flagV1)&&!(V1)) { // start period of low V1
starttimeV1 = millis();
flagV1 = false;
};
if (!(flagV1)&&(V1)) { // stop period of low V1
durationV1 = millis() - starttimeV1;
flagV1 = true;
allV1+=1;
if ((durationV1 <= 90)&&(durationV1 >= 10)) {
lowpulseoccupancyV1 += durationV1;
goodV1+=1;
};
};
V2 = digitalRead(pinV2);
if ((flagV2)&&!(V2)) { // start period of low V2
starttimeV2 = millis();
flagV2 = false;
};
if (!(flagV2)&&(V2)) { // stop period of low V2
durationV2 = millis() - starttimeV2;
flagV2 = true;
allV2+=1;
if ((durationV2 <= 90)&&(durationV2 >= 10)) {
lowpulseoccupancyV2 += durationV2;
goodV2+=1;
};
};
//////////// SDS 011 ////////////////////////
if ((now - lastsds)> 1000) {
lastsds = now;
sdspoll ++;
ProcessSerialData();
Pm25sum += Pm25;
Pm10sum += Pm10;
};
//////////// output ///////////////////////////////
endtime = millis();
if ((endtime-starttime) > sampletime_ms)
{
Serial.print(getdate());
//////////////////////////
Vo = 1.0 * VoRawTotal / VoRawCount;
Vo = Vo / 1024.0 * 5.0;
float dV = Vo - Voc;
if (dV < 0 ) {dV = 0; Voc = Vo;};
Dust = 172.0*dV;
Serial.print(" Sharp: ");
Serial.print(Dust);
VoRawCount = 0;
VoRawTotal = 0;
//////////////////////////
ratioV1 = (lowpulseoccupancyV1*100.0)/(endtime-starttime); // Integer percentage 0=>100
ratioV2 = (lowpulseoccupancyV2*100.0)/(endtime-starttime); // Integer percentage 0=>100
// concentrationV1 = 1.1*pow(ratioV1,3)-3.8*pow(ratioV1,2)+520*ratioV1+0.62; // using spec sheet curve in pcs in 1/100 ft3
// concentrationV2 = 1.1*pow(ratioV2,3)-3.8*pow(ratioV2,2)+520*ratioV2+0.62; // using spec sheet curve in pcs in 1/100 ft3
concentrationV1 = -0.0885*pow(ratioV1,4) - 2.55055*pow(ratioV1,3)- 21.920538*pow(ratioV1,2) + 172.171285*ratioV1 - 90.112;
concentrationV2 = -0.0885*pow(ratioV2,4) - 2.55055*pow(ratioV2,3)- 21.920538*pow(ratioV2,2) + 172.171285*ratioV2 - 90.112;
if (concentrationV1 < 0) {concentrationV1 = 0.0;};
if (concentrationV2 < 0) {concentrationV2 = 0.0;};
one_to_two_point_five = concentrationV2 - concentrationV1;
if (one_to_two_point_five < 0) {one_to_two_point_five = 0;};
Serial.print(" DSM501_>2.5: ");
Serial.print(concentrationV1);
Serial.print(" DSM501_>1.0: ");
Serial.print(concentrationV2);
Serial.print(" DSM501_1-2.5: ");
Serial.print(one_to_two_point_five);
lowpulseoccupancyV1 = 0;
lowpulseoccupancyV2 = 0;
loops = 0;
allV1 = 0;
goodV1 = 0;
allV2 = 0;
goodV2 = 0;
///////////// SDS011 /////////////////
Pm25f = Pm25sum/(sdspoll*10.0);
Pm10f = Pm10sum/(sdspoll*10.0);
Serial.print(" Pm2.5 ");
Serial.print(Pm25f,2);
Serial.print(" Pm10 ");
Serial.print(Pm10f,2);
sdspoll = 0;
Pm25sum = 0;
Pm10sum = 0;
line_25[lineIndex] = int(Pm25f*100.0);
line_10[lineIndex] = int(Pm10f*100.0);
lineIndex++;
if (lineIndex >= 85) {lineIndex = 0;};
//////////// finish ///////////////
tft_output();
Serial.println();
tft_graph();
starttime = millis();
} // if 30 sec passed
} // loop
///////////////////////////////////////////////////////////////////////
/// © tykhon, 2019
///////////////////////////////////////////////////////////////////////
#include "Wire.h"
#include <SoftwareSerial.h>
#include <TFT.h>
/////////////////// for general purposes ////////////////////////////////
boolean automode = true; // manual on/off sensor or auto.
// Auto: sensot itself turn on (for 30 secs. )and off (for 1-30 mins) and gathers statistics.
// Manual: sensor never shuts off by itself, but the program turns it on (for work_time) and off (work_period - work_time) and handle all the incoming data.
byte mode = 3; // 0 for countinuous work of sensor 1-30 for work with 1-30 min delays. Set 0 for automode = false; 1-30 for automode = true
byte work_period = 2; // period of time (work+delay) in min 1 to 30; Recommended: 2, 3, 4, 5, 6, 10, 15, 20, 30. Relevant only if automode = false;
byte work_time = 1; // time of work of sensor in min 1 to 29, should be less than work_period; Relevant only if automode = false;
/////////////////// for LCD ////////////////////////////////////////////
#define TFT_CS 10
#define TFT_RST 9
#define TFT_DC 8
int line_25[135] = {};
int line_10[135] = {};
byte lineIndex;
TFT TFTscreen = TFT(TFT_CS, TFT_DC, TFT_RST);
char Pm25fc[5], Pm10fc[5], maxvaluec[4], line10meanc[4], line25meanc[4], hour_scalec[4];
////////////////// for SDS 011 sensor //////////////////////////////////
int rxPin = 3;
int txPin = 2;
SoftwareSerial mySerial(3, 2); //RX, TX
unsigned int Pm25 = 0;
unsigned int Pm10 = 0;
unsigned int Pm25sum = 0;
unsigned int Pm10sum = 0;
float Pm25f = 0.0;
float Pm10f = 0.0;
#define DS1307_ADDR 0x68 // RTC address
unsigned int current_minute, lastminute;
boolean active = false;
unsigned int loops = 0;
/////////////////// for date and time //////////////////////////////////
byte bcdToDec(byte val) {
return ( (val/16*10) + (val%16) );
}
/////////////////// returns date and time //////////////////////////////////
String getdate(){
Wire.beginTransmission(DS1307_ADDR);
byte zero = 0x00;
Wire.write(zero);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDR, 7);
int secondint = bcdToDec(Wire.read());
int minuteint = bcdToDec(Wire.read());
current_minute = minuteint;
int hour = bcdToDec(Wire.read() & 0b111111); //24 hour time
int weekDay = bcdToDec(Wire.read()); //0-6 -> sunday - Saturday
int monthDay = bcdToDec(Wire.read());
int month = bcdToDec(Wire.read());
int year = bcdToDec(Wire.read());
String second = String(secondint); if (secondint < 10) {second ="0"+second;};
String minute = String(minuteint); if (minuteint < 10) {minute ="0"+minute;};
return String(hour)+":"+minute+":"+second+" "+String(monthDay)+"/"+String(month)+"/"+String(year); ///
}
/////////////////// gets data from sensor //////////////////////////////////
void print_Pm(){
Pm25f = Pm25/10.0;
Pm10f = Pm10/10.0;
String dataString = getdate()+" Pm2.5: "+String(Pm25f,2)+" Pm10: "+String(Pm10f,2);
dataString.replace(".",",");
dataString.replace(" ","\t");
Serial.println(dataString);
line_25[lineIndex] = Pm25;
line_10[lineIndex] = Pm10;
lineIndex++;
if (lineIndex >= sizeof(line_10)/sizeof(int)) {lineIndex = 0;};
}
//////////////////// draws graph ////////////////////////////////////////
void tft_graph(){
// x1 y1 x2 y2
unsigned int line10sum = 0;
unsigned int line25sum = 0;
unsigned int elements = 0;
TFTscreen.stroke(20,0,0);
TFTscreen.fill(20,0,0);
TFTscreen.rect(0, TFTscreen.height()-65, TFTscreen.width(), 64);
TFTscreen.stroke(250,180,10);
TFTscreen.line(20, 65, 20, 118);
TFTscreen.line(20, 118, 156, 118);
int maxvalue = 0;
for (int i = 0; i < (sizeof(line_10)/sizeof(int)); i++){
maxvalue = max(maxvalue, line_10[i]);
maxvalue = max(maxvalue, line_25[i]);
if (line_10[i] > 0) {
line10sum += line_10[i];
line25sum += line_25[i];
elements ++;
};
};
float line10mean = line10sum / (elements*10);
float line25mean = line25sum / (elements*10);
maxvalue = int(ceil( float(maxvalue)/50.0)*50);
String maxvalues = String(maxvalue/10);
maxvalues.toCharArray(maxvaluec,4);
String line10means = String(int(round(line10mean+0.25)));
line10means.toCharArray(line10meanc,4);
String line25means = String(int(round(line25mean+0.25)));
line25means.toCharArray(line25meanc,4);
TFTscreen.setTextSize(1);
TFTscreen.stroke(255,255,255);
TFTscreen.text(maxvaluec, 1, 65);
int hours_count = sizeof(line_10)/sizeof(int);
int period = work_period;
if (automode) {period = mode;};
hours_count = hours_count * period;
hours_count = hours_count / (-60);
int onestep = 1;
if (hours_count < -5) {onestep = 2;};
for (int i = 0; i >= hours_count; i = i - onestep) {
String hour_scales = String(i);
hour_scales.toCharArray(hour_scalec,4);
int posx = 15 +(sizeof(line_10)/sizeof(int)) + i*(60/period);
TFTscreen.stroke(250,180,10);
TFTscreen.line(posx+5, 118, posx+5, 121);
TFTscreen.stroke(255,255,255);
int shift = posx+5;
if (shift > 155) {shift = shift-10;};
TFTscreen.text(hour_scalec, shift, 121);
};
TFTscreen.stroke(10,180,250);
TFTscreen.text(line10meanc, 1, 85);
TFTscreen.stroke(180,10,250);
TFTscreen.text(line25meanc, 1, 105);
TFTscreen.setTextSize(2);
int g = 20+(sizeof(line_10)/sizeof(int));
for (int i = lineIndex-1; i >= 0; i--){
int x1 = g;
g--;
int y1 = map(line_10[i],maxvalue,0,66,118);
int y2 = map(line_25[i],maxvalue,0,66,118);
TFTscreen.stroke(10,180,250);
TFTscreen.line(x1, y1, x1, 118);
TFTscreen.stroke(180,10,250);
TFTscreen.line(x1, y2, x1, 118);
};
for (int i = (sizeof(line_10)/sizeof(int))-1; i >= lineIndex; i--){
int x1 = g;
g--;
int y1 = map(line_10[i],maxvalue,0,66,118);
int y2 = map(line_25[i],maxvalue,0,66,118);
TFTscreen.stroke(10,180,250);
// TFTscreen.point(x1, y1);
TFTscreen.line(x1, y1, x1, 118);
TFTscreen.stroke(180,10,250);
// TFTscreen.point(x1, y2);
TFTscreen.line(x1, y2, x1, 118);
};
}
///////////////////// prints data on screen ///////////////////////////
void tft_output(){
TFTscreen.stroke(20,0,0);
TFTscreen.text(Pm10fc, 70, 2);
TFTscreen.text(Pm25fc, 70, 32);
String Pm10fs = String(Pm10f);
String Pm25fs = String(Pm25f);
Pm10fs.toCharArray(Pm10fc,5);
Pm25fs.toCharArray(Pm25fc,5);
TFTscreen.stroke(255,255,255);
TFTscreen.text(Pm10fc, 70, 2);
TFTscreen.text(Pm25fc, 70, 32);
}
////////////////// reads serial port and decodes data //////////////////////
void ProcessSerialData()
{
uint8_t mData = 0;
uint8_t i = 0;
uint8_t mPkt[10] = {0};
uint8_t mCheck = 0;
while (mySerial.available() > 0)
{
// from www.inovafitness.com
// packet format: AA C0 PM25_Low PM25_High PM10_Low PM10_High 0 0 CRC AB
mData = mySerial.read(); delay(2);//wait until packet is received
if (mData == 0xAA) //head1 ok
{
mPkt[0] = mData;
mData = mySerial.read();
if (mData == 0xc0) //head2 ok
{
mPkt[1] = mData;
mCheck = 0;
for (i = 0; i < 6; i++) //data recv and crc calc
{
mPkt[i + 2] = mySerial.read();
delay(2);
mCheck += mPkt[i + 2];
}
mPkt[8] = mySerial.read();
delay(1);
mPkt[9] = mySerial.read();
if (mCheck == mPkt[8]) //crc ok
{
mySerial.flush();
Pm25 = (uint16_t)mPkt[2] | (uint16_t)(mPkt[3] << 8);
Pm10 = (uint16_t)mPkt[4] | (uint16_t)(mPkt[5] << 8);
if (Pm25 > 9999)
Pm25 = 9999;
if (Pm10 > 9999)
Pm10 = 9999;
return;
} else {Serial.println('crc not ok');};
} else {Serial.println('head not ok');};
}
}
}
////////////////// sends work mode command to SDS011 //////////////////////
void SDS011workmode(byte mode)
{
byte cs = 7 + mode;
uint8_t sleep_command[] = {0xAA, 0xB4, 0x08, 0x01, mode, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, cs, 0xAB};
// ^^ 0 for continuous, 1-30 for 1-30 min delay between turns on. ^^ checksum: 07 for 0x00, 08 for 0x01 and so on
for (uint8_t i = 0; i < 19; i++) {
mySerial.write(sleep_command[i]);
}
}
////////////////// sends SLEEP command to SDS011 //////////////////////
void SDS011sleep()
{
uint8_t sleep_command[] = {0xAA, 0xB4, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x05, 0xAB};
for (uint8_t i = 0; i < 19; i++) {
mySerial.write(sleep_command[i]);
}
}
////////////////// sends WAKEUP command to SDS011 //////////////////////
void SDS011wakeup()
{
uint8_t wakeup_command[] = {0xAA, 0xB4, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x06, 0xAB};
for (uint8_t i = 0; i < 19; i++) {
mySerial.write(wakeup_command[i]);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup()
{
Serial.begin(9600);
mySerial.begin(9600);
Wire.begin();
SDS011wakeup();
delay(10);
SDS011wakeup();
if (automode == false) {SDS011workmode(0);} else {SDS011workmode(mode);};
TFTscreen.begin();
delay(100);
TFTscreen.setRotation(1);
TFTscreen.background(20,0,0);
TFTscreen.setTextSize(2);
TFTscreen.stroke(0,250,250);
TFTscreen.text("Pm10", 2, 2);
TFTscreen.stroke(180,10,250);
TFTscreen.text("Pm2.5", 2, 32);
// tft_graph();
if (automode == false) { //(mode != 0)&&(
delay(30000);
// SDS011sleep();
}
ProcessSerialData();
print_Pm();
tft_output();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{
getdate();
if (automode == false) {
if (((current_minute+work_time)%work_period == 0)&&(active == false)) {
SDS011wakeup();
active = true;
};
if (active == true) {
ProcessSerialData();
loops ++;
Pm25sum += Pm25;
Pm10sum += Pm10;
// /*
Serial.print (loops);
Serial.print (" Pm25 = ");
Serial.print (Pm25);
Serial.print (" Pm10 = ");
Serial.println (Pm10);
// */
};
if (((current_minute)%work_period == 0)&&(active == true)) {
SDS011sleep();
active = false;
Pm25 = Pm25sum/loops;
Pm10 = Pm10sum/loops;
Pm25sum = 0;
Pm10sum = 0;
loops = 0;
print_Pm();
tft_output();
tft_graph();
};
} else { // if sensor goes on and off by itself
if (((current_minute)%mode == 0)&&(lastminute != current_minute)) {
ProcessSerialData();
print_Pm();
tft_output();
tft_graph();
lastminute = current_minute;
}
} // if sensor goes on and off by itself
delay(1000);
} // loop
+185 |
4083
247
|
+9 |
177
5
|
Вот немного дешевле
С большим удовольствием проникся темой которая меня уже давно интересует (дочка астматик-аллергик) и качество воздуха очень интересно.
Жаль у автора нет дома системы очистки воздуха, с ней все веселей.
Я смотрел в сторону
но 5рублей пока пресноводное не дает потратить.
Под брендом xiaomi вроде бы вот такой у этого же продавца все измеряет хорошо
Единственное, что у него только rx-tx, выхода pwm нет. Но в большинстве случаев этого порта хватает. И нет проблем с пересчетом ))
Особенно шокирован был показаниями датчика при включении ультразвукового увлажнителя. С тех пор включаю только когда влажность совсем низкая (раньше работал почти постоянно весь отопительный сезон).
Да и думается мне, что аэрозоль очень быстро испаряется.
Или как вы объясните появление пыли от ультразвукового увлажнителя?
Обсуждалось тыщу раз на профильных форумах. УЗ увлажнитель распыляет растворённые примеси — ну там кальций и всё прочее, что у вас в чайнике откладывается после кипячения. В УЗ надо заливать дистиллят, или как максимум воду после обратного осмоса.
Может, и не полезно этим дышать.
От них по-любому вреда нет, это ж вода.
Что?
Ну как бэ он даёт аэрозоль воды в воздухе (это такие мелкие-мелкие капельки, а не пар-газ), которую и регистрирует датчик.И что тут странного????? И что тут вредного? Если вода чистая конечно для лёгких.
Ну так вроде давно уже водопроводную воду перестали заливать в такие увлажнители.Как появился осмос.
(если вода чистая конечно для лёгких).
Впрочем и соль из воды не сильно вредна для человека.
Дистиллят постоянно пить вреднее.
Ещё один свидетель вымывания солей…
Заливал дистиллят для эксперимента, картина похожая, но показания не такие большие. Может, это подтверждает то, что «шкалит» действительно аэрозоль. А может растворяется налёт в увлажнителе.
В итоге ради перестраховки стал включать увлажнитель сильно меньше.
В инете внятного ничего на эту тему не нашёл. Как провести более точное исследование, пока не знаю :)
Но сам факт довольно интересен. Возможно, это можно использовать для проверки датчиков пыли.
Были б на i2c шине.
Если делать измеритель качества воздуха то не очень понятно как 2 датчика с uart подключить.
Есть еще датчик пыли от Sensirion, из плюсов — можно подключать по I2C, из минусов — стоит как крыло от Шаттла.
И как их обслуживать? Неужели можно прям со всех щелей там достать всю пыль?
А вот остальные «игрушки» явно для применения в бытовых очистителях воздуха как показометры.Их и чистить проще и реже.Внутри они не отличаются от дымовых пожарных датчиков.ИК излучающий диод и фотодиод в лабиринте под углом друг к другу.Ну ещё для селекции размера частиц могли применить микро щелевые диафрагмы и дифракционные фильтры.С их чисткой могут быть нюансы, если тонкие и нежные или на органической плёнке растворимой в спирту итд.
Ничего сложного в чистке нет.По быстрому продуваете компрессором(пылесосом на выдув, грушей-клизмой...).Ну раз в квартал.
ЕСЛИ они у вас работают постоянно, круглосуточно и вам нужна точность.
Раз в год, в 2 года, разбираете (полная разборка) и протираете ватными палочками и зубочистками с ватой и спиртом внутри.Потому что часть пыли жирная(все типы сажи, не считая масел и пыльцы, частиц кожи итд. итп.) и прилипает к поверхностям фотодиода и лазеров намертво.
Лопасти вентилятора тоже чистятся, как и сам вентилятор(я их мою в ПК стекломоем на спирту с ПАВ, с распылителя, сняв предварительно с устройства конечно).
Ну уметь тоже надо, учиться… В.И.Л.
По моим ощущениям, воздух — весьма инерционная система, а тут фиксируются события по 20 секунд в другом конце квартиры.
Может пыль поднимал не пылесос, а вибрации от перфоратора?
Может быть попробовать включить строительный пылесос в максимально герметичном объёме? Заклеить скотчем в картонную коробку. Или в мешок от мусора?
Ну и попробовать включить его без перфоратора…
Лежит в столе датчик nova, надо бы на выходных его к погодке всё же приделать…
Вот к примеру графики моего за сутки:
Сходимость данных с дорогущим BAM1020 очень высокая. Так что можно смело использовать ))
Если у Вас есть — может выложите куда-нибудь? На яндекс-диск например?
1) Вы как-то боритесь с тем что лазер имеет ограниченный срок жизни? К примеру включаете раз в 5 минут или что-то подобное
2) Корпус. Как их нормально закрепить? Жалательно фото :)
Некропост.Ну и как? Сколько прожил? :)
А показал он значения больше 700 по pm2.5…
В доме при этом было видно едва различимый дым
https://aliexpress.com/item/item/PM1-0-PM-2-5-PM10-9/32929301485.html
Вроде работает.
Датчик PMS5003 — народ хвалит его.
Хотя я бы лучше сейчас купил устройство еще и с датчиком улекислого газа:
https://aliexpress.com/item/item/Indoor-Outdoor-PM2-5/32887570197.html
Вариант с выносным датчиком становится интереснее, особенно в контексте умного дома, если есть управление отоплением, вентиляцией и увлажнителем/очистителем воздуха.
При наличии в контроллере DMA все еще проще — сказать ему, чтобы он получил нужное количество результатов АЦП, и подождать пока он завершит :)
вот тут сравнивали тучу датчиков с выводами после длительной работы и предположениями о сроке службы, + и — и тп. по итогу рекомендовали самый дорогой — Sensirion SPS30:
«Sensirion SPS30 не только измеряет частицы PM2.5 и PM10 в воздухе, которым вы дышите, но и доходит до PM1.0. Он имеет номинальный срок службы более десяти лет, даже если он работает непрерывно весь день, каждый день. Еще одним преимуществом использования этого датчика, по сравнению с другими, является то, что он очищает себя. Он делает это, вращая вентилятор до максимальной скорости всего за десять секунд, чтобы удалить любую пыль, которая могла скопиться внутри корпуса. Самоочистка вряд ли будет вас беспокоить, так как она будет работать только один раз в неделю. Этот параметр в настоящее время не может быть скорректирован в ESPHome, и даже если бы вы могли, вы должны оставить его там, где он есть, в соответствии с рекомендацией производителя.
Единственным недостатком Sensirion SPS30 является его цена. При цене около 50 долларов США он не совсем дешевый, и при использовании в мультисенсоре вместе с BMP280 или SHT4X ваш проект, скорее всего, будет стоить дороже, чем вы надеялись. Но, как упоминалось ранее, в конечном итоге вы получите ценность своих денег, так как этот датчик переживет конкурентов, а затем и некоторых.»
а сенсор пыли SDS011 через половину года будет как на фотке. и срок службы в 3 года намекает, как бы) хотя можно очистить, разобрав. лазер закрыт нормально, не в пыли. но предположили, что через пару лет придётся разобрать на прочистку. sps30 же 10 лет норм будет, якобы.
но стоит в 2-2.5 раза дороже pms сенсоров(
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.