{"id":2077,"date":"2021-01-10T17:53:06","date_gmt":"2021-01-10T16:53:06","guid":{"rendered":"http:\/\/hamspirit.pl\/SQ9MDD\/?p=2077"},"modified":"2021-01-10T17:53:06","modified_gmt":"2021-01-10T16:53:06","slug":"pomiar-zanieczyszczenia-powietrza","status":"publish","type":"post","link":"https:\/\/sq9mdd.hamspirit.pl\/?p=2077","title":{"rendered":"Pomiar zanieczyszczenia powietrza."},"content":{"rendered":"<p>Po wielu testach r\u00f3\u017cnych czujnik\u00f3w, chyba w ko\u0144cu znalaz\u0142em ca\u0142kiem sensowny czujnik zanieczyszczenia powietrza. Urz\u0105dzenie nazywa si\u0119 Plantower PMS7003 i mo\u017cna je kupi\u0107 w ca\u0142kiem rozs\u0105dnej cenie.<\/p>\n<p><!--more--><\/p>\n<p>Wed\u0142ug ulotki marketingowej Plantower PMS7003 to:<\/p>\n<p><em>&#8222;Laserowy czujnik zanieczyszcze\u0144 powietrza cz\u0105steczkami PM1,0, PM2.5 oraz PM10.<\/em><br \/>\n<em>Pozwala na precyzyjn\u0105 analiz\u0119 ilo\u015bci zanieczyszcze\u0144 w otoczeniu, ma niewielkie wymiary i wbudowany cichy wentylator przet\u0142aczaj\u0105cy.<\/em><br \/>\n<em>Dane wyprowadzone s\u0105 na interfejs UART&#8221;<\/em><\/p>\n<p>Zaskoczony by\u0142em wymiarami tego czujnika kt\u00f3ry okaza\u0142 si\u0119 du\u017co mniejszy od modeli kt\u00f3re testowa\u0142em wcze\u015bniej. Urz\u0105dzenie jest skalibrowane fabrycznie i nie wymaga ingerencji u\u017cytkownika.<br \/>\nPrace nad programem rozpocz\u0105\u0142em od przeszukania sieci, gdzie znalaz\u0142em kilka gotowych program\u00f3w napisanych pod arduino i w pythonie. Z analizy tych program\u00f3w wynika ze dane s\u0105 przesy\u0142ane w formacie binarnym z sum\u0105 kontroln\u0105 co zapewnia integralno\u015b\u0107 danych odbieranych z czujnika.<\/p>\n<div id=\"attachment_2087\" style=\"width: 310px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/10.9.48.151\/?attachment_id=2087\" rel=\"attachment wp-att-2087\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-2087\" class=\"wp-image-2087 size-medium\" src=\"http:\/\/10.9.48.151\/wp-content\/uploads\/2021\/01\/photo_2021-01-06_17-53-10-300x150.jpg\" alt=\"\" width=\"300\" height=\"150\" srcset=\"https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/photo_2021-01-06_17-53-10-300x150.jpg 300w, https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/photo_2021-01-06_17-53-10-1024x512.jpg 1024w, https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/photo_2021-01-06_17-53-10-768x384.jpg 768w, https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/photo_2021-01-06_17-53-10.jpg 1280w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-2087\" class=\"wp-caption-text\">Czujnik pod\u0142\u0105czony do arduino<\/p><\/div>\n<p>W oparciu o te gotowe fragmenty oraz moje wcze\u015bniejsze wsady do komunikacji za pomoc\u0105 protoko\u0142u mysensors, przygotowa\u0142em program kt\u00f3ry wysy\u0142a dane do Domoticza. Dzi\u0119ki temu b\u0119d\u0119 m\u00f3g\u0142 gromadzi\u0107 trendy i przesy\u0142a\u0107 dane dalej na przyk\u0142ad do systemu APRS.<\/p>\n<p>Poni\u017cej gotowy kod programu:<\/p>\n<pre>\/\/ **************************************************************\/\/\n\/\/ AS-500RVD\n\/\/ ARDUINO 2xVolt meter for solar panel and accumulator\n\/\/         PMS7003 dust sensor PM1, PM2.5, PM10\n\/\/ SQ9MDD @ 2019\n\/\/ **************************************************************\/\/\n\/\/ IO\n#define ai1 A0                                      \/\/AI1 B solar\n#define ai2 A1                                      \/\/AI2 A battery\n#define ONE_WIRE_BUS 4\n#define pms_RX  3\n#define pms_TX  0\n\n\/\/ radio \n#define MY_RADIO_NRF24\n\/\/#define MY_DEBUG                                  \/\/ Enable debug prints\n\/\/#define MY_GATEWAY_SERIAL                                         \/\/ Enable GW serial\n#define MY_REPEATER_FEATURE\n#define MY_NODE_ID      7                           \/\/ &lt;--- !!! SET NODE ADDRESS HERE !!!\n#define MY_RF24_CHANNEL 70                          \/\/ channel from 0 to 125. 76 is default\n\n#include \n#include \n#include                                 \/\/ https:\/\/github.com\/PaulStoffregen\/Time\n#include                       \/\/ biblioteka do komunikacji z czujnikami DS18B20\n#include                                 \/\/ biblioteka do komunikacji z czujnikami one wire\n\n#define CHILD_ID_AI1     1                          \/\/ AI1 B solar\n#define CHILD_ID_AI2     2                          \/\/ AI2 A battery\n#define CHILD_ID_AI3     3                          \/\/ AI3 PM 1.0\n#define CHILD_ID_AI4     4                          \/\/ AI4 PM 2.5\n#define CHILD_ID_AI5     5                          \/\/ AI5 PM 10\n#define CHILD_ID_AI6     6                          \/\/ AI6 Temp sensor DS18B20\n\nSoftwareSerial pms(pms_RX, 0);                           \/\/ Rx, Tx\nOneWire oneWire(ONE_WIRE_BUS);\nDallasTemperature sensors(&amp;oneWire);\n\nMyMessage msgAI1(CHILD_ID_AI1, V_VOLTAGE);\nMyMessage msgAI2(CHILD_ID_AI2, V_VOLTAGE);\nMyMessage msgAI3(CHILD_ID_AI3, V_LEVEL);\nMyMessage msgAI4(CHILD_ID_AI4, V_LEVEL);\nMyMessage msgAI5(CHILD_ID_AI5, V_LEVEL);\nMyMessage msgAI6(CHILD_ID_AI6, V_TEMP);             \/\/ AI6 Temperature\n\nint last_minute = 0;\nint last_second = 0;\nint ai1_raw = 0;\nint ai2_raw = 0;\nint PM_SP_UG_1_0, PM_SP_UG_2_5, PM_SP_UG_10_0;      \/\/ standard particles for indoor use\nint PM_AE_UG_1_0, PM_AE_UG_2_5, PM_AE_UG_10_0;      \/\/ atmospheric enviroment\nunsigned int weight = 10;                           \/\/ Max 64 min 1, (for optimization must be divide by 2) unsigned int = 0-65535, max from adc 1023 *64 = 65472 \nunsigned int ai1_avg = 0;\nunsigned int ai2_avg = 0;\nunsigned int pm1_avg = 0;\nunsigned int pm25_avg = 0;\nunsigned int pm10_avg = 0;\nfloat temp = 0.0;\nfloat temp_avg = 0.0;\n\nvoid receiveTime(uint32_t ts){\n  setTime(ts);  \n}\n\nbool pms_process(){\n  if (!pms.available()) {\n    return false;\n  }\n  uint8_t ch = pms.read();\n  static uint8_t dataIndex = 0;\n  static uint16_t checksum = 0;\n  static uint16_t calculatedChecksum = 0;\n  static uint16_t frameLen = 0;\n  static uint8_t payload[30];\n\n  switch (dataIndex) {\n    case 0:\n      if (ch != 0x42) {\n        return false;\n      }\n      calculatedChecksum = ch;\n      break;\n\n    case 1:\n      if (ch != 0x4D) {\n        dataIndex = 0;\n        return false;\n      }\n      calculatedChecksum += ch;\n      break;\n\n    case 2:\n      frameLen = ch &lt;&lt; 8;\n      calculatedChecksum += ch;\n      break;\n\n    case 3:\n      frameLen |= ch;\n      calculatedChecksum += ch;\n      break;\n\n    default:\n      if (dataIndex == frameLen + 2) {\n        checksum = ch &lt;&lt; 8;\n      } else if (dataIndex == frameLen + 2 + 1) {\n        checksum |= ch;\n\n        if (calculatedChecksum == checksum) {\n          PM_SP_UG_1_0 = (((uint16_t)payload[0]) &lt;&lt; 8) | payload[1];\n          PM_SP_UG_2_5 = (((uint16_t)payload[2]) &lt;&lt; 8) | payload[3];\n          PM_SP_UG_10_0 = (((uint16_t)payload[4]) &lt;&lt; 8) | payload[5];\n\n          PM_AE_UG_1_0 = (((uint16_t)payload[6]) &lt;&lt; 8) | payload[7];\n          PM_AE_UG_2_5 = (((uint16_t)payload[8]) &lt;&lt; 8) | payload[9];\n          PM_AE_UG_10_0 = (((uint16_t)payload[10]) &lt;&lt; 8) | payload[11];\n        } else {\n          \/\/Serial.println(\"Error checksum\");\n        }\n\n        dataIndex = 0;\n        return true;\n      } else {\n        calculatedChecksum += ch;\n        uint8_t payloadIndex = dataIndex - 4;\n\n        if (payloadIndex &lt; sizeof(payload)) {\n          payload[payloadIndex] = ch;\n        }\n      }\n  }\n  dataIndex++;\n  return false;\n}\n\nvoid presentation(){\n  char etykieta[] = \"       \";\n  int addr = MY_NODE_ID;   \n  sendSketchInfo(\"AS-500RVD\", \"1.2\");\n  sprintf(etykieta,\"R%02u.AI1\",addr);     present(CHILD_ID_AI1, S_MULTIMETER, etykieta);\n  sprintf(etykieta,\"R%02u.AI2\",addr);     present(CHILD_ID_AI2, S_MULTIMETER, etykieta);\n  sprintf(etykieta,\"R%02u.PM1\",addr);     present(CHILD_ID_AI3, S_DUST, etykieta); \n  sprintf(etykieta,\"R%02u.PM2.5\",addr);   present(CHILD_ID_AI4, S_DUST, etykieta);\n  sprintf(etykieta,\"R%02u.PM10\",addr);    present(CHILD_ID_AI5, S_DUST, etykieta); \n  sprintf(etykieta,\"R%02u.AI6\",addr);     present(CHILD_ID_AI6, S_TEMP, etykieta);\n}\n\nvoid setup(){  \n  pms.begin(9600);\n  analogReference(INTERNAL);\n  pinMode(ai1, INPUT);\n  pinMode(ai2, INPUT);\n  delay(1000);\n  ai1_avg = analogRead(ai1);\n  ai2_avg = analogRead(ai2);\n  while(pms_process() == false);                  \/\/ wait for data from PMS7003 dust sensor\n    pm1_avg = PM_AE_UG_1_0;\n    pm25_avg = PM_AE_UG_2_5;\n    pm10_avg = PM_AE_UG_10_0;    \n  requestTime();\n  \/\/sensors.begin();\n  \n  float ai1_vf = (ai1_avg * 0.019775549434047);  \/\/ B solar\n  float ai2_vf = (ai2_avg * 0.019930533806591);  \/\/ A battery  \n  int batteryPcnt = map(ai2_avg,530,683,20,100);\n  batteryPcnt = constrain(batteryPcnt,0,100);  \n\n  sensors.requestTemperatures();\n  temp = sensors.getTempCByIndex(0);\n  temp_avg = temp;\n  \n  send(msgAI1.set(ai1_vf, 2)); \n  send(msgAI2.set(ai2_vf, 2)); \n  send(msgAI3.set(pm1_avg, 0));\n  send(msgAI4.set(pm25_avg, 0));\n  send(msgAI5.set(pm10_avg, 0));\n  send(msgAI6.set(temp_avg, 1));\n  sendBatteryLevel(batteryPcnt);\n}\n\nvoid loop(){\n  \/\/filtering readings dust\n  if (pms_process()) {\n    pm1_avg = (PM_AE_UG_1_0*(weight-1) + pm1_avg) \/ weight;\n    pm25_avg = (PM_AE_UG_2_5*(weight-1) + pm25_avg) \/ weight;\n    pm10_avg = (PM_AE_UG_10_0*(weight-1) + pm10_avg) \/ weight;\n  }  \n  if (second() != last_second){\n    ai1_raw = analogRead(ai1);\n    ai2_raw = analogRead(ai2);\n    sensors.requestTemperatures();\n    temp = sensors.getTempCByIndex(0);\n    \n\n    \/\/ filtering readings voltage\n    ai1_avg = (ai1_avg*(weight-1) + ai1_raw) \/ weight;\n    ai2_avg = (ai2_avg*(weight-1) + ai2_raw) \/ weight;  \n    temp_avg =  (temp_avg*(weight-1) + temp) \/ weight;  \n    last_second = second(); \n  }\n  \n  if (minute() != last_minute){    \n    \/\/ ((1e6+470e3)\/470e3)*1.1 = Vmax\n    \/\/ Vmax\/1023 = Volts per bit = 0.0212\n    float ai1_vf = (ai1_avg * 0.019775549434047);  \/\/ B solar\n    float ai2_vf = (ai2_avg * 0.019930533806591);  \/\/ A battery\n\n    \/\/ calculating battery percent (ADC 519 = 10,5V, ADC 668 = 13,5V)\n    \/\/ accu: \n    \/\/ 13,5V = 100% ADC: 683 ---------\n    \/\/ 12,5V = 80%                    | \n    \/\/ 11,0V = 20%\n    \/\/ 10,5V = 0%   ADC: 530 -----    |\n    \/\/ 10,5V = 0% battery dead    |   |\n    int batteryPcnt = map(ai2_avg,530,683,20,100);\n    batteryPcnt = constrain(batteryPcnt,0,100);     \n    \n    send(msgAI1.set(ai1_vf, 2)); \n    send(msgAI2.set(ai2_vf, 2)); \n    send(msgAI3.set(pm1_avg, 0));\n    send(msgAI4.set(pm25_avg, 0));\n    send(msgAI5.set(pm10_avg, 0));\n    send(msgAI6.set(temp_avg, 1));\n    sendBatteryLevel(batteryPcnt);\n    \n    last_minute = minute();\n  }\n}\n<\/pre>\n<p>Czujnik wraz z arduino postanowi\u0142em zainstalowa\u0107 na balkonie w zwi\u0105zku z tym do kodu dorzuci\u0142em te\u017c odczyt temperatury, oraz dwa pomiary napi\u0119cia, kt\u00f3re wykorzystam do opomiarowania panela s\u0142onecznego.<br \/>\nOczywi\u015bcie przygotowa\u0142em te\u017c projekt estetycznej obudowy by ca\u0142y projekt zabezpieczy\u0107 przed przypadkowym uszkodzeniem. Rysunki przygotowa\u0142em w programie Fusion360. A nast\u0119pnie wydrukowa\u0142em na drukarce 3D.<\/p>\n<div id=\"attachment_2080\" style=\"width: 310px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/10.9.48.151\/?attachment_id=2080\" rel=\"attachment wp-att-2080\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-2080\" class=\"wp-image-2080 size-medium\" src=\"http:\/\/10.9.48.151\/wp-content\/uploads\/2021\/01\/photo_2021-01-09_20-37-05-300x164.jpg\" alt=\"\" width=\"300\" height=\"164\" srcset=\"https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/photo_2021-01-09_20-37-05-300x164.jpg 300w, https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/photo_2021-01-09_20-37-05-1024x560.jpg 1024w, https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/photo_2021-01-09_20-37-05-768x420.jpg 768w, https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/photo_2021-01-09_20-37-05.jpg 1280w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-2080\" class=\"wp-caption-text\">Okno programu Fusion 360<\/p><\/div>\n<p>Gotowa obudowa \u0142atwo pomie\u015bci\u0142a wszystkie niezb\u0119dne elementy.<\/p>\n<div id=\"attachment_2085\" style=\"width: 310px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/10.9.48.151\/?attachment_id=2085\" rel=\"attachment wp-att-2085\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-2085\" class=\"wp-image-2085 size-medium\" src=\"http:\/\/10.9.48.151\/wp-content\/uploads\/2021\/01\/photo_2021-01-07_14-51-18-300x150.jpg\" alt=\"\" width=\"300\" height=\"150\" srcset=\"https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/photo_2021-01-07_14-51-18-300x150.jpg 300w, https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/photo_2021-01-07_14-51-18-1024x512.jpg 1024w, https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/photo_2021-01-07_14-51-18-768x384.jpg 768w, https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/photo_2021-01-07_14-51-18.jpg 1280w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-2085\" class=\"wp-caption-text\">Czujnik w obudowie.<\/p><\/div>\n<p>Gotow\u0105 obudow\u0119 mo\u017cna \u015bci\u0105gn\u0105\u0107 z serwisu Thingiverse, pliki znajduj\u0105 si\u0119 pod tym adresem:<\/p>\n<p><a href=\"https:\/\/www.thingiverse.com\/thing:4716558\">https:\/\/www.thingiverse.com\/thing:4716558<\/a><\/p>\n<p>Po zamontowaniu \u015bcianek, frontowej i tylnej pod\u0142\u0105czy\u0142em uk\u0142ad do zasilania i obserwuj\u0119 w Domoticzu sp\u0142ywaj\u0105ce dane.<\/p>\n<div id=\"attachment_2089\" style=\"width: 310px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/10.9.48.151\/?attachment_id=2089\" rel=\"attachment wp-att-2089\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-2089\" class=\"wp-image-2089 size-medium\" src=\"http:\/\/10.9.48.151\/wp-content\/uploads\/2021\/01\/pomiarPMS7003-300x105.png\" alt=\"\" width=\"300\" height=\"105\" srcset=\"https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/pomiarPMS7003-300x105.png 300w, https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/pomiarPMS7003-1024x358.png 1024w, https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/pomiarPMS7003-768x269.png 768w, https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/pomiarPMS7003.png 1072w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-2089\" class=\"wp-caption-text\">Odczyt danych w domoticzu.<\/p><\/div>\n<p>Dane z Domoticza do systemu APRS przesy\u0142am za pomoc\u0105 skryptu WxDomoPY kt\u00f3ry mo\u017cna znale\u017a\u0107 na githubie pod tym adresem:<\/p>\n<p><a href=\"https:\/\/github.com\/SQ9MDD\/WXDomoPy\">https:\/\/github.com\/SQ9MDD\/WXDomoPy<\/a><\/p>\n<p><a href=\"http:\/\/10.9.48.151\/?attachment_id=2092\" rel=\"attachment wp-att-2092\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-2092\" src=\"http:\/\/10.9.48.151\/wp-content\/uploads\/2021\/01\/pomiarPMS-w-APRS-300x135.png\" alt=\"\" width=\"300\" height=\"135\" srcset=\"https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/pomiarPMS-w-APRS-300x135.png 300w, https:\/\/sq9mdd.hamspirit.pl\/wp-content\/uploads\/2021\/01\/pomiarPMS-w-APRS.png 381w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>R.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Po wielu testach r\u00f3\u017cnych czujnik\u00f3w, chyba w ko\u0144cu znalaz\u0142em ca\u0142kiem sensowny czujnik zanieczyszczenia powietrza. Urz\u0105dzenie nazywa si\u0119 Plantower PMS7003 i mo\u017cna je kupi\u0107 w ca\u0142kiem rozs\u0105dnej cenie.<\/p>\n","protected":false},"author":2,"featured_media":2081,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,6,10,11],"tags":[],"class_list":["post-2077","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aprs","category-arduino","category-diy","category-domoticz"],"_links":{"self":[{"href":"https:\/\/sq9mdd.hamspirit.pl\/index.php?rest_route=\/wp\/v2\/posts\/2077","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sq9mdd.hamspirit.pl\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sq9mdd.hamspirit.pl\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sq9mdd.hamspirit.pl\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/sq9mdd.hamspirit.pl\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2077"}],"version-history":[{"count":0,"href":"https:\/\/sq9mdd.hamspirit.pl\/index.php?rest_route=\/wp\/v2\/posts\/2077\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sq9mdd.hamspirit.pl\/index.php?rest_route=\/wp\/v2\/media\/2081"}],"wp:attachment":[{"href":"https:\/\/sq9mdd.hamspirit.pl\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2077"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sq9mdd.hamspirit.pl\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2077"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sq9mdd.hamspirit.pl\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2077"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}