Sterownik terrarium
Uwielbiam Animal Planet, jako że pasją do “dziwnych” i “niestandardowych” zwierząt zaraził mnie mój ojciec to też w naszym domu pojawiło się kilka węży między innymi boa tęczowy, boa constrictor, następnie były ptaszniki, mrówki to i w końcu kiedyś musiała pojawić się też i jaszczurka. Jako, że chyba nie ma piękniejszej jaszczurki od brodatego smoka to stałem się też i jej szczęśliwym posiadaczem.
Przed zakupem stworzyłem dla niej lokum to które widzicie na zdjęciu, ale postanowiłem przy okazji, że zapewnię jej warunki zbliżone do “naturalnych”. Pomysł również podrzucił mi ojciec, którego Agamka często zasypiała w dziwnych miejscach bo nagle zaskakiwała ją “noc”.
Wtedy powstał plan sterownika.
Założenia:
- Cykl dnia i nocy z zmierzchem i świtem.
- Pomiar temperatury i wilgotności
- Wyświetlacz który pokazywał by datę i czas, temperaturę i wilgotność, temperaturę wyspy, stan oświetlenia i stan UVB.
- Ma być tanio i łatwo
Wybór padł na Arduino bo proste tanie i mnóstwo wsparcia na forach. Spełniał wszystkie wymogi poza jednym nie chciałem symulować zmierzchu i świtu za pomocą LED. I tu pojawił się problem skąd wziąć ściemniacz AC który da się podłączyć do Arduino… Szukałem gotowych rozwiązań i nie mogłem znaleść nic co by mi pasowało, aż pewnego razu znalazłem to link. Po więcej szczegółów odsyłam do tego genialnego artykułu.
Po zlutowaniu układu okazało się, że jest to strzał w dziesiątkę. Układ sprawdzał się idealnie i bez problemu mógł obsłużyć dwie żarówki 40W.
Poza lampami żarnikowymi w terrarium znajdą się jeszcze dwie żarówki które nie mogą być podpięte pod w/w układ, a są to żarówka UVB oraz żarówka LED. Te podłączyłem za pomocą zwykłych przekaźników.
Jako czuniki temperatury i wilgotności użyłem dwóch układów DHT11, które chyba każdy kto choć trochę interesował się elektroniką i Arduino zna. A temperaturę wyspy mierzę wtopionym czujnikiem DS18B20.
Za kontrolę czasu odpowiadał zegar DS1307 chodzący na szynie I2C.
Wyświetlacz który użyłem był również obsługiwany po szynie I2C więc każdy z was może dobrać sobie taki wyświetlacz jaki mu pasuje najlepiej. Ja użyłem wyświetlacza 2×16.
Aby Arduino dało się szybko podmienić lub przeprogramować użyłem “tarczy” (Shield-a) wpinanej do Arduino na “kanapkę”.
Zanim zamieszczę okrojony schemat podłączenia to trochę o nim opowiem. Najważniejsze o czym trzeba pamiętać to ilość kabli które potrzebujemy włożyć do środka terrarium. Ja zastosowałem dwie żarówki 40W (obecnie używam tylko jednej), 1 żarówka UVB, 1 żarówka Led-owa. Żarówki żarnikowe posłużą nam do symulacji zmierzchu i świtu i te podłączmy jednym przewodem. Żarówka UVB i LED-owa zaświecać będą się zaraz po zakończeniu symulacji świtu i gasić tuż przed symulacją zmierzchu i też podłączmy je jednym przewodem szeregowo. Następnie czujniki. Dla dwóch DHT11 użyłem skrętki, a czujnik DS18B20 ma wbudowany kabel więc tylko go przedłużyłem.
Mam nadzieję, że schematy są wystarczająco zrozumiałe. Na początku może się to wydawać skomplikowane, ale po analizie kodu wszystko staje się wystarczająco proste.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
#include <Wire.h> #include "RTClib.h" #include <OneWire.h> #include <LiquidCrystal_I2C.h> #include <LCD.h> #include <dht11.h> #include <TimerOne.h> //DIMMER/////////////////////////////////////////////////////// #define SWIT 7 // #define ZMIERZCH 21 // #define OKRES 30 // /////////////////////////////////////////////////////////////// //BUTTONS////////////////////////////////////////////////////// //SENSORY I PRZEZNIKI////////////////////////////////////////// #define TERMOMETR_PIN 6 //port czujnika temperatury (T0) // #define DHT1_PIN A0 //port analogowy dla DHT1 (T1) // #define DHT2_PIN A1 //port analogowy dla DHT2 (T2) // #define PRZEKAZNIK_PIN1 7 //port cyfrowy dla przekaznika 1 // #define PRZEKAZNIK_PIN2 8 //port cyfrowy dla przekaznika 2 // /////////////////////////////////////////////////////////////// //LCD////////////////////////////////////////////////////////// #define I2C_LCD_ADDRESS 0x27 // #define LCD_COLS 16 // #define LCD_ROWS 2 // #define BACKLIGHT_PIN 3 // #define En_pin 2 // #define Rw_pin 1 // #define Rs_pin 0 // #define D4_pin 4 // #define D5_pin 5 // #define D6_pin 6 // #define D7_pin 7 // /////////////////////////////////////////////////////////////// //ZMIENNE SENSOROW I ICH KONSTRUKCJA///////////////////////////////////////////////////////////// RTC_DS1307 RTC; //zegar RTC // OneWire ds(TERMOMETR_PIN); //termometr DS1820 // dht11 DHT1; //klasa odczytujaca czujniki DHT // //dht11 DHT2; //klasa odczytujaca czujniki DHT // LiquidCrystal_I2C lcd(I2C_LCD_ADDRESS,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin); // int temperatura; // int wilgotnosc1DHT; // int temperature1DHT; // int wilgotnosc2DHT; // int temperature2DHT; // int msgIndex; // boolean UVBstatus; // String statusDnia; // ///////////////////////////////////////////////////////////////////////////////////////////////// //DIMMER MODULE////////////////////////////////////////////////////////////////////////////////// //PIN 2 PODLACZONY JAKO INTERUPT 0 // volatile int i=0; // Licznik // volatile boolean zero_cross=0; // identyfikator przejscia przez zero // int AC_pin = 5; // wyjscie dla opto triaka // int dim = 128; // Poziomy sciemnienia (0-128) 0 = wlczone, 128 = wylaczone // int inc=1; // zmienna przyrostowa, 1=up, -1=down // int freqStep = 78; // (1000000 / (AC Freq(50Hz) * 2)) / 128 // ///////////////////////////////////////////////////////////////////////////////////////////////// void <b>setup</b> () { Wire.begin(); RTC.begin(); pinMode(PRZEKAZNIK_PIN1, OUTPUT); pinMode(PRZEKAZNIK_PIN2, OUTPUT); digitalWrite(PRZEKAZNIK_PIN1, 1); digitalWrite(PRZEKAZNIK_PIN2, 1); lcd.begin(LCD_COLS, LCD_ROWS); lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE); lcd.setBacklight(LOW); lcd.home(); msgIndex=0; pinMode(AC_pin, OUTPUT); // Ustawia pin triaka na wyjście attachInterrupt(0, zero_cross_detect, RISING); // Dodaje przerwanie na pin 2(interupt 0) w momencie przejścia sinusoidy prądu zmiennego przez 0 Timer1.initialize(freqStep); // Inicjalizacja biblioteki TimerOne dla żadanej częstotliwości Timer1.attachInterrupt(dim_check, freqStep); //RTC.adjust(DateTime(__DATE__, __TIME__)); //Ustwienie czasu przy pierwszym wgraniu czasu } void <b>loop</b> () { //odczyt aktualnej daty DateTime now = RTC.now(); //Obsługa światła setLights(now); // odczyt czujnikow getSensorsData(now); //wypisz wartosci printOnDisplay(now); //czekanie delay(1000); } void setLights(DateTime now){ if (now.hour()>=SWIT && now.hour()<ZMIERZCH) { lcd.setBacklight(HIGH); //7:00-7:30 if (now.hour()==SWIT && now.minute()<OKRES) { statusDnia="SWIT"; dim=128-((now.minute()*4)+(now.second()/15)); digitalWrite(PRZEKAZNIK_PIN1, 1); UVBstatus=false; } //20:30-21 else if (now.hour()==ZMIERZCH-1 && now.minute()>OKRES) { statusDnia="ZMIERZCH"; dim=((now.minute()-OKRES)*4)+(now.second()/15); digitalWrite(PRZEKAZNIK_PIN1, 1); UVBstatus=false; } else{ statusDnia="DZIEN"; dim=0; digitalWrite(PRZEKAZNIK_PIN1, 0); UVBstatus=true; } } //21-7 else { statusDnia="NOC"; lcd.setBacklight(LOW); dim=128; digitalWrite(PRZEKAZNIK_PIN1, 1); UVBstatus=false; } } void getSensorsData(DateTime now){ if(now.second()%10==0){ temperatura = getTemp(); if(now.second()%20==0){ DHT1.read(DHT1_PIN); temperature1DHT = DHT1.temperature; wilgotnosc1DHT = DHT1.humidity; } else{ DHT1.read(DHT2_PIN); temperature2DHT = DHT1.temperature; wilgotnosc2DHT = DHT1.humidity; } } } void printOnDisplay(DateTime now){ if(now.second()%5==0){ msgIndex++; if (msgIndex>4){ msgIndex=1; } lcd.clear(); } if (msgIndex==1){ lcd.setCursor(0, 0); lcd.print("T1="); lcd.print(temperature1DHT); lcd.print((char)223); lcd.print("C "); lcd.print("W1="); lcd.print(wilgotnosc1DHT); lcd.print("% "); lcd.setCursor(0, 1); lcd.print("T2="); lcd.print(temperature2DHT); lcd.print((char)223); lcd.print("C "); lcd.print("W2="); lcd.print(wilgotnosc2DHT); lcd.print("% "); } if (msgIndex==2){ lcd.setCursor(0, 0); lcd.print("Temp wyspy:"); lcd.print(temperatura); lcd.print((char)223); lcd.print("C "); } if (msgIndex==3){ //data lcd.setCursor(0, 0); lcd.print(' '); lcd.print(now.day(), DEC); lcd.print('/'); lcd.print(now.month(), DEC); lcd.print('/'); lcd.print(now.year(), DEC); lcd.print(' '); String day = getMonthName(now.dayOfWeek()); lcd.print(day); //czas lcd.setCursor(0, 1); lcd.print(" "); if(now.hour()<10){ lcd.print("0"); } lcd.print(now.hour(), DEC); lcd.print(':'); if(now.minute()<10){ lcd.print("0"); } lcd.print(now.minute(), DEC); lcd.print(':'); if(now.second()<10){ lcd.print("0"); } lcd.print(now.second(), DEC); } if (msgIndex==4){ lcd.setCursor(0, 0); lcd.print("Oswiet.: "); lcd.print(getLightInPercentage(dim)); lcd.print("%"); lcd.setCursor(0, 1); lcd.print(" UVB: "); lcd.print(getUVBStatus()); } } String getMonthName(int dayofTheWeek){ String day; switch(dayofTheWeek){ case 0: day = " Nie"; break; case 1: day = " Pon"; break; case 2: day = " Wto"; break; case 3: day = " Sro"; break; case 4: day = " Czw"; break; case 5: day = " Pia"; break; case 6: day = " Sob"; break; } return day; } int getLightInPercentage(int dim){ int temp = ((dim*100)/128); return 100-temp; } String getUVBStatus(){ if (UVBstatus){ return "wlacz."; } else{ return "wylacz."; } } int getTemp(){ //returns the temperature from one DS18S20 in DEG Celsius byte data[12]; byte addr[8]; if ( !ds.search(addr)) { //no more sensors on chain, reset search ds.reset_search(); return -1000; } if ( OneWire::crc8( addr, 7) != addr[7]) { <b>Serial</b>.println("CRC is not valid!"); return -1000; } if ( addr[0] != 0x10 && addr[0] != 0x28) { <b>Serial</b>.print("Device is not recognized"); return -1000; } ds.reset(); ds.select(addr); ds.write(0x44,1); // start conversion, with parasite power on at the end byte present = ds.reset(); ds.select(addr); ds.write(0xBE); // Read Scratchpad for (int i = 0; i < 9; i++) { // we need 9 bytes data[i] = ds.read(); } ds.reset_search(); byte MSB = data[1]; byte LSB = data[0]; float tempRead = ((MSB << 8) | LSB); //using two's compliment int TemperatureSum = (int)tempRead / 16; return TemperatureSum; } void zero_cross_detect() { zero_cross = true; // set the boolean to true to tell our dimming function that a zero cross has occured i=0; digitalWrite(AC_pin, LOW); // turn off TRIAC (and AC) } // Turn on the TRIAC at the appropriate time void dim_check() { if(zero_cross == true) { if(i>=dim) { digitalWrite(AC_pin, HIGH); // turn on light i=0; // reset time step counter zero_cross = false; //reset zero cross detection } else { i++; // increment time step counter } } } |
Jeżeli wszystko poszło ok to po włączeniu układu powinien on o godzinie 6:30 zacząć wschód o 7 załączyć UVB i LED, a o 20 30 wyłączyć UVB i LED i powoli zacząć zmierzch.
Po zakończonych pracach projektowych czas wszystko włożyć w jakąś obudowę ja wybrałem drewniane pudełko i efekt końcowy widać na poniższej galerii.
Taki układ działa już u mnie od około roku. Jedynym minusem to brak zaimplementowanej zmiany czasu z letniego na zimowy, ale myślę, że załatwię to kiedyś przy okazji jakąś zworką. Jeżeli będziecie mieć jakieś pytania to piszcie w komentarzach postaram się Wam odpowiedzieć w ramach możliwości.
Pozdrawiam
Tomek
www.darbean.net.pl
Czyli że wszystkie czasy są ustawione na sztywno i nie można ich zmienić bez przeprogramowania procka. Obecnie pisze program sterownika do akwarium co prawda w Bascom ale ja każdą wartość mogę zmieniać.
Czasu RTC nie trzeba zmieniać bo nawet przy spadku napięcia zewnętrznego bateria podtrzyma jego działanie(a czas letni/zimowy można zrobić na zworkę). Co do czasów ustawiania świtu i zmierzchu to myślałem nad funkcją ustawiania tych wartości bez potrzeby przeprogramowania Arduino, ale przeanalizowałem jak często będę musiał zmieniać ten czas i w moim przypadku jest to na tyle rzadko, że nie ma sensu tego implementować(od roku nie zmieniałem). Gdybym jednak zmieniał arduino na osobny mikrokontroler jak zasugerował #ioiok to z pewnością dodałbym mały panel sterowania.
ja w swoim termometrze wykorzystuje bluetooth :) jak podpinam do niego telefon to ten wysyła aktualną datę i godzinę (przy odpowiednim ustawieniu w apce)
Super rozwiązanie mam nadzieję że Twój projekt również się tu pojawi bo z chęcią podpatrzył bym to rozwiązanie :)
niestety u mnie trochę estetyka kuleje, i to był pierwszy projekt który robiłem, na własnoręcznie robionym klonie arduino, własnej płytce itp, i wiem że ma spore niedociągnięcia np świruje analogowy termometr który miał służyć do mierzenia temperatury w mieszkaniu. Pewnie przez brak cewki w zasilaniu albo jest zbyt blisko 7805:P kiedyś przy okazji innego projektu wrzucałem zdjęcie.
Robiłem drzewiaste menu (do podobnego LCD) do programatora czasowego i zęby na tym zjadłem – jest to zaskakująco dużo roboty.
Po pierwsze, znaleźć/przerobić odpowiednią bibliotekę do obsługi menu, żeby intuicyjnie prezentowała godziny, dni tygodnia, opcje itp.
Po drugie, znaleźć szybko rysującą i nieblokującą bibliotekę do obsługi wyświetlacza po I²C.
Po trzecie, obsługa menu nie może kolidować z główną pętlą programu, więc przyciski muszą być na przerwaniach – czyli w efekcie albo dokładamy kolejny scalak I²C do obsługi przycisków, albo robimy jakieś bramki, bo pinów do obsługi przerwań w Arduino Uno nie ma zbyt wiele.
Po czwarte, 2KB RAM w Arduino ledwie wystarcza, żeby pomieścić obsługę aktuatorów i wskaźniki do struktury drzewa menu; komunikaty trzeba było programować w EPROMie i stamtąd pobierać przez wskaźnik. Ale i tak musiałem np. “pakować” booleany do pojedynczego uint8_t i używać operatorów binarnych, żeby oszczędzić RAM.
Po piąte, gdzie zapisywać ustawione wartości, żeby się zapamiętywały po restarcie, ale też nie zamordować EPROMu (ja użyłem wolnych komórek RAMu od RTC)
Po szóste, ten prościutki program razem z bibliotekami, po wywaleniu z nich WSZYSTKICH nieużywanych funkcji i struktur, zajął… 31,6KB czyli zostało 400 bajtów (!) wolnego miejsca w EPROMie.
Projekt zakończył się powodzeniem i kiedyś nawet chciałem go opisać, ale doszedłem do wniosku, że i tak nikomu nie będzie chciało się tego czytać, bo nie da się z tego zrobić tutoriala ;)
Piąta gwiazdka za fajną obudowę ;)
Mało do czego mogę się przyczepić więc powiem tylko standardowo, że jeśli ten projekt działa już ok. roku to czas pomyśleć nad przeniesieniem go na jakiś osobny mikrokontroler, albo (to co ja polecam) na arduino pro mini
Tak wiem, ale cały czas brakuje czasu. Poza tym musiałbym go zbudować na płytce podobnej do arduino, żeby nie musieć przerabiać tej “kanapki”. Narazie szybciej i o niewiele drożej wyjdzie kupić mi drugie arduino uno choć przymierzam się kupić arduino mega i zrobić mały inteligentny pokój, a później dom.
Możesz zrobić sobie bardo prostą przejściówkę na płytce uniwersalnej. A dokładniej w dużą płytkę uniwersalną przez goldpiny wlutowujesz arduino pro mini a w miejscach gdzie powinny być wyjścia arduino uno wstawiasz gniazda do goldpinów i oczywiście odpowiednio ciągniesz ścieżki i ewentualnie zworki/przewody. Wtedy masz wszystko pasujące do twojej ,,kanapki” i na pewno taniej, zwłaszcza gdy użyjesz klona arduino pro mini
Witaj,
fajny pomysł i wykonanie również ciekawe.
Od siebie polecam Arduino Pro Mini – tanie jak barszcz i nie trzeba się pierniczyć z obstawą do Atmegi budując ją samemu.
Poza tym nie polecam tego DS, dokładności to nie ma żadnej, śpieszy się kilkadziesiąt sekund dziennie – także jako zegarek odpada, no chyba że sobie zrobisz synchronizacje po NTP ale to się mija z celem. Zdecydowanie lepiej użyć DS3231 – droższy ale zupełnie inna bajka – odchyłka na poziomie poniżej 2s rocznie!
Zamiast DHT11 poleciłbym również DHT22 bo podobnie jak z DS zupełnie inna dokładność w dokonywanych pomiarach ;/
Ogólnie fajnie, że chciało ci się zamieścić, bo sam wiem, że szybciej się robi projekt, niż go później dokumentuje ;D
Wszystko jest tak przygotowane że wymiana na wymienione przez Ciebie elementy trwałaby 30 minut z magicznym “pssst” na początku. Z tym zegarem to racja ale te straty czasu na razie mi nie dokuczają(to znaczy zauważyłem je ale nie ma tragedii), a co do czujników to fakt, ale podczas budowania tylko takie miałem.
Jak już pisałem sterownik chodzi już sobie od roku. Działa i to jest najważniejsze. A dokumentacja i prezentacja? No cóż rok się do tego zabierałem :)
Dzięki za polecenie odpowiednich podzespołów.
Dokładnie czegoś w tym stylu szukam, tyle że do akwarium, czyli bez czujników wilgotności i temp. Nie myślałeś o wyświetlaczu z przyciskami i menu, gdzie mógł byś w każdej chwili zmieniać czas naświetlania?
No rozważałem ale nie potrzebuję zbyt często zmieniać czasów i postanowiłem że na razie zostawię to zaprogramowane w układzie. Sprawę ułatwia mi kanapka odpinam arduino od shield-a i aktualizuję soft. Cała operacja zajmuje 10 minut wiec nie ma to dla mnie znaczenia. Jeżeli chciałbyś zbudować podobny układ to popatrz do komentarza #Konrada. Użyj zamiast moich proponowane przez niego elementy. A co do przycisków to faktycznie można to dodać i jeżeli będę jeszcze kiedyś komuś budował podobny układ to pewnie się nad nimi zastanowię.
Kto szuka gotowych rozwiązań, można zainteresować się tym: http://www.esea.pl są tam fajnie działające ściemniacze na szynę DIN, komunikacja po ModBusie.
Czy można prosić o jakieś zdjęcia i informacje o samym terrarium? Z góry dzięki :)
Jutro zrobię szczegółowe zdjęcia i wstawię tutaj do nich link.
Minęły 4 dni i jeszcze nie ma, jak coś to dalej czekam :)
Przepraszam najmocniej, ale obecnie się przeprowadzam i mam juz wszystko spakowane. Jak tylko będę mógł to zdjęcia się pojawią.
Przyczepię się do słownictwa… Nie ma czegoś takiego jak “żarówka LED”! W LED-ach nie ma żarnika (wolframowego, węglowego itp.)! Na takie lampki (tak, lampki, źródła światła) możemy mówić “LEDówki”, “lampki LED”, lub same “LED-y”. Nie bądźmy głupi jak ‘sprzedawcy’ z Allegro, którzy nie mają pojęcia, co sprzedają ;)
nie lepiej zamiast funkcji getMonthName użyć zwykłej tablicy, coś takiego powinno zadziałać.
deklaracja
char dni[7]={“Nie”,”Pon”,”Wto”,”Sro”,”Czw”,”Pia”,”Sob”};
wywołanie
String day = dni[now.dayOfWeek()];
nie ten typ danych :) deklaracja raczej powinna wyglądać tak:
String dni[7]={“Nie”,”Pon”,”Wto”,”Sro”,”Czw”,”Pia”,”Sob”};
można prosić o fotki bo te coś wygasły a jestem chętny zrobić sobie takie pudełko do mojego terrarium
Jezeli mozna prosic o schematy… niestety wygasly. Pozdrawiam autora
a co powiecie na taki sterownik https://www.youtube.com/watch?v=dPhhYaAw0c4