Zbliża się zima więc znów w mediach będzie głośno o panującym nad Polską smogu. Ponadto władza chyba nie ma zamiaru odchodzić od węgla, więc trzeba jakoś sobie radzić z zanieczyszczeniami. W związku z tym popularne w ostatnich latach są czujniki mierzące jakość powietrza i dzisiaj takim się zajmę.
Najważniejszymi parametrami, które powinien mierzyć taki czujnik są wartości PM10 i PM2,5. PM10 mówi o wielkości zapylenia powietrza cząsteczkami o średnicy mniejszej niż 10 µm. Analogicznie PM2,5 mówi o ilości cząsteczek zawartych w powietrzu o średnicy poniżej 2,5 µm. Wielkości te wydają się na prawdę małe i tak faktycznie jest bo PM2,5 jest w stanie przedostać się do najgłębszych zakamarków płuc, a w związku z tym przedostaje się do krwioobiegu. Jak zwykle na takie parametry są normy dobowe. Dla PM10 zdefiniowano 3 poziomy:
- Dopuszczalny – 50 µg/m³
- Informowania- 200 µg/m³
- Alarmowy- 300 µg/m³
Dla pyłu PM2,5 jest określony tylko jeden dopuszczalny limit średni-roczny 25 µg/m³. Źródło.
Pomiar
Profesjonalne metody pomiarów polegają na zasysaniu na bieżąco mierzonej ilości powietrza i podawaniu go na specjalne filtry, które wyłapują odpowiednie cząsteczki. Póżniej te filtry są ważone i na tej podstawie ustalana jest wartość zapylenia. Jest to metoda dosyć dokładna jednak wymaga drogiego sprzętu i zaangażowania.
Istnieją na rynku mniej wymagające metody – pomiar przy pomocy lasera. Są one mniej dokładne, ale za to łatwe w zastosowaniu. Takie rozwiązanie idealnie sprawdza się na co dzień w domowych warunkach.
Czujnik SDS011
Jednym z takich tanich, laserowych czujników jest SDS011 firmy Nova Fitness. Kosztuje on około 150 zł w Polsce. W porównaniu do innej elektroniki jest stosunkowo duży bo 71x70x26 mm. Jednak posiada on wszystko co jest mu potrzebne do łatwego odczytu pomiarów przez interfejs – wentylator, tunel powietrzny, mikrokontroler oraz oczywiście laser.
Do pracy wymaga zasilania 5V, ale zestawy Nucleo na szczęście mają taki pin. Pobór prądu w stanie aktywnym jest dosyć spory bo 70 mA co przy zasilaniu bateryjnym będzie miało marne skutki. Na szczęście posiada tryb uśpienia w którym apetyt na zasilanie spada poniżej 4 mA. Czujnik ten mierzy obydwie interesujące nas wartości – PM10 i PM2,5 – w zakresie 0÷999,9 µg/m³ z maksymalną częstotliwością 1 pomiar na sekundę. Minimalne cząsteczki, które potrafi wykryć to 0,3 µm a pomiaru dokonuje z dokładnością 15% lecz nie gorzej niż ±10 µm.
Niestety deklarowana przez producenta żywotność lasera wynosi jedynie 8000 godzin ciągłej pracy. Na szczęście dał do dyspozycji wspomniany wcześniej tryb śpiący, więc można czas życia zdecydowanie wydłużyć.
Czujnik SDS011 potrafi komunikować się ze światem na dwa sposoby. Pierwszy i zdecydowanie najpopularniejszy to UART. Drugim jest PWM. Sprawdzę działanie obydwu.
Więcej informacji znajdziesz w dokumentacji SDS011 Datasheet .
Schemat i konfiguracja
Jako, że czujniki takie wykorzystwane są często do badania warunków zewnętrznych, istnieje potrzeba zasilić go bateriami lub panalem słonecznym. Stąd wymagany będzie mikrokontroler z niskim zapotrzebowaniem na prąd. Świetnie sprawdzi się tutaj STM32L053R8 który jest w jednym z zestawów Nucleo i taki też użyłem.
Czujnik będzie podłączony do UART1. Drugi interfejs szeregory wykorzystam do printowania na terminal. Piny pod odczyt z PWM ustawiłem jako wejścia kanałów 1 i 2 Timera 2. Konfiguracja UART na potrzeby czujnika to 9600 8n1. Dodatkowo odblokowałem przerwania od UART1, TIM2 i załączyłem w NVIC podczas inicjalizacji HAL.
UART
Protokół komunikacji po UART został opisany przez producenta czujnika w dokumencie: SDS011 UART Protocol . W domyślnym trybie zaraz po uruchomieniu czujnik zacznie wysyłać co sekndę ramkę z danymi dotyczącymi pomiarów. Najprościej byłoby po prostu napisać samą obsługę tej ramki. Lecz pamiętaj, że życie diody laserowej ograniczone jest jedynie do 8000 godzin. Aby to wydłużyć musimy posłużyć się komendami UART, które uśpią czujnik a przy tym wyłączą diodę.
Ramka danych otrzymywanych z czujnika zawsze jest długości 10 bajtów. Kiedy zmienia się ustawienia czujnika również odsyła potwierdzenie o długości 10 bajtów. Ułatwia to parsowanie, ale ja nie zdecydowałem się na parsowanie potwierdzeń komend. Być może dopiszę później jeśli okaże się, że czujnik nie zachowuje się tak, jakbym chciał.
Jeśli chodzi o komendy to według mnie godne poświęcenia czasu są dwie: wprowadzenie czujnika w sen(oraz oczywiście budzenie) oraz ustawienie cyklu pracy. O ile działanie funkcji sleep można sobie łatwo wyobrazić tak czym jest ten cykl pracy?
Otóż czujnik może sam wchodzić w sleep i budzić się bez ingerencji MCU. Ustawia się czas trwania uśpienia w zakresie 0-30 minut. Zero oznacza standardową pracę z wysyłaniem danych co sekundę. Ustawienie Innej wartości mówi czujnikowi, że ma pracować 30 sekund, wysłać tylko raz zmierzone dane po czym pójść spać na ustawione właśnie 1-30 minut. Brzmi świetnie.
Dlaczego wysyła dane tylko raz w ciągu tych 30 sekund? Otóż zauważyłem, że wchodzenie ręcznie w sleep i późniejsze uruchomienie czujnika wprowadza błędy pomiaru. Czujnik w pierwszych chwilach pokazuje wartości bardzo zaniżone. Zapewne algorytm zliczania ilości pyłu potrzebuje trochę czasu na samokalibrację. Dlatego też w trybie z automatycznymi pauzami czujnik chodzi 30 sekund i dopiero po tym czasie wysyła wynik pomiarów. Sprytne, co nie? Jeśli chciałbyś usypiać na dłuższy czas, musisz użyć zwykłego sleepa, lecz pamiętaj o zaniżeniu pomiaru przez pierwsze chwile od wybudzenia. Przejdźmy do funkcji
W pliku nagłówkowym należy najpierw odkomentować definicję
#define SDS011_UART
Pierwszą funkcją jest oczywiście inicjalizacja. Przypisuje ona wskaźnik do UART’a, ustawia tryb pracy czujnika na ciągły. oraz aktywuje przerwanie po otrzymaniu kompletnej ramki z układu.
SDS011_SATUS Sds011_InitUart(UART_HandleTypeDef *huart);
Kolejne funkcje odpowiadają za konfigurację SDS011. Ustawienie cyklu pracy, ręczne uspanie czujnika oraz wybudzenie.
SDS011_SATUS Sds011_SetWorkingPeriod(uint8_t Period); SDS011_SATUS Sds011_SetSleepMode(void); SDS011_SATUS Sds011_WakeUp(void);
Ważnym elementem jest obsługa przerwania. Po nadejściu całej ramki, wywoływane jest odpowiednie przerwanie, w którym musi być wywłoana funkcja:
void Sds011_UartRxCpltCallback(UART_HandleTypeDef *huart);
Wklej ją do funkcji _weak oferowanej przez HAL.
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { #ifdef SDS011_UART Sds011_UartRxCpltCallback(huart); #endif }
Pozostaje odczyt ostatniej wartości przekazanej przez czujnik. Służą do tego dwie funkcje.
SDS011_SATUS Sds011_GetPm2_5(uint16_t *Value); SDS011_SATUS Sds011_GetPm10(uint16_t *Value);
Efekt działania wypisałem na terminalu.
PWM
Drugim sposobem komunikacji z czujnikiem jest PWM. Niestety jest to komunikacja jedno kierunkowa i nie ma możliwości zmian trybu pracy czujnika. Jedyne zastosowanie jakie widzę dla takiego rozwiązania to miejsce, gdzie wymagany jest ciągły pomiar, a nie mamy akurat wolnego UARTa. Nie mniej jednak uznałem, że jest to świetny trening umiejętności programistycznych. Okres PWM wynosi aż 1004 ms, czyli jest to ok 1 Hz. Dosyć wolny sygnał do pomiarów PWM. Do tego STM32L053 może mierzyć tylko jeden PWM na raz.
Dlatego wykorzystałem tryb Input Capture w drugim timerze mierząc jedynie czas trwania stanu wysokiego. Tryb ten reaguje na wybrane zbocze(lub dwa) i w momencie jego wystąpienia zatrzaskuje wartość licznika przy której to nastąpiło. Pomiar podzieliłem na 3 etapy.
Pierwszy punkt to pojawienie się zbocza narastającego na jednym z sygnałów – wybrałem PM10. W tym momencie zerowany jest timer, zmieniam reakcje przerwania na zbocze na opadające i ponownie startuję przerwanie. W tym momencie licznik już tyka.
Drugi przystanek to pojawienie się pierwszego zbocza opadającego. Nigy nie wiadomo które zbocze pojawi się pierwsze. Nie dokonuję tutaj żadnych obliczeń czy odczytów gdyż wartość licznika jest już bezpiecznie zatrzaśnięta w odpowiednim rejestrze. Czekam na ostatnie zbocze.
Trzeci punkt to pojawienie się drugiego zbocza. W tym momencie przywracam ustawienie reakcji przerwania PM10 na zbocze narastające szykując się do kolejnego odczytu. Później zbieram wartości z zatrzaśniętych rejestrów licznika i ponownie startuję obydwa przerwania. Na koniec ustawiam zmienną stanu obsługi przerwania na stan początkowy.
Cykl się powtarza co sekundę, kiedy to nadchodzą nowe dane. Kod przerwania możesz sobie przeanalizować poniżej:
void Sds011_TimerCaptureCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == htim_sds->Instance) { static uint8_t state = 0; if(state == 0) // Rising edge - begin { // Clear counter __HAL_TIM_SetCounter(htim_sds, 0); // Set edge to falling htim->Instance->CCER |= (1<<(Pm10_TimChannel_sds+1)); HAL_TIM_IC_Start_IT(htim_sds, Pm10_TimChannel_sds); // Change state state = 1; } else if(state == 1) // First falling edge { // Change state only and wait for second edge state = 2; } else // Second falling edge { // Set edge back to Rising htim->Instance->CCER &= ~(1<<(Pm10_TimChannel_sds+1)); // Get values Pm10 = __HAL_TIM_GetCompare(htim_sds, Pm10_TimChannel_sds) - 2; Pm2_5 = __HAL_TIM_GetCompare(htim_sds, Pm2_5_TimChannel_sds) - 2; // Enable Interrupts HAL_TIM_IC_Start_IT(htim_sds, Pm10_TimChannel_sds); HAL_TIM_IC_Start_IT(htim_sds, Pm2_5_TimChannel_sds); // Back to beginning - wait for next data state = 0; } } }
Trybu PWM możesz używać po odkomentowaniu w pliku nagłówkowym
#define SDS011_PWM
Podsumowanie
Czujnik zanieczyszczeń powietrza SDS011 jest bardzo ciekawym urządzeniem. Z pewnością można go zastosować do przydomowej stacji pogodowej, aby wiedzieć czy wyjść z domu nie narażając się na zapylenie. Sam z pewnością wystawię czujnik za okno.
Odbiór danych PWM osobiście traktowałbym jako ciekawostkę. Dużo lepiej skorzystać z dobrodziejstw usypiania czujnika jakie niesie ze sobą UART. Trzeba bowiem pamiętać, że zastosowany w czujniku laser nie jest trwały.
Dziękuję Ci za przeczytanie tego wpisu. Jeśli taka tematyka Ci odpowiada, daj mi znać w komentarzu. Będę też wdzięczny za propozycję tematów które chciałbyś abym poruszył.
Kod standardowo dostępny jest na moim GitHubie: link
Jeśli zauważyłeś jakiś błąd, nie zgadzasz się z czymś, chciałbyś coś dodać istotnego lub po prostu uważasz, że chciałbyś podystkutować w tym temacie, napisz komentarz. Pamiętaj, że dyskusja ma być kulturalna i zgodna z zasadami języka polskiego.
2 komentarze
Mivhał · 10/03/2019 o 11:22
Super artykuł przyjemnie się go czyta dobra robota 😉
Mateusz · 10/03/2019 o 11:29
Dzięki! Polecam poczytać również inne 🙂