Авторизация
Зарегистрироваться

Датчик пыли SDS011. Ставим три разных датчика в одно устройство.


Сравним три популярных датчика пыли. Стоит ли переплачивать за более дорогой? Какие наиболее распространенные ошибки допускают при работе с бюджетными датчиками? Ответы под катом.


Об устройстве чуть позже, сначала посмотрим внимательнее на датчики.

SDS011
Первый датчик лазерный, фирмы Nova Fitness. Он предназначен для определения количества пыли размером от 0,3 до 10 мкм в воздухе. Датчик разделяет пыль на две категории — размером от 0,3 до 2,5 мкм и от 2,5 до 10 мкм. Это общепринятая классификация, и наиболее опасна для здоровья пыль от 0,3 до 2,5 мкм.



Как и у многих современных датчиков, у этого два типа представления данных. Он способен передавать сведения в последовательный порт, а кроме того, имеет два выхода, скважность сигналов на которых пропорциональна концентрации пыли двух размеров.



Прочие сведения:
диапазон измерений: 0 — 999 мкг/м3
напряжение питания: 5 вольт
потребляемый ток: 100 мА
ток в режиме энергосбережения: 2 мА
температура окружающей среды: -20-50°С
время отклика: 1 сек
частота отправки сообщений в компорт: 1 раз в секунду.
минимальный определяемый размер частиц пыли: менее 0,3мкм.
размеры: 71х70х23 мм



Датчик снабжен центробежным микровентилятором.
В комплект входит переходник с последовательного порта на usb.





Тут pdf на с подробной информацией по датчику: https://inovafitness.de/downloads/

А тут описание протокола обмена по UART интерфейсу. https://nettigo.eu/attachments/415

GP2Y1014AU0F
Второй датчик производства фирмы Sharp. Он дешевле, компактнее и проще первого. Определяет пыль по принципу фотометрии. Краткие характеристики:



потребляемый ток: 20 мА
напряжение питания: 5 вольт
чувствительность: 0,5 вольт/100мкг/м3
размеры: 46х30x17,6мм
диапазон измерений: 0 — 500 мкг/м3



Вентилятора у датчика нет, что с одной стороны является преимуществом, но с другой обещает не такую быструю реакцию датчика на изменение атмосферы.

Вот описание датчика: http://www.sharp-world.com/products/device/lineup/data/pdf/datasheet/gp2y1010au_appl_e.pdf

Samyoung DSM501a
Третий датчик для определения пыли он использует тот же метод, что и Шарп, но в отличие от него, данные выдает по двум калибрам пыли — до 2,5мкм и от 2,5 и выше. Для связи передачи данных используется широтно-импульсная модуляция, что немного сложнее обработке, но намного надежнее с точки зрения помехозащищенности, чем аналоговый сигнал.
Его краткая характеристика:
напряжение питания: 4,5-5,5 вольт
потребляемый ток: 90ма
минимальный размер обнаруживаемой пыли: 1мкм
диапазон измерений: 0-1400 мкг/м3


У этого датчика есть выход, который используется для регулировки чувствительности второго канала. Таким образом, мы можем менять границу разделения пыли на две фракции. Для улучшения циркуляции воздуха используется микро-печка из резистора, которая нагревает воздух и за счет конвекции он быстрее обновляется у сенсора.



Описание: http://www.platan.ru/pdf/datasheets/samyoung/DSM501.pdf

Разумеется, фаворитом соревнований является лазерный датчик. Он и дороже и современнее своих конкурентов. Мне его прислали бесплатно, на обзор, и, как многие думают, я его должен хвалить уже из-за этого. Это не так, но сказать пару хороших слов можно сразу, едва вынув датчик из упаковки. Его легко подключить к компьютеру через прилагаемый переходник, скачать программу снятия данных и вуаля! График пыли строится на вашем экране.



Программа у меня провисела в фоне несколько дней, прежде чем я занялся сравнением датчиков, и уже в эти дни я понял, очень мало я знаю о пыли в своем доме.

Теперь, когда с соперниками разобрались, кратко остановимся на тех деталях, которые будут использованы для изготовления стенда.
Экран 128х160 с интерфейсом SPI, размером 1,8 дюйма.
Часы реального времени DS3231, соединение по шине I2C
«Черный ящик» OpenLog — будет висеть на компорте и записывать на флешку все, что контроллер отправляет в порт.



И конечно же Arduino Pro Mini — мозг и память всего проекта.
В качестве переходного звена, объединяющего все элементы, будет макетная платка. В основном на ней разъемы для подключения датчиков, но еще имеются резистор и электролитик — они требуются для нормального функционирования датчика Sharp.



Два резистора с джамперами — они служат для переключения порога чувствительности второго канала датчика DSM501, разъем микро-usb для питания в отсутствии компьютера и линейка резисторов для безопасного подключения экрана.



Вот что у меня получилось в результате.





Первым делом я решил протестировать датчик фирмы Sharp. Аналоговый выход обещал легкое подключение и обработку данных. Но все оказалось не совсем так. бОльшую часть времени датчик простаивает. Один раз в 10 мс на датчик нужно подать короткий импульс длительностью 320 мкс, который включит светодиод подсветки пыли. Потом, через 280 мкс, когда на выходе датчика окажется ответный сигнал, нужно успеть его снять и определить его амплитуду. Она-то и характеризует количество пыли в воздухе. Как только сигнал оцифрован, напряжение с подсветки снимается и все отключается до следующего импульса. Конечно, такой алгоритм снижает износ датчика и еще, как упомянуто в мануале, позволяет отличить пыль от дыма. Жаль, там не развивается эта мысль и остается только догадываться, как их отличить на практике.
Простым вольтметром сигнал с датчика не померить и пришлось снимать осциллограмму.



Желтый график — управление светодиодом подсветки пыли. Отрицательный сигнал включает диод.
Голубой — ответ датчика.
Фиолетовый я ввел для наглядности, он включается непосредственно перед тем, как контроллер приступает к измерению напряжения сигнала и выключается после того, как напряжение измерено. Что интересно, мы видим, что сигнал от датчика начинает спадать не после отключения диода подсветки, а еще при его работе. Так что в измерениях важно не промахнуться по времени и все точно выполнять по инструкции.
Второй важный момент — грамотно перевести полученные вольты в микрограммы на кубометр. Для этого надо вычесть из сигнала постоянную полку и по графику вычислить концентрацию пыли. Постоянная составляющая сигнала у каждого датчика своя. Все требует настройки.

Программисту на заметку:
В демонстрационных программах, которые можно найти на гитхабе, со временем промашка. Чтобы успеть все правильно измерить, нужно приступать уже через 200 мкс после включения диода, а не чрез 280, как там пишут.

delayMicroseconds(200); // was 280, but real 320mks at 200 setting



Итак. Что же мы имеем с гуся? А с гуся мы имеем почти ничего. Показания скачут вокруг нулевых значений. В принципе, это и не удивительно, я пылесошу каждый день. Но хотелось бы меньшего разброса показаний. Если поступить по-варварски и ввести в отверстие датчика какой-то предмет, то показания сразу взлетают на максимум, так что датчик рабочий. Просто для относительно чистой комнаты не очень чувствительный.



Второй датчик тоже заработал не сразу. Халява не прошла, и первый попавшийся в интернете код не заработал. Датчик показывал погоду на Марсе, и пришлось разбираться детально. Для начала я решил исключить плохое питание. Может оно и хорошее, но на всякий случай я припаял между + и — электролитик. Затем, в соответствии с мануалом, надо учитывать только импульсы от 10 до 90 мс. Да, импульсы у этого датчика следуют не с постоянной частотой и скважностью, а хаотично. И программно нужно их суммировать и вычислять процент присутствия импульсов относительно общего времени наблюдения. Так вот, в эту сумму не попадают импульсы менее 10 и более 90 мс. Третье: надо научиться пользоваться настройками датчика. При замыкании провода управления на землю через резистор 18,2КОм первый выход датчика становится столь же чувствительным, как и второй. Что не имеет практического смысла. При замыкании на землю через резистор 47 КОм первый выход начинает видеть пыль только крупнее 1,75 мкм. Таким образом, если в датчик залетит пылинка 1,5 мкм, то на втором выходе будет сигнал, а на первом — нет. При полном отключении управляющего провода от земли первый выход начинает замечать только пыль крупнее 2,5 мкм. Что нам и нужно. Мы снимаем показания со второго выхода, вычитаем их них показания с первого и получаем, таким образом, количество пыли в диапазоне от 1 до 2,5 мкм.



Но это еще не все. Остается перевести длительность присутствия сигнала на выходах датчика в количество пыли. А для этого в нашем распоряжении лишь график. График в программу не засунешь, я нашел онлайн сервис по оцифровке графиков и подбору полиномов для их более-менее точного моделирования. До меня это сделал какой-то парень из Штатов, но там у них свой стандарт, они меряют пыль в пылинках на одну сотую кубического фута. А у нас микрограммы на кубометр. Так что формула из готовых скриптов мне решительно не подходила.

Программисту на заметку:
Вот моя формула:
y = -0.088496207228664*x^4 — 2.5505502324503*x^3 — 21.92053783603786*x^2 + 172.17128476610927*x — 90.1119605706346
Где
х — процент времени, когда присутствует сигнал от датчика, 0-100%
y — концентрация пыли, мкг/м3
Формула очень хорошо коррелирует с графиком из датшита, но совсем не коррелирует с действительностью, увы.


После всех этих плясок с бубном ничего сильно хорошего не получилось: датчик показывал то чистейший горный воздух, то тяжелую атмосферу шлифовального цеха.
Удивительно, но если вернуться к старой формуле из примеров использования датчика, той, с штуками пылинок на сотую кубофута, то выдаваемые датчиком значения численно более-менее правдоподобны, для чистой комнаты. Но как только поднимается пыль, они начинают отставать.



А вот лазерный датчик хлопот не доставил. Байты из порта прочитались сразу. Сигнал с ШИМ — выходов тоже равномерный и красивый.



Показания очень хорошо соотносятся с происходящими вокруг датчика событиями. Это было заметно еще по первому включению, с выводом графика на десктоп. Пики в левой части графика — пайка. Датчик лежал на столе, где я паял стенд. По ощущениям, дыма особого не было, но график решительно полз вверх каждый раз, когда я включал паяльник. Короткий всплеск справа — расстилание кровати. Оказывается, даже чистая постель — источник огромного количества пыли.



Еще один неочевидный вывод — хороший пылесос совсем не гоняет пыль по комнате, а действительно ее всасывает. Включение пылесоса совершенно не отражалось на графике. А вот строительный пылесос — совсем другое дело. Мне нужно было просверлить пару небольших отверстий в ванной комнате. Там вытяжка, там маленькие отверстия, но все-таки я подогнал строительный пылесос, чтобы пылью от сверления не портить эксперимент с датчиками пыли. Эффект получился совсем не такой, как я ожидал.



Концентрация пыли мгновенно выросла в 20 раз и спадала потом еще полчаса. Кстати, проветривание резко снижает количество пыли в воздухе. Наверное, не во всех областях это так, но мне вот повезло.

Код скрипта

/////////////////// © 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






Выводы
Я пару дней гонял стенд с тремя датчиками в разных условиях. В области малых концентраций пыли для измерений годится только лазерный датчик. Он же и самый быстрый на отклик. У меня нет возможностей проверить показания этого датчика. Но мне достаточно того, что в его измерениях отражались все события в комнате, которые так или иначе должны были влиять на пыль. Расстилание кровати, проветривание, пайка, работа пылесоса, ремонтные работы — все это тут же сказывалось на графике. Что до шума от вентилятора, то он не очень большой. И у датчика есть режим энергосбережения, при котором вентилятор не работает. Два других датчика скорее подойдут для контроля относительно грязной атмосферы. Если выбирать из них, то Samyoung DSM 501 выглядит предпочтительнее. Его преимущества — разделение пыли по фракциям, лучшая помехозащищенность, большой диапазон и улучшенная циркуляция воздуха.

Плюсы датчика SDS011
+ высокая корреляция показаний прибора и обстановки с пылью вокруг
+ стабильность показаний
+ скорость реакции
+ невысокая (для лазерных датчиков) стоимость.

Минусы датчика SDS011
— шум от вентилятора
— относительно высокое энергопотребление

Обновление от 24.05.19
В итоге, для постоянной работы я оставил один только датчик пыли — разумеется SDS011. У него обнаружилась полезная особенность. Для экономии электричества и ресурса он может работать в перывистом режиме — заданный период он ожидает, потом включается на 30 секунд, измеряет концентрацию пыли и выдает ее в порт. Потом снова засыпает. Еще одна полезная настройка — датчик может самостоятельно посылать данные в порт, а может ждать запроса от контроллера. Мне показалось, что 30 секунд недостаточно, чтобы датчик вышел на стабильные показания, так что я провел серию экспериментов. В скетч добавил режим, в котором датчик принудительно включается и выключается. Пробовал запускать его на минуту и две, регулярно опрашивать и усреднять показания. В итоге оказалось, что как раз-таки 30 секунд — оптимальный период работы.
В новой версии скетча добавлен автоматический режим, когда датчик сам включается и выключается, и ручной. Добавлены шкалы на графике. В итоге экран прибора выглядит примерно так:



Код скетча

///////////////////////////////////////////////////////////////////////
///                    © 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




Товар предоставлен для написания обзора магазином. Обзор опубликован в соответствии с п.18 Правил сайта.
Планирую купить +55 Добавить в избранное +83 +126
свернуть развернуть
Комментарии (64)
RSS
+
avatar
  • Angrim
  • 11 апреля 2019, 08:11
+2
Офигеть, даже такое бывает? Ждем новых версий diy народных настольных метеостанций с датчиками пыли!
+
avatar
  • spc
  • 11 апреля 2019, 11:00
0
У меня уже почти третий год трудится с датчиком Sharp. Ну, так — посмотреть, что плюс-минус происходит — годится. Но какие-то сомнения по поводу адекватности то ли датчика, то ли алгоритма получения данных с него имеются.
+
avatar
0
Если честно то не покупаю метеостанции именно по причине того, что они или меряют многое но стоят космических денег или кроме влажности и темпы от них ничего.
С большим удовольствием проникся темой которая меня уже давно интересует (дочка астматик-аллергик) и качество воздуха очень интересно.
Жаль у автора нет дома системы очистки воздуха, с ней все веселей.

Я смотрел в сторону
но 5рублей пока пресноводное не дает потратить.
+
avatar
  • violant
  • 11 апреля 2019, 14:13
+1
так вроде бы обсуждали, что он не меряет СО2, а вычисляет его приблизительно.
Под брендом xiaomi вроде бы вот такой у этого же продавца все измеряет хорошо
+
avatar
  • pulp
  • 11 апреля 2019, 08:33
+3
интересная работа, спасибо. Рекомендую попробовать датчики pms5003. Тоже лазерный, отменная точность, удобно что вход и выход воздуха с одной стороны, что сильно упрощает установку в корпус. Ну и дешевле почти вдвое, да ))
Единственное, что у него только rx-tx, выхода pwm нет. Но в большинстве случаев этого порта хватает. И нет проблем с пересчетом ))
+
avatar
  • Lvr
  • 11 апреля 2019, 08:43
0
Также рекомендую, сам пробовал.
Особенно шокирован был показаниями датчика при включении ультразвукового увлажнителя. С тех пор включаю только когда влажность совсем низкая (раньше работал почти постоянно весь отопительный сезон).
+
avatar
+5
Датчик видит аэрозоль от увлажнителя, ничего удивительного :)
+
avatar
  • Lvr
  • 11 апреля 2019, 11:07
0
Очень хотел (и хочу) в это верить. Но в соседней комнате, за закрытыми дверями… Причём влажность в этой соседней комнате практически не возрастает, а вот показания пыли растут почти сразу.
Да и думается мне, что аэрозоль очень быстро испаряется.
+
avatar
+2
Скорее всего взвесь водяная.
Или как вы объясните появление пыли от ультразвукового увлажнителя?
+
avatar
  • figvam
  • 12 апреля 2019, 11:33
+2
> Или как вы объясните появление пыли от ультразвукового увлажнителя?

Обсуждалось тыщу раз на профильных форумах. УЗ увлажнитель распыляет растворённые примеси — ну там кальций и всё прочее, что у вас в чайнике откладывается после кипячения. В УЗ надо заливать дистиллят, или как максимум воду после обратного осмоса.
+
avatar
0
А, это да. Это я и называл взвесью.
Может, и не полезно этим дышать.
+
avatar
  • Q2W
  • 11 апреля 2019, 09:38
0
Так может датчик реагирует на мелкие капельки воды?
От них по-любому вреда нет, это ж вода.
+
avatar
  • wwest
  • 11 апреля 2019, 09:44
+2
(Особенно шокирован был показаниями датчика при включении ультразвукового увлажнителя.)
Что?
Ну как бэ он даёт аэрозоль воды в воздухе (это такие мелкие-мелкие капельки, а не пар-газ), которую и регистрирует датчик.И что тут странного????? И что тут вредного? Если вода чистая конечно для лёгких.
+
avatar
+2
Возможно, ели в увлажнитель заливать не дистиллят, то после высыхания капель остаются соли, т.е. та же пыль.
+
avatar
  • wwest
  • 11 апреля 2019, 10:13
+1
Да, вся мебель потом белая от соли.Проходили уже, ещё в 70 годы прошлого столетия.Как появились первые распылительные увлажнители.
Ну так вроде давно уже водопроводную воду перестали заливать в такие увлажнители.Как появился осмос.
(если вода чистая конечно для лёгких).
Впрочем и соль из воды не сильно вредна для человека.
Дистиллят постоянно пить вреднее.
+
avatar
+9
Вот только не у всех осмос стоит. Я из кувшинного фильтра заливаю.
Дистиллят постоянно пить вреднее.
Ещё один свидетель вымывания солей…
+
avatar
  • Lvr
  • 11 апреля 2019, 11:08
+1
Вот я и реально испугался этой пыли. Т.е. про налёт я знал, но что её столько…
Заливал дистиллят для эксперимента, картина похожая, но показания не такие большие. Может, это подтверждает то, что «шкалит» действительно аэрозоль. А может растворяется налёт в увлажнителе.
В итоге ради перестраховки стал включать увлажнитель сильно меньше.
В инете внятного ничего на эту тему не нашёл. Как провести более точное исследование, пока не знаю :)
+
avatar
0
Ну, есть предположение, что раз оли растворимые, то они будут далеко не так вредны, как, скажем, уличная пыль. Но я не настоящий сварщик.
+
avatar
  • Lvr
  • 11 апреля 2019, 12:19
0
Многолетняя эксплуатация увлажнителей такого типа без очевидных побочных эффектов подтверждает, что что бы это ни было, оно не сильно вредно. Думаю, к следующей зиме паранойя пройдёт и буду включать как и раньше.
Но сам факт довольно интересен. Возможно, это можно использовать для проверки датчиков пыли.
+
avatar
  • rx3apf
  • 11 апреля 2019, 11:52
+1
Ну, по цене сопоставимо — это на BG SDS011 дороговато, на ali нашел от $18, более-менее дешево. PMS5003 есть дешевле, но не радикально.
+
avatar
  • pulp
  • 11 апреля 2019, 13:02
0
беру их по $13. получается что можно взять 3 штук по цене двух. А беру я их десятками. Так что мне принципиально ))
+
avatar
  • skeptik
  • 11 апреля 2019, 20:23
0
rx-tx и у mh-z19 rx-tx, к одной esp8266 или pro mini не подключить вроде как.
Были б на i2c шине.
Если делать измеритель качества воздуха то не очень понятно как 2 датчика с uart подключить.
+
avatar
  • pulp
  • 12 апреля 2019, 04:43
0
Z19 прекрасно подключается по pwm.
+
avatar
+1
Игрался с разными типами датчиков. Эти светодиодные, видимо, хорошо работают только в Пекинском воздухе :) После всех экспериментов остановился на Plantower PMSA003.
Есть еще датчик пыли от Sensirion, из плюсов — можно подключать по I2C, из минусов — стоит как крыло от Шаттла.
+
avatar
0
с обозреваемым не сравнивали? обзорчик может есть?
+
avatar
  • mavr
  • 11 апреля 2019, 09:41
0
рано или поздно, датчик забъётся пылью, это будет влиять на точность измерения и как понять когда его надо очищать?
+
avatar
  • wwest
  • 11 апреля 2019, 09:45
0
По графику обслуживания и проверки.
+
avatar
  • Q2W
  • 11 апреля 2019, 10:04
0
А каков этот график у этих датчиков?
И как их обслуживать? Неужели можно прям со всех щелей там достать всю пыль?
+
avatar
  • wwest
  • 11 апреля 2019, 10:21
+2
Я думаю что на серьёзный лазерный производитель даёт информацию.Поищите на сайте, в описании и инструкции, запросите у производителя наконец.
А вот остальные «игрушки» явно для применения в бытовых очистителях воздуха как показометры.Их и чистить проще и реже.Внутри они не отличаются от дымовых пожарных датчиков.ИК излучающий диод и фотодиод в лабиринте под углом друг к другу.Ну ещё для селекции размера частиц могли применить микро щелевые диафрагмы и дифракционные фильтры.С их чисткой могут быть нюансы, если тонкие и нежные или на органической плёнке растворимой в спирту итд.

Ничего сложного в чистке нет.По быстрому продуваете компрессором(пылесосом на выдув, грушей-клизмой...).Ну раз в квартал.
ЕСЛИ они у вас работают постоянно, круглосуточно и вам нужна точность.
Раз в год, в 2 года, разбираете (полная разборка) и протираете ватными палочками и зубочистками с ватой и спиртом внутри.Потому что часть пыли жирная(все типы сажи, не считая масел и пыльцы, частиц кожи итд. итп.) и прилипает к поверхностям фотодиода и лазеров намертво.
Лопасти вентилятора тоже чистятся, как и сам вентилятор(я их мою в ПК стекломоем на спирту с ПАВ, с распылителя, сняв предварительно с устройства конечно).
Ну уметь тоже надо, учиться… В.И.Л.
+
avatar
  • pulp
  • 11 апреля 2019, 13:03
0
У лазера есть физический срок службы. у PMS производительуказывает 8000 часов, что примерно равно году безостановочной работы. После этого сенсор нужно менять, если нужна гарантированная точность. Как показометр он продолжит работать и дальше, но точность будет уже не та.
+
avatar
0
У сенсириона по спекам
Lifetime > 8 years operating continuously 24h/day
+
avatar
+1
А такие чёткие всплески от строительного пылесоса — это часом не артефакты?

По моим ощущениям, воздух — весьма инерционная система, а тут фиксируются события по 20 секунд в другом конце квартиры.

Может пыль поднимал не пылесос, а вибрации от перфоратора?

Может быть попробовать включить строительный пылесос в максимально герметичном объёме? Заклеить скотчем в картонную коробку. Или в мешок от мусора?

Ну и попробовать включить его без перфоратора…
+
avatar
  • pulp
  • 11 апреля 2019, 10:22
+1
Подозрителен не резкий подъем, а резкий спад. Для такого резкого спада должны быть все окна в квартире раскрыты настеж и дуть сильный ветер. Иначе спад со 150мкг до 20мкг может занять не меньше часа.
+
avatar
  • Oksion
  • 11 апреля 2019, 10:38
0
Как-то попадался мне в руки лазерный датчик пыли AS-1000 штука вроде годная рабочая, но по факту не сильно-то и нужная в хозяйстве, включеную сковороду гриль на раз определял даже в другой комнате. Поигрался я с ним, но так и не придумал нормальный копрус что-бы на постоянное использование. Единственный (ну существенный) минус в нём был, несколько не стандартный ответ в UART, помню долго мучался разбирая что он там выдает, ибо документации по нему нет и в инете не найти было. Работал в формате раз в 5 минут включаем получаем десяток измерений и спим до следующий пятиминутки.
+
avatar
  • Reaper5
  • 11 апреля 2019, 10:40
0
Спасибо за обзор.
Лежит в столе датчик nova, надо бы на выходных его к погодке всё же приделать…
+
avatar
0
С пылью все ясно, вот с СО2 бы разобраться, особенно в этом вариантессылка Настоящий ли там датчик СО2 или как в станции от Сяоми СО2 перерасчитвыается опосредованно другим способом?

+
avatar
-1
Вроде как там SenseAir внутри
+
avatar
  • batal
  • 11 апреля 2019, 13:10
+1
SenseAir S8 там
+
avatar
0
Это хороший, годный?
+
avatar
  • batal
  • 12 апреля 2019, 09:47
0
Да. В дорогих НЕкитайских устройствах тоже его ставят.
+
avatar
+1
Как-то они все бледно смотрятся на фоне дешевого PMS7003: лазер, микро вентилятор, куча параметров, быстрая, почти мгновенная, реакция. Точность абсолютных показаний мне сравнить не с чем, но по реакции на происходящее — похоже на правду.
Вот к примеру графики моего за сутки:

+
avatar
  • pulp
  • 11 апреля 2019, 13:08
0
Точность у серии pms очень хорошая. 5003 даже дешевле, но немного крупнее.
Сходимость данных с дорогущим BAM1020 очень высокая. Так что можно смело использовать ))
+
avatar
0
Откуда Вы скачали программу для отображения данных лазерного измерителя SDS011?
+
avatar
  • tykhon
  • 11 апреля 2019, 14:15
+1
Первая же ссылка в посте — там все: и документация, и программа, и драйвер, если нужен.
+
avatar
0
ссылка на программу неактивна:


Если у Вас есть — может выложите куда-нибудь? На яндекс-диск например?
+
avatar
  • batal
  • 11 апреля 2019, 13:12
0
А те кто Plantower использует (PMSA003, PMS7003, PMS5003, без разницы), подскажите 2 момента:

1) Вы как-то боритесь с тем что лазер имеет ограниченный срок жизни? К примеру включаете раз в 5 минут или что-то подобное
2) Корпус. Как их нормально закрепить? Жалательно фото :)
+
avatar
  • pulp
  • 11 апреля 2019, 13:42
+2
Зимой их гасить и включать раз в 5 минут — это плохое решение. Потому что в первые пару минут после старта в холодное время он врет в меньшую сторону очень заметно. Видимо, это связано с тем, что вентилятор замерзает и не успевает выйти на расчетный режим воздухообмена. Я поэкспериментировал и забил. Проще менять их раз в год. Если нужны точные данные. ))
+
avatar
0
А оно у вас стоит в неотапливаемом помещении или на улице?
+
avatar
0
В приточке вероятно.
+
avatar
  • pulp
  • 11 апреля 2019, 17:35
0
В приточке их ставить бессмысленно. Сильный поток воздуха вокруг заставит их врать безбожно в меньшую сторону.
+
avatar
  • pulp
  • 11 апреля 2019, 17:34
0
На улице. Зимой до -20 бывало. Две с половиной зимы отстояло.
+
avatar
0
1) Забил. Если накроется — куплю новый, а пока молотит 24x7 с опросом раз в секунду. заодно и узнаю правда 8000 часов или это так гарантированый срок.
+
avatar
  • pulp
  • 11 апреля 2019, 15:14
0
У меня есть один, который работает с декабря 2016 круглосуточно без остановок. Пока врет не сильно.
+
avatar
+1
Я как-то после жарки стейка посмотрел, что покажет детектор частиц.
А показал он значения больше 700 по pm2.5…
В доме при этом было видно едва различимый дым
+
avatar
  • NikSe
  • 11 апреля 2019, 15:12
0
Интересный обзор. А что за программа для ПК на скринах? Ссылочку можно?
+
avatar
  • NikSe
  • 11 апреля 2019, 15:14
0
Нашел, спасибо. ссылка
+
avatar
0
А подскажите бытовой готовый датчик? Давно интересно было
+
avatar
0
У меня вот такой:
aliexpress.com/item/PM1-0-PM-2-5-PM10-9/32929301485.html
Вроде работает.
Датчик PMS5003 — народ хвалит его.
Хотя я бы лучше сейчас купил устройство еще и с датчиком улекислого газа:
aliexpress.com/item/Indoor-Outdoor-PM2-5/32887570197.html
+
avatar
0
Мда, дизайнер отдохнул на первом устройстве… ) И цена приличные.
Вариант с выносным датчиком становится интереснее, особенно в контексте умного дома, если есть управление отоплением, вентиляцией и увлажнителем/очистителем воздуха.
+
avatar
  • AndyBig
  • 12 апреля 2019, 10:51
+1
Чтобы успеть все правильно измерить, нужно приступать уже через 200 мкс после включения диода, а не чрез 280, как там пишут.
delayMicroseconds(200); // was 280, but real 320mks at 200 setting
Или приступать немедленно, запускать непрерывное измерение, снимать 5 показаний и оставлять максимальное из них :) Ну или 5 раз подряд вручную запускать измерение, но боюсь, что ардуиновская библиотека слишком тормозная для этого :)
При наличии в контроллере DMA все еще проще — сказать ему, чтобы он получил нужное количество результатов АЦП, и подождать пока он завершит :)
+
avatar
  • yualeks
  • 12 апреля 2019, 22:44
+2
Теперь мы точно знаем откуда берётся пыль, ещё бы узнать куда деваются деньги.

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.