[Aktualizacja 08-02-2018] Czujnik pyłu zawieszonego – Mierzymy SMOG 2,5 oraz 10μm

[Aktualizacja 08-02-2018] Czujnik pyłu zawieszonego – Mierzymy SMOG 2,5 oraz 10μm

Ostatnie zmiany:
07-11-2017: Przykłady pobierania danych z zaawansowanych stacji, Integracja ze Open-Smog, promocja na czujnik
20-12-2017: Kontrola zasilania USB na Raspberry Pi 3, dzięki czytelnikowi
03-02-2018: Nowe API od GIOŚ Poland
08-02-2018: Odnajdywanie ID

SMOG stał się nieodłączną częścią życia w dużych miastach. Postarajmy się więc zbudować wiarygodny, własny czujnik SMOGu – na początek cząsteczek o wielkości 2,5 oraz 10 μm. Pewnym problemem staje się wybór czujnika, ponieważ niełatwo znaleźć taki, który łączy w sobie długą żywotność i precyzję pomiaru z przystępną ceną. W końcu – po zasięgnięciu opinii – zdecydowałem się na czujnik cząsteczek – Nova Fitness SDS011:

  • zadowalająca precyzja pomiaru
  • wentylator
  • możliwość zamontowania wężyka
  • duża trwałość
  • wbudowany interfejs UART, wraz z konwerterem USB
  • Niska cena całego rozwiązania: od około $40 przy użyciu Orange Pi Zero – do $60 jeśli używamy Raspberry Pi

Czego będziemy potrzebować?

Hardware:

  • Raspberry Pi lub klon (Orange Pi) z wolnym portem USB
  • czujnik Nova Fitness SDS011 w wersji z konwerterem USB-UART, możesz znaleźć najtaniej na Aliexpress.

    Software:

  • ulubiona dystrybucja Raspbian lub Armbian dla klonów
  • podstawowe programy występujące jako opcje w dystrybucji Linuksa

    Budowa, konfiguracja oraz wykresy

    Obraz1
    Urządzenie podłączamy możliwie krótkim (kilka cm) wężykiem, tak aby wlot pobierał z zewnątrz powietrze. Długi wężyk – 100cm jak na pierwszym zdjęciu – znacznie obniża rezultaty – zaniża pomiar.
    P2190103
    Pamiętajmy także, że czujnik pracuje cały czas, więc być może warto spróbować załączać go tylko na czas pomiaru. W wersji pracującej u mnie – wybrałem dość proste rozwiązanie – wyłączam zasilanie po pomiarze, a przed pomiarem – załączam i czekam 60 sekund.
    czujnik_na_dachu
    Kolejna ważna kwestia – czujnik staje się niewiarygodny, gdy wilgotność przekracza 70%, pamiętajmy o tym!
    Najpierw zainstalujmy bc – potrzebny do obliczeń:

    apt update
    apt upgrade
    apt install bc git

    Nie zapominajmy, że to pierwsza wersja naszego czujnika – i jak się domyślacie – będziemy modyfikować go pod kątem pomiarów wilgotności.
    Komunikacja z urządzeniem jest bardzo prosta – po umieszczeniu w porcie USB otrzymujemy /dev/ttyUSB0 – z którego czytamy tak samo jak z innego interfejsu szeregowego:

    /bin/stty -F /dev/ttyUSB0 9600 raw
    /usr/bin/od --endian=big -x -N10 < /dev/ttyUSB0

    Po odczytaniu – sprawdzamy czy linia zaczyna się od „aac0”. Dane podawane szesnastkowo, little endian – a więc finalną wartość należy wyliczyć. Cały kod, łącznie z aktualizowaniem bazy Influx do rysowania w Grafanie przedstawia się następująco, mam nadzieję, że jest niezwykle przejrzysty:

Wersja dla dowolnego komputera, testowana na Orange Pi:

#!/bin/bash
# Czytnik z SDS011, zakładamy port szeregowy na /dev/ttyUSB0
# Bazuje na: http://kuehnast.com/s9y/archives/633-Feinstaubmessung_mit_dem_Raspberry_Pi.html
# [email protected], 2017, NO WARRANTY, GPL v2
#
# Zmienne - jeśli trzeba należy zmienić
serial_port="/dev/ttyUSB0"
#Włączamy port USB - zasilanie - dotyczy WYŁĄCZNIE Orange Pi (A20)
/usr/bin/sunxi-pio -m PH26''
#Czekamy aż załączy się wentylator i ustablizuje przepływ powietrza
sleep 60
#Główny program
#Ustawiamy tryb portu szeregowego
/bin/stty -F $serial_port 9600 raw
#Czytamy dane - big-endian, szenastkowe
RAW_DATA=`/usr/bin/od --endian=big -x -N10 < /dev/ttyUSB0 | /usr/bin/head -n 1 | /usr/bin/cut -f2-10 -d" "`
HEADER=`echo $RAW_DATA | /usr/bin/awk '{print $1}'`
#Probe for propper header
if [ "$HEADER" = "aac0" ];
 then
  #Let us cut the RAW DATA and put it into variables - data is in hexadecimals
  HEX_PPM25_L=$(echo $RAW_DATA|cut -f2 -d " "|cut -b1-2);
  HEX_PPM25_H=$(echo $RAW_DATA|cut -f2 -d " "|cut -b3-4);
  HEX_PPM10_L=$(echo $RAW_DATA|cut -f3 -d " "|cut -b1-2);
  HEX_PPM10_H=$(echo $RAW_DATA|cut -f3 -d " "|cut -b3-4);
  #Convert variables to decimals
  PPM25_L=$(echo $((0x$HEX_PPM25_L)));
  PPM25_H=$(echo $((0x$HEX_PPM25_H)));
  PPM10_L=$(echo $((0x$HEX_PPM10_L)));
  PPM10_H=$(echo $((0x$HEX_PPM10_H)));
  #More simple math
  PPM25=`echo "((${PPM25_H}*256)+${PPM25_L})/10" | bc`
  PPM10=`echo "((${PPM10_H}*256)+${PPM10_L})/10" | bc`
  #Update the local InfluxDB
  /usr/bin/curl -i -XPOST 'http://127.0.0.1:8086/write?db=smogdb' --data-binary "ppm25sds011 value=${PPM25}"
  /usr/bin/curl -i -XPOST 'http://127.0.0.1:8086/write?db=smogdb' --data-binary "ppm10sds011 value=${PPM10}"
 else
  echo HEADER ERROR
 fi
#Turining OFF the USB ports for Orange Pi
/usr/bin/sunxi-pio -m PH26''

Wersja dla Raspberry Pi 3

Dzięki czytelnikowi bloga – Piotrek Pilek, pozdrowienia, mamy sposób na identyczną kontrolę zasilania USB! (przy okazji – Piotr pracuje w Lantech w Szczecinie – popatrzcie na to niedocenione miasto – https://lantech.com.pl/internet_szczecin_oferta/transmisje-live/)
Kontrolę zasilania USB możemy uzyskać dzięki programowi Vadim Mikhailov, który stworzył oprogramowanie dla kilkunastu róznych hubów USB wraz z obsługą Raspberry Pi. Program instalujemy:

cd ~
sudo apt install libusb-1.0 libusb-dev
git clone https://github.com/mvp/uhubctl
cd uhubctl
make
sudo make install

Następnie modyfikujemy linie w powyższym przykładzie:

/usr/bin/sunxi-pio -m PH26''

na

/usr/sbin/uhubctl -a off -p 2

A następnie analogicznie na końcu pliku:

/usr/bin/sunxi-pio -m PH26''

na

/usr/sbin/uhubctl -a on -p 2

Uwaga – wyłącza to wszystkie porty USB, ale nie wyłącza portu eth0 ani też wlan0

Rysujemy!

Teraz – podobnie jak wcześniej – rysujemy dane w Grafanie, definicje wyglądają następująco:

SELECT last("value") FROM "ppm25sds011" WHERE $timeFilter GROUP BY time(1m) fill(none)
SELECT last("value") FROM "ppm10sds011" WHERE $timeFilter GROUP BY time(1m) fill(none)

sds011-data-plot
To wszystko!

Dzielenie się danymi, wysyłanie do innych systemów. Czy masz własny projekt?

Nasze dane od smogu są dostępne i bardzo łatwo możemy się nim podzielić. Jeśli prowadzisz własny projekt agregujacy dane – napisz proszę komenatarz lub emaila – dopiszę sposób w jaki można wysłać do konkretnych projektów dane.

Integracja z OpenSmog

Open-Smog to projekt zapoczątkowany jako realizacja pomysłu Artura Kurasińskiego. Po szczegóły zapraszam na Slacka: https://open-smog.slack.com/
Integracja sprowadza się do dodania do skryptu na końcu:

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d "[ { \"data\": { \"pm2_5\": ${PPM25}, \"pm10\": ${PPM10}, \"temp\": ${temperature}, \"hum\": ${humidity} } } ]" 'http://ADRES_WWW_OPENSMOG/v1/Sensors/TWOJEID/data'

Obiecywałem, że będzie prosto, prawda ?
Pomiary, aktualizowane co 60 minut, możecie śledzić pod adresem http://pogoda.jokielowie.com/

Pobieranie danych i weryfikacja ze stacjami WIOŚ

Z pewnością każdego interesuje jak dokładny jest taki czujnik. Możemy bardzo szybko dodać do naszego skryptu dane z oficjalnych stacji WIOŚ: mapa znajduje się pod adresem: http://powietrze.gios.gov.pl/pjp/current. Wybieramy interesującą nas stację – w przykładzie będzie to stacja na ul. Koszyka w Opolu, która ma adres: http://powietrze.gios.gov.pl/pjp/current/station_details/chart/10374. Jak zauważył Krzysztof Styc na grupie Domoticz – WIOŚie podają aktualne dane (ostatni pomiar) w formacie JSON. Pod koniec 2017 uległo to jednak zmianie i udostępniono nowe API pod nowym adresem, także w formacie JSON: http://powietrze.gios.gov.pl/pjp/content/api
To pozwala nam szybko wyciągnąć nową zmienną i wpisać ją do bazy InfluxDB – dodając kilka linijek do naszego skryptu. Najpierw musimy jednak znaleźć ID naszej stacji:

http://api.gios.gov.pl/pjp-api/rest/station/findAll

Szukamy np: Opola i znajdujemy StationID:
gios1

Mając StationID – wpisujemy je w URL, który pokaże konkretne pomiary:
http://api.gios.gov.pl/pjp-api/rest/station/sensors/10374
gios2

A więc mamy nasze ID do pomiarów:
http://api.gios.gov.pl/pjp-api/rest/data/getData/16147
gios3

Modyfikujemy skrypt:

city_wios_station=`curl -s http://api.gios.gov.pl/pjp-api/rest/data/getData/16147 | awk -F, '{print $3}' | sed -e 's/}/:/g' | awk -F: '{print $2}'`
if [ $city_wios_station = "null" ] ; then
  city_wios_station=`curl -s http://api.gios.gov.pl/pjp-api/rest/data/getData/16147 | awk -F, '{print $5}' | sed -e 's/}/:/g' | awk -F: '{print $2}'`
fi
/usr/bin/curl -i -XPOST 'http://127.0.0.1:8086/write?db=smogdb' --data-binary "ppm10_city_wios_station value=${city_wios_station}"

Oczywiście dla stacji podających więcej danych – należy zmodyfikować nieco kod. Najpierw trzeba znaleźć ID jak powyżej.
Po modyfikacji (przykłady bez optymizacji) możemy pobrać więcej zmiennych, co wymaga jak zwykle małej zabawy z parametrami.
Na wykresie pojawi się więc dodatkowa zmienna – lub zmienne, dzięki której zweryfikujemy nasze wskazania. Dla gazów warto zrobić osobny wykres.

Wskaźniki – zegary – aktualne wartości na stronie WWW

Informację o aktualnych wartościach ilości cząsteczek w metrze sześciennym możemy także prezentować w formie wskaźników lub zegarów. Z pomocą przychodzi gotowa bilblioteka w JavaScript, która współpracuje bezproblemowo z większością przeglądarek: http://justgage.com/:
smog-gauges
Pobieramy ZIP „justgauge”, rozpakowujemy i przygotowujemy naszą surową stronę, w przykładzie będziemy jeszcze pobierać dane z bazy InfluxDB – temperaturę i wilgotność, tak abyśmy wiedzieli czy nasz pomiar jest wiarygodny. Surowa wersja strony:

Stacja pogodowa/Weather station

Stacja pogodowa SMOG w ... /SMOG Weather Station in ...
<div id="zegar1"></div>
<div id="zegar2"></div>
<div id="zegar3"></div>
<div id="zegar4"></div>
<script src="raphael-2.1.4.min.js"></script>
<script src="justgage.js"></script>
<script>
      var zegar1, zegar2, zegar3, zegar4;
      window.onload = function(){
        var zegar1 = new JustGage({
          id: "zegar1",
          value: __ppm25__,
          min: 0,
          max: 300,
          title: "Cząsteczki 2,5µm",
          label: "µg/m^3"
        });
        var zegar2 = new JustGage({
          id: "zegar2",
          value: __ppm10__,
          min: 0,
          max: 300,
          title: "Cząsteczki 10µm",
          label: "µg/m^3"
        });
        var zegar3 = new JustGage({
          id: "zegar3",
          value: __temperature__,
          min: -40,
          max: 60,
          title: "Temperatura",
          label: "st C"
        });
        var zegar4 = new JustGage({
          id: "zegar4",
          value: __humidity__,
          min: 0,
          max: 100,
          title: "Wilgotność",
          label: "%"
        });
      };
    </script>

Zapiszmy plik jako raw-index.html
W głównym skrypcie mamy już zmienną która odpowiada na 2,5 i 10. Pobierzmy więc – dodają te line do skryptu – wilgotność i temperaturę. W InfluxDB są to w moim przykładzie zmienne „temperatura_out” oraz „wilgotnosc_out” – zmieniamy je na własne, podobnie jak adres IP i nazwę bazy danych – oczywiście dołączamy do głównego skryptu:

temperature=`/usr/bin/curl -s -G 'http://127.0.0.1:8086/query' --data-urlencode "db=ZMIEN_MNIE" --data-urlencode "q=SELECT last(\"value\") FROM \"temperatura_out\"" | /bin/sed -e 's/[{}]/''/g' | /usr//bin/awk -v k="text" '{n=split($0,a,","); print a[n]}' | tr -d "[\]]"`
humidity=`/usr/bin/curl -s -G 'http://127.0.0.1:8086/query' --data-urlencode "db=ZMIEN_MNIE" --data-urlencode "q=SELECT last(\"value\") FROM \"wilgotnosc_out\"" | /bin/sed -e 's/[{}]/''/g' | /usr//bin/awk -v k="text" '{n=split($0,a,","); print a[n]}' | tr -d "[\]]"`

Gotowe – zatem wymieńmy odpowiednie wartości w naszym pliku i stwórzmy nowy:

sed -e "s/__temperature__/${temperature}/g" -e "s/__humidity__/${humidity}/g" -e "s/__ppm25__/${PPM25}/g" -e "s/__ppm10__/${PPM10}/g" < raw-index.html > index.html

Plik index.html wysyłamy do naszego hostingu na przykład tak, oczywiście wcześniej wykonując „ssh keygen” oraz „ssh-copy-id”

scp index.html [email protected]:/var/www/pogoda.host.com/html/index.html

Każde uruchomienie skryptu zaktualizuje nam plik!

Previous Post Next Post