/*
Barometr elektroniczny / electronic barometer
Autor / Author: Dawid Gutkiewicz
Wersja / Version: 2.0
Licencja / License: GNU GPL "General Public License"
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
This file is part of GNU package.
GNU package is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
GNU package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
*/
#include <LiquidCrystal.h>
#include <BMP180.h>
#include <Wire.h>
#include <idDHT11.h>
#include <Time.h>
#define maxLoopToSleep 63 // max Loop without pressing button, after LCD going to sleep
#define lcdRefreshRate 95 // sets lcd refresh rate, LCD will refresh after 10*lcdRefreshRate ms
#define BMPMeasurementError 14 //sets BMP meter measurement error for preasure
int idDHT11pin = 2; //Digital pin for comunications
int idDHT11intNumber = 0; //interrupt number (must be the one that use the previus defined pin (see table above)
//declaration
void dht11_wrapper(); // must be declared before the lib initialization
// Lib instantiate
idDHT11 DHT11(idDHT11pin,idDHT11intNumber,dht11_wrapper);
//temp variable
int i;
int j=0;
int loopDone;
//set up boad to display (Temp + preasure or Altitude + humidity)
int boardToDisplay = 0;
//set up LCD pins
LiquidCrystal lcd(4, 5, 6, 7, 8, 9, 10);
// Store an instance of the BMP180 sensor.
BMP180 barometer;
// We are going to use the on board LED for an indicator.
int indicatorLed = 13;
// Store the current sea level pressure at your location in Pascals.
double seaLevelPressure = 101325;
//keyboard:
int buttonPin = 0;
int buttonNumber;
void setup()
{
//sets start time
setTime(12, 30, 30, 15, 6, 2010);
// sets how much loops have been done before LCD sleep
loopDone = 0;
// control LCD backlight
pinMode(11, OUTPUT);
// turn on lcd backlight
digitalWrite(11, HIGH);
//set up LCD size:
lcd.begin(20,4);
//clear LCD:
lcd.clear();
// We start the I2C on the Arduino for communication with the BMP180 sensor.
Wire.begin();
// Set up the Indicator LED.
pinMode(indicatorLed, OUTPUT);
// We create an instance of our BMP180 sensor.
barometer = BMP180();
// We check to see if we can connect to the sensor.
if(barometer.EnsureConnected())
{
digitalWrite(indicatorLed, HIGH); // Set our LED.
// When we have connected, we reset the device to ensure a clean start.
barometer.SoftReset();
// Now we initialize the sensor and pull the calibration data.
barometer.Initialize();
}
else
{
digitalWrite(indicatorLed, LOW); // Set our LED.
}
}
//necesary to Humidity check work
void dht11_wrapper() {
DHT11.isrCallback();
}
// returns max day number for specyfic month and year
int max_day(int month_value, int year_value)
{
int max_day_atm; // max day "at the moment"
int tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // table with max days
if(month_value != 2) //sets max day for every month but February
max_day_atm = tab[month_value-1];
if(month_value == 2) // sets max day for February
{
if(year_value % 4 != 0) //non leap-year - 28 days
max_day_atm = tab[month_value-1];
if(year_value % 4 == 0) //leap-year - 29 days
max_day_atm = 29;
}
return max_day_atm;
}
//setting up current clock with user
void set_clock()
{
//getting current settings:
int setHour=hour();
int setMinute=minute();
int setSecond=second();
int setDay=day();
int setMonth=month();
int setYear=year();
int setDataNumber=0; // setDataNumber minds which data we are ediding at now
//starting while function to set clock (interaction with user)
while(setDataNumber !=6)
{
display_board_zero(setHour, setMinute, setSecond, setDay, setMonth, setYear); // displays data
if(setDataNumber < 3) // dispalys what are we editing
{
lcd.setCursor(6, 0);
if(setDataNumber == 0)
lcd.print("(godz.)");
if(setDataNumber == 1)
lcd.print("(min.)");
if(setDataNumber == 2)
lcd.print("(sek.)");
}
if(setDataNumber > 2) // dispalys what are we editing
{
lcd.setCursor(2, 2);
if(setDataNumber == 3)
lcd.print("(dzien)");
if(setDataNumber == 4)
lcd.print("(mies.)");
if(setDataNumber == 5)
lcd.print("(rok)");
}
buttonNumber = 0;
set_button_number(analogRead(buttonPin), 0);
if(buttonNumber == 1)
setDataNumber++;
if(buttonNumber == 4 && setDataNumber > 0)
setDataNumber--;
if(buttonNumber == 2)
{
if(setDataNumber == 0 && setHour < 23)
setHour++;
if(setDataNumber == 1 && setMinute < 59)
setMinute++;
if(setDataNumber == 2 && setSecond < 59)
setSecond++;
if(setDataNumber == 3 && setDay < max_day(setMonth, setYear))
setDay++;
if(setDataNumber == 4 && setMonth < 12)
{
setMonth++;
if(setDay > max_day(setMonth, setYear))
setDay = max_day(setMonth, setYear);
}
if(setDataNumber == 5 && setYear < 2099)
{
setYear++;
if(setDay > max_day(setMonth, setYear))
setDay = max_day(setMonth, setYear);
}
}
if(buttonNumber == 3)
{
if(setDataNumber == 0 && setHour > 0)
setHour--;
if(setDataNumber == 1 && setMinute > 0)
setMinute--;
if(setDataNumber == 2 && setSecond > 0)
setSecond--;
if(setDataNumber == 3 && setDay > 1)
setDay--;
if(setDataNumber == 4 && setMonth > 1)
{
setMonth--;
if(setDay > max_day(setMonth, setYear))
setDay = max_day(setMonth, setYear);
}
if(setDataNumber == 5 && setYear > 1971)
{
setYear--;
if(setDay > max_day(setMonth, setYear))
setDay = max_day(setMonth, setYear);
}
}
delay(150);
}
setTime(setHour, setMinute, setSecond, setDay, setMonth, setYear);
}
void set_sea_level_preasure()
{
int done=0;
while(done == 0)
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Cisn. wzgl.:");
lcd.setCursor(0, 1);
lcd.print(double(seaLevelPressure/100+BMPMeasurementError));
lcd.print(" hPa");
set_button_number(analogRead(buttonPin), 0);
if(buttonNumber == 1 || buttonNumber == 4)
done++;
if(buttonNumber == 2 && seaLevelPressure < 105000)
seaLevelPressure += 25;
if(buttonNumber == 3 && seaLevelPressure > 95000)
seaLevelPressure -= 25;
delay(200);
}
}
void display_board_zero(int hour_dis, int minute_dis, int second_dis, int day_dis, int month_dis, int year_dis)
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Czas:");
lcd.setCursor(0, 1);
//printing hour:
if(hour_dis<10)
lcd.print("0");
lcd.print(hour_dis);
lcd.print(":");
//printing minute:
if(minute_dis<10)
lcd.print("0");
lcd.print(minute_dis);
lcd.print(":");
//printing second:
if(second_dis<10)
lcd.print("0");
lcd.print(second_dis);
lcd.setCursor(-4, 2);
lcd.print("Data:");
lcd.setCursor(-4, 3);
//printing day:
if(day_dis<10)
lcd.print("0");
lcd.print(day_dis);
lcd.print(".");
//printing month:
if(month_dis<10)
lcd.print("0");
lcd.print(month_dis);
lcd.print(".");
//printing year:
lcd.print(year_dis);
lcd.print("r.");
}
void display_board_one()
{
if(barometer.IsConnected)
{
//getting current preasure in hPa
long currentPressure = barometer.GetPressure()/100;
//getting current temp in *C
double currentTemperature = barometer.GetTemperature();
//printing data on LCD:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Temperatura:");
lcd.setCursor(0, 1);
lcd.print(currentTemperature);
lcd.print(" *C");
lcd.setCursor(-4, 2);
lcd.print("Cisnienie:");
lcd.setCursor(-4, 3);
lcd.print(currentPressure+BMPMeasurementError);
lcd.print(" hPa");
}
}
void display_board_two()
{
if(barometer.IsConnected)
{
//getting current altitude in meters
float altitude = barometer.GetAltitude(seaLevelPressure);
//getting current Humidity in %
DHT11.acquireAndWait();
long currentHumidity = DHT11.getHumidity();
//printing data on LCD:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Wyskokosc npm:");
lcd.setCursor(0, 1);
lcd.print(altitude);
lcd.print(" m");
lcd.setCursor(-4, 2);
lcd.print("Wilgotnosc:");
lcd.setCursor(-4, 3);
lcd.print(currentHumidity);
lcd.print(" %");
}
}
void set_button_number(int value, int wait) // getting button number from value, from analog 0 pin, also can wait till button get released
{
if(value > 1010)
{
buttonNumber = 1;
}
if(value < 1010 && value > 980)
{
buttonNumber = 2;
}
if(value < 980 && value > 900)
{
buttonNumber = 3;
}
if(value < 900 && value > 820)
{
buttonNumber = 4;
}
if(value < 100) // if value isn't in range for specyfic button, then sets buttonNumber to 0, what minds "no button has been pressed"
{
buttonNumber = 0;
}
if(wait == 1)
while(analogRead(buttonPin)>100)
delay(10);
}
void sleep_LCD()
{
delay(10); // waiting for any button to wake up LCD
set_button_number(analogRead(buttonPin), 1);
if(buttonNumber !=0) // if any button pressed turns on LCD backlight and going back to loop function
{
loopDone = -1;
digitalWrite(11, HIGH);
j=0;
}
}
void loop()
{
// if done some loops then LCD gets sleep
if(loopDone == maxLoopToSleep)
{
digitalWrite(11, LOW);
lcd.clear();
}
// waiting till loop counter restarts
while(loopDone == maxLoopToSleep)
sleep_LCD();
i=0;
// check with button has been pressed, checking if not waking up from sleep, if waking up, then not checking button, to avoid screen change
set_button_number(analogRead(buttonPin), 1);
if(loopDone == -1)
{
buttonNumber = 0;
loopDone = 0;
}
// helps to not refresh LCD every 200 ms but every (200 * lcdRefreshRate) ms
if(j == lcdRefreshRate)
{
j=0;
loopDone++;
}
if(buttonNumber == 1) // reaction for button number 1
{
loopDone = 0; // setting loopDone to 0, to restart LCD sleep counter after changing board to display
if(boardToDisplay == 0)
{
boardToDisplay = 1;
i++;
}
if(boardToDisplay == 1 && i == 0)
{
boardToDisplay = 2;
i++;
}
if(boardToDisplay == 2 && i == 0)
{
boardToDisplay = 0;
}
j=0; // sets j = 0 to refresh LCD, after change board to display
}
if(buttonNumber == 4) // reaction for button number 4
{
loopDone = 0; // setting loopDone to 0, to restart LCD sleep counter after changing board to display
if(boardToDisplay == 0)
{
boardToDisplay = 2;
i++;
}
if(boardToDisplay == 1 && i == 0)
{
boardToDisplay = 0;
i++;
}
if(boardToDisplay == 2 && i == 0)
{
boardToDisplay = 1;
}
j=0; // sets j = 0 to refresh LCD, after change board to display
}
if(buttonNumber == 2 || buttonNumber == 3)
{
if(boardToDisplay == 0)
{
set_clock();
j = 0;
loopDone = 0;
}
if(boardToDisplay == 1)
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Barometr");
lcd.setCursor(0, 1);
lcd.print("elektroniczny");
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Autor:");
lcd.setCursor(0, 1);
lcd.print("Dawid Gutkiewicz");
lcd.setCursor(-4, 2);
lcd.print("Wersja:");
lcd.setCursor(-4, 3);
lcd.print("2.0");
delay(5000);
j = 0;
loopDone = 0;
}
if(boardToDisplay == 2)
{
set_sea_level_preasure();
j = 0;
loopDone = 0;
}
}
if(boardToDisplay == 0 && j == 0)
display_board_zero(hour(), minute(), second(), day(), month(), year());
if(boardToDisplay == 1 && j == 0)
display_board_one();
if(boardToDisplay == 2 && j == 0)
display_board_two();
delay(10); // makes refresh every 10ms, this refresh rate is used to read buttons, but not to LCD refresh directly
j++;
}
“-cztery przyciski typu” Jakiego?
Dzięki, rzeczywiście brakowało opisu, chodzi o tack switch oczywiście ;)
W roku przestępnym może być kłopot z ustawieniem 29 lutego:
nt tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // table with max days
Ten kawałek kodu to koryguje:
if(month_value != 2) //sets max day for every month but February
max_day_atm = tab[month_value-1];
if(month_value == 2) // sets max day for February
{
if(year_value % 4 != 0) //non leap-year – 28 days
max_day_atm = tab[month_value-1];
if(year_value % 4 == 0) //leap-year – 29 days
max_day_atm = 29;
}
> if(year_value % 4 == 0) //leap-year
Warunek na rok przestępny jest trochę bardziej złożony…
http://pl.wikipedia.org/wiki/Rok_przest%C4%99pny#Algorytm
Dawid ,
Arduino działa na 5V, a BMP085 na 3.3V. Musisz zatem,
również dopasować poziomy logiczne linii I2C. Podanie 3.3V na VCC barometru
nie wystarczy. Wrzuć w goole: „logic level converter” i zobaczysz o co chodzi.
—
http://www.malebranchi.waw.pl
Wg informacji, które znalazłem na temat BMP085/BMP185, moduł posiada wbudowany konwerter dla I2C.
Dlatego możemy zasilać je poprzez 3,3V ( w niektórych modelach nawet 5V), a komunikacja poprzez I2C nadal będzie odbywać się poprawnie.
Tłumaczy to dlaczego mój barometr działa ;)
Źródło:
https://www.sparkfun.com/tutorials/253
oraz
http://oomlout.co.uk/products/pressure-temperature-altitude-sensor-bmp085
cytat z ostatniego:
“It also includes an on board 3.3v regulator and level shifter meaning it is compatible with the Arduino’s 5v logic. Finally it is i2c (digital) meaning the readings are fast and reliable.”
Niemniej jednak brawo za spostrzegawczość i zauważenie takiej możliwości ;)
Dawid,
Przede wszystkim układ BMP085 nie posiada konwertera i VDDIO max to 3.6V – http://www.bosch-sensortec.com/de/homepage/products_3/environmental_sensors_1/bmp180_1/bmp180
. I tutaj nie ma o czym dalej pisać.
Niemniej, rozumiem, że akurat na Twojej płytce jest taki konwerter. Swoją drogą wydaje mi się, że również na stronie Spark Fun jest dokładnie ten sam problem – bark dopasowania poziomów logicznych. A płytka od Adafruit ma ów konwerter.
Zwracam na to uwagę, ponieważ, według mnie warto znać tego rodzaju niuanse i poprawnie budować swoje urządzenia.
—
http://www.malebranchi.waw.pl
Rozumiem, dzięki
Ale ciekawi mnie jedna rzecz, czy gdybyśmy na przykład użyli wyjść/wejść analogowych, czy nie dałoby się obejść bez konwersji sygnałów (tutaj z 3.3 na 5V i w drugą stronę odwrotnie)?
W sensie moglibyśmy zdefiniować własny poziom sygnału 1.
Hmm nie wiem, możliwe, że mogłoby to zadziałać, wydaje mi się, że
czujnikowi BMP za jedno czy będzie dostawać sygnał z cyfrowego czy analogowego portu, byle by poziomy napięć się zgadzały. Niemniej, niewątpliwie prościej użyć 4 rezystorów i 2 tranzystorów, niż pisać software’owy I2C na porcie analogowym, gdzie dodatkowo mogą pojawić się problemy z częstotliwością magistrali…
—
http://www.malebranchi.waw.pl
DTH11 to slaby czujnik, walczylem z nim ponad 2 miesiace, wymienilem na dth22..
DTH11 nie daje rady w otoczeniu powyzej 80%, po prostu jego konstrukja nie zdaje egzaminu (kilkanascie dni za oknem i sniedzieje plyta pomiarowa)…
BMP85 u mnie tez dziala, choc kalibracja jest skomplikowana (za duzo kodu jak na taki czujnik), ale dziala.:)
Projekt fajny, jak rowiazales klawisze na jednym pinie?
ADC?
Lepszy czujnik jak najbardziej wskazany, póki co, mój sprawdza się bez zarzutu.
Wiele przycisków na jednym pinie to zasługa drabinki rezystorowej,
Przykładowo mamy rezystory w szeregu R1,R2,R3….Rn
I teraz mamy przyciski S1, S2, S3… Sn, przycisk S1 zamyka obwód między 5V, a wejściem analogowym z oporem R1, przycisk S2 – R(S2)=R1+R2, przycisk Sn – R(Sn) = R1+R2+R3+…..+Rn, także dla każdego przycisku na pinu analogowego wpływa inny prąd, możemy to potraktować jako sygnał, a co za tym idzie rozróżnić przyciski, na przykład przy pomocy takiego kodu:
void set_button_number(int value, int wait)
{
if(value > 1010)
{
buttonNumber = 1;
}
if(value 980)
{
buttonNumber = 2;
}
if(value 900)
{
buttonNumber = 3;
}
if(value 820)
{
buttonNumber = 4;
}
if(value 100)
delay(10);
}
gdzie:
wait – może przyjmować wartość 1 lub inną, całkowitą, dając możliwość oczekiwania na zakończenie “wciskania” przycisku, aby kontynuować wykonywanie programu bez dalszych sygnałów
value – odczyt stanu z wejścia analogowego, dla A0, będzie to analogRead(0)
buttonNumber – określa numer przycisku, numerujemy wedle własnego uznania ;)
Inaczej jeżeli chcemy zdefiniować przycisk i poczekać z wykonywaniem programu na zwolnienie przycisku, funkcję wywołamy tak:
set_button_number(analogRead(0), 1)
Jeśli program ma nadal pracować mimo wciśnięcia (np, gdybyśmy programowali Snake na arduino, taka funkcja ma sens):
set_button_number(analogRead(0), 0)
W sumie tak to wygląda, przynajmniej tak to sobie obmyśliłem
Czujnik ciśnienia nie musi znajdować się na zewnątrz. Światowa Organizacja Meteorologiczna jak najbardziej dopuszcza montowanie barometrów w budynkach stacji meteorologicznych, tak też jest w stacjach IMGW(poza czujnikami stacji automatycznych MAWS).
W jaki sposób obliczasz ciśnienie względne?
Jak policzyć, “na piechotę”:
http://stacjepogody.waw.pl/index.php?topic=4.0
Gotowy kalkulator:
http://stacjepogody.waw.pl/kalkulator_cisnien.php
Niestety, ten kalkulator jest zły. Może nie powoduje dużych błędów, ale stosowana, dziwna metoda iteracyjna jest niezgodna ze wszystkimi normami. Wrzucam wzór redukcyjny IMGW.
P.S. Fajnie by było, jakbyś dodał opcję, że barometr dla wysokości powyżej 500m n.p.m. nie wypisuje ciśnienia względnego(dla takich wysokości już się tego nie robi).
Chyba nie doszedł obrazek:
http://wstaw.org/m/2014/09/30/redukcja.PNG
łezka się zakręciła w oku, sam 1,5 roku temu robiłem coś takiego tylko na dht22, atmega 16, płytce bluetooth HC-06 ( komunikacja z telefonem, wysyłanie temperatury i ciśnienia, oraz odbieranie informacji o godzinie (odpalenie aplikacji ustawiało zegar w termometrze – przydatne jak się czas zmienia), u mnie były 2 przyciski jeden od włączania wyświetlacza, a drugi zapalał diodę rgb która zmieniała kolory w zależności od temperatury na zewnątrz, oczywiście można było też zrobić w apce na telefonie. użyty czujnik ciśnienia BMP085 z tym że w płytce zmieniającej poziom napięć na 5v, miał być jeszcze zapis na sd ale próba zapisu zawieszała mi atmegę i nigdy nie znalazłem błędu. największą wadą było złe umieszczenie czujnika temperatury wewnętrznej który zdaje się pokazuje temperaturę stabilizatora (zasilanie z zasilacza zewnętrznego, lub baterii 9v)
Super tutek… Jak można prosić to jeszcze przydałby się GPS do tego i potem komunikacja po wifi ;) czyli czujniki na zwewnątrz a w domu wyświetlacz ;) ale to tylko moja propozycja ;)
Witaj,
Czy mogę wymienić DHT11 DHT22 z bardziej precyzyjne, i nie zmieniając niczego?
Dziękuję.
Pingback: nathan