Witajcie! Tytuł oczywiście mylący, już wyjaśniam. Przyznaję – nie mam ręki zbytnio do kwiatków, a jak do tego dodać krótką pamięć to efekt jest jeden: zieleninka usycha. Oczywiście wystarczyłoby ustawienie sobie przypominacza w telefonie czy coś takiego, ale prawdziwi mężczyźni załatwiają sprawy po męsku. Postanowiłem zbudować układ, który będzie przypominał mi o konieczności podlania doniczki. Ma marudzić – no podlej kwiatki, no kurde podlej!
Założenia:
- przypominacz ma migać diodą gdy kwiatki mają za sucho (i siedzieć cicho gdy podlewać nie trzeba),
- układ ma być zasilanie bateryjne (Edit: oraz z ogniw słonecznych).
W części pierwszej:
W części drugiej:
- jak prosto przenieść projekt do Armegi z wykorzystaniem programatora USBasp
- jak najtaniej zbudować moduł ogniwa słonecznego do zasilania mikrokontrolera
W ostatniej części:
Zaczynamy!
Na warsztat wziąłem Arduino. Na początek trochę nudnej teorii.
Wbrew pozorom rozwiązanie nie jest wcale takie proste jak by się wydawało. Bo najprościej: wystarczy zastosować jakiś czujnik wilgotności, podpiąć pod wejście analogowe, w pętli programu dać warunek kontroli sygnału względem zadanej wartości – i voila! Pfff, jaki to problem?! Ano problem jest tylko jeden – pobór prądu. Szacunkowo Arduino pobiera prąd rzędu 25mA, co przy pojemności baterii/akumulatora około 2000mAh daje czas pracy ~80 godzin czyli niecałe 3,5 dnia. Dwa komplety baterii w tydzień – oj nie tędy droga. Na szczęście Atmegi mają funkcje oszczędzania, które musimy zaprząc do pracy. Kliku-kliku i napisałem program:
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 |
#include <avr/sleep.h> int wakePin = 2; // pin used for waking up int sleepStatus = 0; // variable to store a request for sleep int count = 0; // counter int led = 13; void wakeUpNow() // here the interrupt is handled after wakeup { } int lpDelay(int quarterSeconds) { int oldClkPr = CLKPR; // save old system clock prescale CLKPR = 0x80; // Tell the AtMega we want to change the system clock CLKPR = 0x02; // 1/256 prescaler = 60KHz for a 16MHz crystal delay(quarterSeconds); // since the clock is slowed way down, delay(n) now acts like delay(n*256) CLKPR = 0x80; // Tell the AtMega we want to change the system clock CLKPR = oldClkPr; // Restore old system clock prescale } void setup() { pinMode(wakePin, INPUT); pinMode(led, OUTPUT); ADCSRA = 0; PRR = 0b00000111; CLKPR = 0x80; // Tell the AtMega we want to change the system clock CLKPR = 0x08; attachInterrupt(0, wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function // wakeUpNow when pin 2 gets LOW } void sleepNow() // here we put the arduino to sleep { set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here sleep_enable(); // enables the sleep bit in the mcucr register // so sleep is possible. just a safety pin MCUCR |= (1<<BODS) | (1<<BODSE); MCUCR &= ~(1<<BODSE); // must be done right before sleep attachInterrupt(0,wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function // wakeUpNow when pin 2 gets LOW sleep_mode(); // here the device is actually put to sleep!! // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP sleep_disable(); // first thing after waking from sleep: // disable sleep... detachInterrupt(0); // disables interrupt 0 on pin 2 so the // wakeUpNow code will not be executed // during normal running time. } void loop() { count++; digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level) delay(1); // wait for a second digitalWrite(led, LOW); // turn the LED off by making the voltage LOW delay(1); if (count >= 10) { delay(2); // this delay is needed, the sleep count = 0; sleepNow(); // sleep function called here } } |
“Napisałem” to może duże słowo, nie ma co ukrywać że nie wyważałem otwartych drzwi i korzystałem z gotowców, w większości kod pochodzi stąd, plus moje udoskonalenia. Na czym polegają owe udoskonalenia? Działający, zaprogramowany i w pełni funkcjonalny układ pobiera w trybie czuwania … (tutaj dźwięk werbli) …. około 100 nA (nanoamperów). Tak – dziesięć tysięcy razy mniej niż miliamper! Patrząc od strony matematycznej – w teorii urządzenie na jednym komplecie baterii powinno wytrzymać jakieś 2300 lat.
Wykorzystane zostały dwie funkcjonalności mikrokontrolerów Atmega:
– maksymalne wykorzystanie funkcji oszczędzania energii, w praktyce realizowane jako “wyłączenie” się układu,
– wybudzenie następuje poprzez wykorzystanie funkcji przerwań (o tym za chwilę).
Realizacja programowa w moim programie wygląda następująco:
- tuż po włączeniu układ mignie dziesięć razy diodą, na znak że żyje
- następnie – wyłącza się :-) (kurtyna!)
Tak, to już koniec! Po wejściu do funkcji loop() jednym z końcowych poleceń jest funkcja sleepNow(), która każe mikrokontrolerowi pójść spać.
Samo zaś kładzenie do łóżka realizowane jest następująco:
1 2 3 4 |
set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_enable(); attachInterrupt(0,wakeUpNow, LOW); sleep_mode(); |
Po kolei:
- pierwsza linia definiuje tryb usypiania. Trybów jest kilka, najbardziej “ekologiczny” jest oczywiście użyty tutaj SLEEP_MODE_PWR_DOWN, wyłączający większość modułów układu oprócz zegara, podstawowych podsystemów procesora, układu watchdoga (“sprzętowego” resetu procesora), BOD (podsystemu kontroli napięcia zasilania) i TWI (interfejs Two Wire). Po zapadnięciu w tak twardy sen jedynym sposobem jest odłączenie zasilania, wciśnięcie RESET lub – to co nas najbardziej interesuje: podanie zewnętrznego przerwania INT,
- druga linia umożliwia wejście w tryb uśpienia,
- trzecia linia definiuje pobudkę: informuje mikrokontroler, że podanie sygnału o logicznym poziomie “0” (LOW) na pin oznaczony jako “wakeUpNow” ma obudzić układ,
- czwarta linia to ululanie do snu: od tego momentu mikrokontroler chrapie aż się bity trzęsą.
Kilka słów o przerwaniach: o ile mi wiadomo tylko dwa piny mogą realizować tę funkcję, pin oznaczony w Arduino jako “2” i “3” (odpowiednio: czwarta i piąta nóżka w Atmedze). Wszędzie wykorzystywany jest pin 2, więc i ja tak zrobiłem. Od strony praktycznej wygląda to następująco: mikrokontroler śpi tak długo, dopóki na pin 2 nie zostanie podany stan niski. Po obudzeniu wykonywane są dwa polecenia:
1 2 |
sleep_disable(); detachInterrupt(0); |
Analogicznie – pierwsza komenda każe słać łóżko, druga chwilowo wyłącza obsługę przerwań (żeby przez przypadek układ się nie zakałapućkał). I następuje tradycyjne wykonanie pętli loop(), czyli w moim przypadku miganie diodą z radości.
To jednak nie wszystko. Okazało się bowiem, że taki typowy podręcznikowy program średnio jest oszczędny. W trybie uśpienia pobierał ok 270 mikroamperów, więc jednak sporo (ok. 300 dni na bateriach). Lektura kilku książek i szukanie w internecie skutkowało dodaniem kilku fikuśnych komend. Pierwsza:
1 2 |
ADCSRA = 0; PRR = 0b00000111; |
1 2 |
MCUCR |= (1<<BODS) | (1<<BODSE); MCUCR &= ~(1<<BODSE); // must be done right before sleep |
W pętli setup() dodałem dwie linie:
- pierwsza wyłącza układ przetwornika analogowo-cyfrowego. Nie korzystam w układzie z przetwornika A/C oraz wejść analogowych, więc spokojnie mogę się bez tego obejść. Drastycznie obniżyło to pobór prądu!
- druga linia to operacje na rejestrze PRR (Power Reduction Register). Pozwalają na wyłączenie innych podsystemów: obsługi portów szeregowych, układu programowania itp. Taka a nie inna konstrukcja (każda “1” wyłącza inny układ) sprawdziła się u mnie, wyłączanie dalszych podsystemów powodowało że mikrokontroler działał nieprawidłowo. Nie wnikałem dlaczego, tym bardziej że oszczędności już nie były duże.
- kolejne dwie wyłączają układ BOD, który ma na celu zrestartować mikrokontroler gdy napięcie zasilania będzie zbyt niskie. Zawsze to zaoszczędzone parę mikroamperów.
Kolejny trick to zmniejszanie częstotliwości pracy zegara. Robi się to dwoma komendami:
1 2 |
CLKPR = 0x80; // Tell the AtMega we want to change the system clock CLKPR = 0x08; |
Pierwsza informuje mikrokontroler o chęci przeskalowania zegara, druga to czyni. Zegar dzielony jest 2 do potęgi 8 raza (256 razy), co powoduje że mikrokontroler pracuje z zegarem niecałe 32kHz (!). A wiadomo – im wolniejsze taktowanie tym mniejszy pobór prądu. Można by tu jeszcze wstawić kwarc, np 3MHz i zmusić Atmegę do pracy na 11kHz, ale już zabrakło mi miejsca na płytce. Swego czasu bawiłem się podłączaniem kwarców zegarowych (32kHz do Atmeg, ale po kilku razach zablokowałem sobie kontrolery z niewiadomych przyczyn i/lub nie udało mi się ich uruchomić typowymi metodami, pomógł dopiero stary programator), co po podzieleniu przez 256 dałoby taktowanie układu na poziomie 100Hz – toż to prawie megabassy!
Dla potomnych zostawiłem jeszcze znalezioną w sieci funkcję lpDelay(). Ciekawa rzecz zastępująca standardową funkcję delay(). W typowym programie wywołanie np.: delay(1000) powoduje bowiem zatrzymanie programu na jedną sekundę, przy czym mikrokontroler pracuje “w tle” z pełną mocą. Funkcja lpDelay() powoduje, iż na czas przerwy mikrokontroler taktowany jest zegarem 31kHz, odczekuje tyle ile ma odczekać a potem znów wraca do pełnej szybkości. Sprytne!
W akcji zestaw wygląda następująco:
(średni pobór prądu około 150-200 μA, w impulsach do kilku miliamper)
Układ w stanie uśpienia, z włączonymi dzielnikami napięcia (o których wspomnę w następnej części):
(średni pobór prądu ok 15-30 μA. Czujnik wilgoci “zasymulowany” kawałkiem przewodu)
Ze względu na błędy pomiarowe (sam chociażby miernik daje na tym zakresie prądowym spory opór) należy przypuszczać, iż prądy są tu nieco większe, ale z moich wstępnych wyliczeń nawet zawyżone wartości w teorii powinny wystarczyć na rok pracy na komplecie baterii.
Cześć sprzętowa – w następnej części.
Nieźle, czy zastanawiałeś się nad użyciem paneli słonecznych jako źródło zasilania? Ostatnio zastanawiam się nad użyciem takowych dla projektów balkonowo-ogródkowych, ktoś ma z takimi panelami doświadczenie?
Oczywiście że by się dało, ale kosztem komplikacji układu. O ile
wydajność prądowa byłaby wystarczająca, to gorzej by było ze
stabilizacją napięcia – w nocy napięcie mogłoby spaść do zbyt niskiego
poziomu i urządzenie mogłoby zgłupieć, z zawieszeniem się włącznie
(minimum napięcia to 1,8V). Więc trzeba by dołożyć albo przetwornicę
step-up/down albo jakiś kondensator gromadzący energię. Sumarycznie –
dużo prościej dać dwie bateryjki niż ogniwo słoneczne. Ale przyznam –
pomysł zacny, to byłby dopiero efekt, 100% ekologiczny i
samowystarczalny układ działający z niczego :-)
Czyli taka przetwornica spowoduje, że przy słabym słońcu po prostu da 0V i atmega się wyłączy? W nocy i tak nie jest potrzebna informacja o podlaniu.
Czy przy bateriach też taki efekt kiedyś nie nastąpi (gdy zaczną się rozładowywać)?
A może po prostu Panel słoneczny jako ładowarka akumulatorów zasilających cały układ?
Taką opcję też rozważam :)
Co do ogniw: nie mam pojęcia :-) Ale mam podejrzenie, że przy zasilaniu poniżej poziomu minimalnego Atmega może po prostu się zawiesić, i nawet późniejsze podanie poprawnego napięcia jej nie pomoże. Ale to tylko moje podejrzenia, będę wdzięczny za opinię specjalistów. Odnośnie nocy: w układzie jest fotorezystor, który brzy braku oświetlenia “podciąga” pin przerwania do wyższego napięcia i tym samym “dezaktywuje” układ na noc. Problem rozładowania rozwiązałem następująco: w układzie jest przycisk Reset, wystarczy że go raz na miesiąc-dwa-trzy wcisnę. Jak baterie są OK to zamiga dioda świecąca, jak nie – baterie do wymiany :-)
Pingback: Marudnik do kwiatów (część druga) » Majsterkowo.pl
a dlaczego ma marudzić, a nie po prostu podlewać?
Tylko i wyłącznie kwestia potrzeb. Układ podlewania to dodatkowy zawór/elektroprzełącznik, zbiornik z wodą lub podłączenie do kranu, jakaś pompka do wody lub mechanizm nalewający itd. Stopień komplikacji rośnie znacząco. Ale nie ma problemu by to zrealizować, wystarczy zamiast diody migającej podłączyć coś co uruchomi podlewanie (jakaś pompeczka, i ewentualnie tranzystor gdy trzeba większego prądu) – i sprawa załatwiona. Druga sprawa – dla mnie priorytetem było zasilanie z baterii, dodanie przekaźników i pompek oznacza konieczność dołożenia zasilacza, kabli do 230V i takie tam. W takim wypadku nie ma potrzeby bawienia się w nanoampery i miniaturyzacji układu :-) W moim wypadku wystarczy jak mi miga “przypominacz”, jak ktoś chciałby dodać podlewanie to tylko wyjście sygnału trzeba inaczej wykorzystać niż do włączenia diody.
Czekam na dalsze części – chciałem zbudować coś podobnego, tylko z elektrozaworem podlewającym kwiatki ;)
Super fajny projekt! przeczytałem od deski do deski ale mam jedną gorąca prośbę ;) Zainwestuj 100-150 zł w odrobinkę lepszej klasy multimetr. Na pewno będziesz z niego zadowolony a i wyniki będą bliższe prawdy. Pozdrawiam!
To, jakiej jakości mam miernik jestem w pełni świadomy. Typowy supermarketowy za 10zł sztuka. Tak patrząc “na oko” błąd pomiaru między zakresami to ok. 5%, zakładając że miernik sam z siebie daje też 5% więc mierzę z dokładnością jakieś 10%. I myślę że pomiar małych prądów/napięć też nie są dokładne. W pracy mam oczywiście dużo lepszej klasy przyrządy, w domu korzystam z tego. Dla moich zastosowań taki “szacownik” :-) wystarcza, bo czy przez diodę płynie 2mA czy 1,5mA ma znaczenie drugorzędne.
Głupia moda na mikrokontrolery, cały układ dałoby się zrobić bez atmegi, ale dzisiaj zamiast złożyć układ lepiej zaprogramować atmege
Czy moda… raczej jedno z wielu narzędzi do realizacji pomysłu. Możliwości zrobienia takiego projektu jest dużo, zwykły 555 by wystarczył. Ale 555 w wersji micropower w trybie “micro” ciągnie 3µA, i to bez obciążenia. A Atmega z kompletem obciążeń na start ciągnie 30x miej prądu. I jedno i drugie zajmuje mniej-więcej tyle samo miejsca, kosztuje tyle samo, mikrokontroler ma tę przewagę, że na żądanie mogę zmieniać funkcjonalność. PonadtoCSS555 nie ma na ebay, a 88PA jest :-). Mój pomysł to wersja “jednokwiatkowa”, żeby zrobić wersję “wielokwiatkową” wystarczy że dodam kolejne światełka sterowane z kolejnych wyjść cyfrowych. Bez Atmegi – już zrobiłby się problem konstrukcyjny.
Reasumując – masz rację. Dzisiaj zamiast złożyć układ lepiej jest zaprogramować Atmegę :-) Ja, jako stary elektronik-analogowiec też się z tym muszę zgodzić.
Pingback: Marudnik do kwiatków - część trzecia (i ostatnia) » Majsterkowo.pl
Witam serdecznie. Mam pytanie odnośnie wybudzania atmegi88 z trybu SLEEP_MODE_PWR_DOWN. Chciałbym zamiast wybudzania stanem niskim użyć wybudzania poprzez zmianę stanu na pinie nr2
(attachInterrupt(0,wakeUpNow,CHANGE)).
Wiem że atmega8 umożliwia wybudzenie z tego trybu TYLKO poprzez zadanie stanu niskiego. Jak wygląda sprawa z atmega88?
Jak naradzie udało mi się zablokować dwie atmegii328p, także odradzam wszelkie operacje ze zmianą taktowania, no chyba że ja coś źle robię. Aspusb niestety nie dał rady odblokować.
A ja po wyłączeniu ADC przez wyzerowanie rejestru ADCSRA nic nie uzyskałem… nadal arduino bierze 45mA. Czy to powinno działać ciągle czy tylko przy trybach uśpienia? :)
Witam!
Moje Arduino Nano dziwnie się zachowuje. Niby wszystko ok. ale pod warunkiem , że mam ręce daleko od płytki stykowej. Jeśli jej dotykam
urządzenie natychmiast się wybudza ze stanu uśpienia. Pin 2 nie jest do niczego podpięty. Takie samo zachowanie mam na Crowduino.
Czy może ktoś z Was też miał taki przypadek?
Mam uwagę do tej magii nanoamperów :) Powyższy kod wrzuciłem na Arduino ProMini 328P, 16Mhz, 5V, oczywiście z płytki wylutowałem diodę sygnalizującą zasilanie. Po ululaniu, markowy multimetr Sanwa RD701, pokazuje konsumpcję na stałym poziomie tj. 47,7uA. To bardzo daleko od 100nA. Podobnie ma się sprawa na innym multimetrze DT890C+, marki noname. To tak jakby 47700nA, czyli 477 razy więcej niż w artykule. W czym tkwi ta wielka różnica?