Moduł BME280 |
Jak widać ze zdjęcia, na tej samej płytce producent modułu montuje BME280 lub BMP280 (wersja bez pomiaru wilgotności). Co więcej, nie miałem oznaczonej wersji czujnika (puste białe pola/kwadraciki). Jednak przykładowy kod (podany poniżej) wzięty z użytej biblioteki (2) obsługującej ten czujnik, wykrył BME280. Widać to na screenie z terminala portu szeregowego (zamieszczonym na końcu posta).
Specyfikacja
- interfejs I²C i SPI
- napięcie interfejsu max 3,9V (moduł ma wbudowany konwerter poziomów i umożliwia pracę z napięciem 5V)
- domyślny adres I²C zależy od stanu na pinie SDO:
- stan niski 0x76
- stan wysoki 0x77
- napięcie zasilania 1,2 do 3,6V. Sam moduł może być zasilany do 5V (wbudowany regulator napięcia 662K)
- pobór prądu 3,6uA
- parametry pracy -40…+85 °C, 0…100 %RH, 300…1100 hPa
Druga strona modułu |
Schemat elektryczny ze strony sprzedawcy |
Do uruchomienia układu posłużył mi tradycyjnie przykład Environment_Calculations.ino z biblioteki finitespace/BME280 (2). Pierwsza zmiana, to szybkość transferu szeregowego na 9600 (stała SERIAL_BAUD). Taką wartość akurat mam ustawioną w terminalu używanego Atmel Studio. Przykład przeredagowałem wydzielając wyznaczanie parametrów powietrza przez czujnik do funkcji printBasicData(), natomiast pozostałe obliczenia środowiskowe do funkcji printEnvironmentCalculations(). Poprawiłem też czytelność wyników prezentowanych na terminalu, dodając jeden wiersz nagłówków (funkcja printHeader()). Nawiasem mówiąc funkcja Altitude() zwracała jakieś kosmiczne wyniki, ale pomogła zmiana wartości referencePressure z hPa na Pa (obliczenia w tej funkcji są w Pa-skalach).
Schemat blokowy połączeń |
/* Connecting the BME280 Sensor: Sensor -> Board ----------------------------- Vin (Voltage In) -> 5V Gnd (Ground) -> Gnd SDA (Serial Data) -> A4 on Uno/Pro-Mini, 20 on Mega2560/Due, 2 Leonardo/Pro-Micro SCK (Serial Clock) -> A5 on Uno/Pro-Mini, 21 on Mega2560/Due, 3 Leonardo/Pro-Micro */ #include <BME280I2C.h> #include <EnvironmentCalculations.h> #include <Wire.h> #define SERIAL_BAUD 9600 BME280I2C bme; // Default : forced mode, standby time = 1000 ms // Oversampling = pressure ×1, temperature ×1, humidity ×1, filter off, float temp(NAN), hum(NAN), pres(NAN); // Assumed environmental values: float referencePressure = 101900; // Pa local QFF (official meteor-station reading) float outdoorTemp = 10.0; // °C measured local outdoor temp. float barometerAltitude = 80; // meters ... map readings + barometer position BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); BME280::PresUnit presUnit(BME280::PresUnit_Pa); EnvironmentCalculations::AltitudeUnit envAltUnit = EnvironmentCalculations::AltitudeUnit_Meters; EnvironmentCalculations::TempUnit envTempUnit = EnvironmentCalculations::TempUnit_Celsius; void setup() { Serial.begin(SERIAL_BAUD); while (!Serial) {} // Wait Wire.begin(); while (!bme.begin()) { Serial.println("Could not find BME280 sensor!"); delay(1000); } switch (bme.chipModel()) { case BME280::ChipModel_BME280: Serial.println("Found BME280 sensor! Success."); break; case BME280::ChipModel_BMP280: Serial.println("Found BMP280 sensor! No Humidity available."); break; default: Serial.println("Found UNKNOWN sensor! Error!"); } Serial.print("Assumed outdoor temperature: "); Serial.print(outdoorTemp); Serial.write(0xC2); Serial.write(0xB0); Serial.print("C\nAssumed reduced sea level Pressure: "); Serial.print(referencePressure); Serial.print("Pa\nAssumed barometer altitude: "); Serial.print(barometerAltitude); Serial.println("m\n***************************************"); printHeader(&Serial); } void loop() { printBasicData(&Serial); printEnvironmentCalculations(&Serial); delay(5000); } void printHeader(Stream* client) { client->print("Temp: "); client->print("\t\tHumidity: "); client->print("\tPressure: "); client->print("\tAltitude: "); client->print("\tDew point: "); client->print("\tEquivalent Sea Level Pressure: "); client->print("\tHeat Index: "); client->println("\tAbsolute Humidity: "); } void printBasicData(Stream* client) { bme.read(pres, temp, hum, tempUnit, presUnit); //Temperature client->print(temp); client->write(0xC2); client->write(0xB0); client->print(String(tempUnit == BME280::TempUnit_Celsius ? 'C' :'F')); client->print("\t\t"); //Humidity client->print(hum); client->print("% RH"); client->print("\t"); //Pressure client->print(pres); client->print("Pa"); client->print("\t"); } void printEnvironmentCalculations(Stream* client) { float altitude = EnvironmentCalculations::Altitude(pres, envAltUnit, referencePressure, outdoorTemp, envTempUnit); float dewPoint = EnvironmentCalculations::DewPoint(temp, hum, envTempUnit); float seaLevel = EnvironmentCalculations::EquivalentSeaLevelPressure(barometerAltitude, temp, pres, envAltUnit, envTempUnit); float absHum = EnvironmentCalculations::AbsoluteHumidity(temp, hum, envTempUnit); float heatIndex = EnvironmentCalculations::HeatIndex(temp, hum, envTempUnit); //Altitude client->print(altitude); client->print((envAltUnit == EnvironmentCalculations::AltitudeUnit_Meters ? "m" : "ft")); client->print("\t\t"); //Dew point client->print(dewPoint); client->write(0xC2); client->write(0xB0); client->print(String(envTempUnit == EnvironmentCalculations::TempUnit_Celsius ? "C" :"F")); client->print("\t\t"); //Equivalent Sea Level Pressure client->print(seaLevel); client->print(String( presUnit == BME280::PresUnit_hPa ? "hPa" :"Pa")); // expected hPa and Pa only client->print("\t\t\t"); //Heat Index client->print(heatIndex); client->write(0xC2); client->write(0xB0); client->print(String(envTempUnit == EnvironmentCalculations::TempUnit_Celsius ? "C" :"F")); client->print("\t\t"); //Absolute Humidity client->println(absHum); }
Powyższy kod można także znaleźć na bitbukecie w repo gitowym.
Za pomocą funkcji tempUnit można ustawić odczyt wartości temperatury w jednej z dwóch jednostek:
- TempUnit_Celsius - Celsjusz
- TempUnit_Fahrenheit - Fahrenheit
Za pomocą funkcji presUnit() można ustawić odczyt wartości ciśnienia w następujących jednostkach:
- PresUnit_Pa - paskal
- PresUnit_hPa - hektopaskal
- PresUnit_inHg - cal słupa rtęci
- PresUnit_atm - atmosfera
- PresUnit_bar - bar
- PresUnit_torr - tor (milimetr słupa rtęci)
- PresUnit_psi - funt-siła na cal kwadratowy
Użyta biblioteka dostarcza dodatkowo funkcje (zdefiniowane w pliku nagłówkowym EnvironmentCalculations.h) przeznaczone do wyliczania innych parametrów powietrza:
- wysokość nad poziomem morza - funkcja Altitude() z parametrami:
- pressure - ciśnienie wskazywane przez czujnik
- AltitudeUnit - jednostka wysokości (domyślnie w metrach)
- seaLevelPressure - lokalne ciśnienie zredukowane do poziomu morza. Domyślna wartość wynosi 1013.25 hPa i jest to średnia wielkości ciśnienia atmosferycznego na Ziemi na poziomie morza.
- outsideTemp - temperatura powietrza na zewnątrz (domyślnie 15 stopni Celsjusza)
- TempUnit - jednostka temperatury (domyślnie Celsjusz)
- punkt rosy - funkcja DewPoint() z parametrami:
- temp - temperatura wskazywana przez czujnik
- hum - wilgotność względna wskazywana przez czujnik
- TempUnit - jednostka temperatury (domyślnie Celsjusz)
- ciśnienie dla poziomu morza - funkcja EquivalentSeaLevelPressure() z parametrami:
- barometerAltitude - wysokość czujnika (wysokość nad poziomem morza + wysokość lokalizacji czujnika)
- temp - temperatura wskazywana przez czujnik
- pres - ciśnienie wskazywane przez czujnik
- envAltUnit - jednostka wysokości (domyślnie metr)
- envTempUnit - jednostka temperatury (domyślnie Celsjusz)
- wilgotność bezwzględna - funkcja AbsoluteHumidity() z parametrami:
- temp - temperatura wskazywana przez czujnik
- hum - wilgotność względna wskazywana przez czujnik
- envTempUnit - jednostka temperatury (domyślnie Celsjusz)
- wskaźnik ciepła (temperatura odczuwana) - funkcja HeatIndex() z parametrami:
- temperature - temperatura wskazywana przez czujnik
- humidity - wilgotność względna wskazywana przez czujnik
- tempUnit - jednostka temperatury (domyślnie Celsjusz)
Wzory dla wykonywanych obliczeń i linki do źródeł można znaleźć w pliku źródłowym EnvironmentCalculations.cpp i w pliku README.
Kończąc temat uruchomienia, trzeba jeszcze wspomnieć o możliwościach konfiguracji czujnika, jakie daje użyta biblioteka. Domyślną konfigurację przedstawia przykład BME280_Modes.ino.
BME280I2C::Settings settings( BME280::OSR_X1, BME280::OSR_X1, BME280::OSR_X1, BME280::Mode_Forced, BME280::StandbyTime_1000ms, BME280::Filter_Off, BME280::SpiEnable_False, BME280I2C::I2CAddr_0x76 // I2C address. I2C specific. ); BME280I2C bme(settings);
Stałe powyżej określają pracę czujnika.
Czwarty parametr (w przykładzie przyjmuje wartość BME280::Mode_Forced określa tryb pracy czujnika. Możliwe są trzy wartości: Mode_Sleep, Mode_Forced, Mode_Normal i opisane w rozdziale "Sensor modes" dokumentacji czujnika. W opisie jest też diagram przejść między tymi trybami. W skrócie Mode_Sleep oznacza brak wykonywania jakichkolwiek operacji i najniższy możliwy pobór mocy. Mode_Forced oznacza wykonanie jednego pomiaru i przejście do stanu uśpienia (Mode_Sleep). Kolejny pomiar, jak mniemam, uruchamia funkcja bme.read(). Ostatni z trybów, Mode_Normal powoduje cykliczne wykonywanie pomiarów po których czujnik przechodzi w stan uśpienia. O częstotliwości pomiarów decyduje piąty parametr, w przykładzie wyżej jest to StandbyTime_1000ms. Do wyboru mamy stałe/wartości: StandbyTime_500us, StandbyTime_62500us, StandbyTime_125ms, StandbyTime_250ms, StandbyTime_50ms, StandbyTime_1000ms, StandbyTime_10ms, StandbyTime_20ms.
Szósty parametr określa, czy włączony jest filtr IIR (filtr o nieskończonej odpowiedzi impulsowej), który służy do wygładzania wyników pomiaru temperatury i ciśnienia.Można powiedzieć, że spełnia funkcję filtra dolnoprzepustowego. Filter_Off oznacza wyłączenie filtru. Inne możliwe wartości konfigurują nam filtr (filter coefficient): Filter_1, Filter_2, Filter_4, Filter_8, Filter_16. Im większa wartość tym sygnał wyjściowy wolniej podąża za zmianami sygnału wejściowego (wyniku pomiaru). Zgodnie z dokumentacją filtr może być użyty przy pomiarze ciśnienia i temperatury, które mogą się gwałtownie zmieniać na skutek chwilowych zdarzeń takich jak przeciąg, czy na moment otwarte drzwi wejściowe na zewnątrz domu. Z kolei wilgotność, z tego co można wyczytać w dokumentacji, nie ulega już takim wahaniom i nie wymaga używania filtra IIR.
Pozostałych dwóch parametrów już nie rozpatrywałem. Używałem tu magistrali I2C i adres czujnika się zgadzał z wartością domyślną.
W rozdziale "Recommended modes of operation" dokumentacji czujnika producent przestawia różne możliwości zastosowania czujnika i propozycje konfiguracji dla każdego z wymienionych zastosowań. Domyślna konfiguracja czujnika użyta w omawianej bibliotece odpowiada pracy czujnika w stacji pogody. Poza tym można tam jeszcze znaleźć sposób konfiguracji czujnika w przypadku użycia go jako monitora wilgotności, wewnętrznej nawigacji (cokolwiek to znaczy) lub w przypadku gier. W dwóch ostatnich przypadkach jest wzmianka o konieczności dokładanego pomiaru wysokości nad poziomem morza, więc zalecany jest "wysoki" oversampling i włączony filtr IIR z dużym (Filter_16) współczynnikiem filtra.
Uruchomienie czujnika |
Wyniki i obliczenia na terminalu |
Artykuł ze źródła (3) zawiera porównanie różnych tanich czujników w tym BME280. Okazuje się, że jest on zwycięzcą tego zestawienia. W szczególności wyprzedza opisywany czujnik DHT11 i co więcej, DHT22. Moim zdaniem jest najlepszym wyborem do pomiaru najbardziej podstawowych parametrów powietrza. Można się ewentualnie zastanawiać nad dodaniem DS18B20, który oferuje lepszy pomiar temperatury (dokładność ±0.5°C w przedziale od -10°C do +85°C). W przypadku BME280 jest to ±1.0°C w przedziale od 0°C do +65°C.
Czujnik użyłem praktycznie przy realizacji własnej Stacji pogody
Może rozbudujesz kod do obliczenia DevPoint i wstawisz możliwość pokazywania ciśnienia takiego jakie jest na wysokości morza.
OdpowiedzUsuńSposób wyznaczania tych wielkości znajdziesz w przykładach do użytej tu biblioteki pod linkiem https://github.com/finitespace/BME280/blob/master/examples/Environment_Calculations/Environment_Calculations.ino. Samą rozbudowę przykładu na tej stronie dodaję do długiej listy TODO.
UsuńKod rozbudowałem o dodatkowe obliczenia (ze wspomnianego Environment_Calculations.ino) w tym DevPoint.
Usuń