Projekt przedstawia zaawansowany bezprzewodowy układ wyzwalania migawki w aparatach fotograficznych firmy Nikon wykorzystujący komunikację za pośrednictwem portu podczerwieni (Fig. 1). Stanowi on rozwinięcie pomysłu autorstwa Bartosza Zielińskiego opublikowanego na portalu Majsterkowo.pl i przedstawionego w realizacji Arduino: wyzwalanie aparatu firmy Nikon za pomoca portu IR.
Projekt umożliwia wyzwalanie aparatu fotograficznego w trybach zdjęć:
Najważniejszą funkcją układu jest możliwość wpływania na parametry fotografowania w trybie zdjęć seryjnych. System umożliwia konfigurację odstępu z jakim będą robione zdjęcia (interwal) oraz czasu rejestracji sekwencji (czas_sekw.). Sterowanie odbywa się za pomocą kilkupoziomowego menu sterującego. Wielopoziomowe meniu z możliwością ustawienia różnych opcji sterujących to jedno z typowych elementów spotykanych w projektach Arduino. W przedstawianym projekcie wykorzystano bibliotekę MenuBackend autorstwa Alexandra Brevig'a. Nieocenioną pomoc oddał tutorial autorstwa wojtekizk (MenuBackend jak się w nim odnaleźć?).
Układ jest wyposażony w wyświetlacz LCD (2 × 16) umożliwiający nawigację po menu i ustawianie parametrów sekwencji (Fig. 2). Do obsługi menu wykorzystano pięć przycisków sterujących. Cztery z nich umożliwiają nawigację w menu: w dół, w górę, w lewo i w prawo. Ostatni przycisk służy do potwierdzania wartości ustawianych zmiennych i uruchamiania wybranych akcji. Przyciski podłączone są do jednego pinu analogowego Arduino. Zastosowano drabinkę rezystorów opartą na identycznych wartościach rezystancji (Arduino: obsługa przycisków za pomocą wejścia analogowego). Sygnał sterujący portem podczerwieni aparatu generowany jest przez program i następnie jest przekazywany do diody IR. W układzie zastosowano także dwie diody LED. LED1 (czerwona) służy do sygnalizacji wciśnięć przycisków. LED2 (zielona) informuje o wygenerowaniu sygnału sterującego IR w celu zrobienia zdjęcia. Aby obniżyć liczbę połączeń pomiędzy Arduino i wyświetlaczem LCD, zastosowano konwerter I2C.
Układ wyposażono w czteropoziomowe menu sterujące:
ZDJECIE Zrob zdjecie SEKWENCJA Parametry sekw. Zrob sekwencje Ustawienia Manualne Interwał Czas sekwencji Predefiniowane co 1s przez 5s co 1s przez 10s co 1s przez 15s co 3s przez 5s co 3s przez 10s co 3s przez 15s co 5s przez 5s co 5s przez 10s co 5s przez 15s INFO
Pierwszy poziom menu stanowią trzy opcje:
Opcje menu: ZDJĘCIE i SEKWENCJA umożliwiają dostęp do zasadniczych części funkcji urządzenia. Ustawiamy w nich niezbędne parametry i uruchamiamy wybrane opcje. Menu INFO ma za zadanie informować użytkownika o wersji zastosowanego oprogramowania. W menu ZDJĘCIE istnieje tylko jedna opcja: "Zrob zdjecie". Wejście do niej i naciśnięcie przycisku OK spowoduje wygenerowanie sygnału sterującego IR i przy widoczności emitera (diody IR) i detektora (portu IR w aparacie), spowoduje wyzwolenie migawki aparatu.
Najbardziej rozbudowane jest menu trybu zdjęć seryjnych. Pierwsza opcja "Parametry sekw." umożliwia sprawdzenie aktualnych ustawień trybu zdjęć seryjnych. Po wejściu do podmenu wyświetlane są aktualne parametry sekwencji: interwał z jakim będą wykonywane zdjęcia oraz czas wykonywania całej sekwencji (Fig. 3). Zmiana ustawień zmiennych czy to w trybie manualnym:
SEKWENCJA > Ustawienia > Manualne > Interwał/Czas sekwencji
czy też w trybie predefiniowanym:
SEKWENCJA > Ustawienia > Predefiniowane
powodują, że są one globalnie modyfikowane, a zmiana odzwierciedla się także w opisywanej opcji "Parametry sekw.".
Kolejna opcja "Zrób sekwencje" umożliwia uruchomienie mechanizmu generowania sygnału IR zgodnego z ustawieniami parametrów sekwencji (interwału i czasu trwania serii). Wysyłany jest pierwszy sygnał sterujący wyzwoleniem migawki i wykonywane jest pierwsze zdjęcie. Po jego wykonaniu następuje przerwa równa ustawionemu interwałowi. Po jej upłynięciu generowany jest kolejny sygnał sterujący aparatem i wykonywane jest kolejne zdjęcie. Sytuacja powtarza się, aż do momentu upłynięcia całego czasu sekwencji. Po uruchomieniu opcji, na wyświetlaczu pojawia się informacja o liczbie wykonanych zdjęć oraz liczbie wszystkich zdjęć w sekwencji. Zielona dioda LED wskazuje wygenerowanie sygnału IR. Po wykonaniu wszystkich zdjęć sekwencji, zielona dioda LED zostaje zapalona na 5 sekund, a na wyświetlaczu pojawia się komunikat: "Sekw. wykonana". Podczas wykonywania sekwencji zdjęć wyświetlacz LED pozostaje cały czas podświetlony.
Opcja "Ustawienia" umożliwia konfigurację dwóch parametrów właściwych dla trybu zdjęć seryjnych: interwału i czasu wykonywania sekwencji. Użytkownik ma do dyspozycji dwa opcjonalne sposoby wprowadzania zmian:
Wejście do ustawień predefiniowanych umożliwia użytkownikowi wybór jednego z przygotowanych zestawów parametrów sekwencji (Fig. 6).
Dostępne są następujące opcje:
Pierwsza liczba w każdej opcji wyraża czas interwału pomiędzy kolejnymi zdjęciami. Druga liczba definiuje całkowity czas trwania sekwencji na zmontowanym filmie. Dla przykładu: Jeżeli wybrano opcję "co 1s przez 5s", czas trwania sekwencji to 5 sekund. Oznacza to, że aby otrzymać wrażenie płynności ruchu, przy FPS ( - liczbie klatek wyświetlanych na sekundę filmu: 25), należy wykonać 5 × 25 czyli 125 zdjęć. czas rejestracji wszystkich zdjęć wyniesie więc 125 × 1s = 125s, co jest równoważne 2,08 minut. Jeżeli wybrano opcję "co 5s przez 15s", oznacza to, że przy tym samym FPS, robiąć zdjęcie co 5 sekund, będziemy rejestrować 15 × 25 czyli 375 zdjęć. Zaś czas wykonania całej sekwencji wyniesie 375 × 5s = 1875s czyli ponad 31 minut.
| opcja menu | interwał | czas sekwencji | liczba klatek/s | liczba zdjęć | czas rejestracji |
|---|---|---|---|---|---|
| d | ts [s] | FPS | n = ts × FPS | tr = (n × d) / 60 [min] | |
co 1s przez 5s |
1 | 5 | 25 | 125 | 2,08 |
co 1s przez 10s |
1 | 10 | 25 | 250 | 4,16 |
co 1s przez 15s |
1 | 15 | 25 | 375 | 6,25 |
co 3s przez 5s |
3 | 5 | 25 | 125 | 6,25 |
co 3s przez 10s |
3 | 10 | 25 | 250 | 12,5 |
co 3s przez 15s |
3 | 15 | 25 | 375 | 18,75 |
co 5s przez 5s |
5 | 5 | 25 | 125 | 10,41 |
co 5s przez 10s |
5 | 10 | 25 | 250 | 20,83 |
co 5s przez 15s |
5 | 15 | 25 | 375 | 31,25 |
Wejście do ustawień Manualnych umożliwia ręczną modyfikację interwału (Fig. 7) i czasu rejestracji sekwencji (Fig. 8).
Wartości parametrów ustawia się za pomocą przycisów "w górę" i "w dół". Naciśnięcie przycisku "w górę" zwiększa wartość parametru, a naciśnięcie przycisku "w dół" zmniejsza jego wartość. Ustawione wartości wymagają akceptacji za pomoca przycisku "OK".
/*
Przykład użycia biblioteki MenuBackend do obsługi wielopoziomowego menu:
...by wojtekizk (wk@warcaby.lh.pl) przygotowany na forum majasterkowo.pl
//----------------------------------------------------------------------------------
Opis:
Generalnie tworzenie menu w oparciu o bibliotekę MenuBackend jest bardzo proste,
jeśli pamietać o kilku zasadach:
1) Najważniejsza jest konfiguracja Menu w funkcji menuSetup()
2) Poruszanie po menu powinno byc zgodne ze schematem menu, tzn. jeśli
coś jest submenu, to szukasz tego klawiszem "w prawo", a równorzędne "dół - góra"
3) Warto dla użytkownika wydrukować taka mapkę przy klawiaturce.
4) Obsługę wszystkich opcji wrzucaj do funkcji void menuUseEvent(MenuUseEvent used)
Tam zapuszczasz pętle while lub do while i czytasz w niej stan klawiaturki.
W tej pętli modyfikujesz zachowanie programiku w zależności od tego, co wciśniesz.
Jeśli wciśniesz OK to wychodzisz z pętli while i de facto wracasz do loop-a.
Wszystkie ważniejsze kwestie opatrzono dość czytelnym komentarzem.
*/
// ============================================================
#include <Wire.h> // Dołączenie biblioteki Wire (I2C)
#include <LiquidCrystal_I2C.h> // Dołączenie biblioteki I2C dla LCD
#include <MenuBackend.h> // dołączenie biblioteki
// --- definicja znaków strzałek: dół, lewo, prawo, gora-dół i powrót ---
uint8_t arrowUpDown[8] = {
0x4,0xe,0x15,0x4,0x15,0xe,0x4};
uint8_t arrowDown[8] = {
0x4,0x4,0x4,04,0x15,0xe,0x4};
uint8_t arrowRight[8] = {
0x0,0x4,0x2,0x1f,0x2,0x4,0x0};
uint8_t arrowLeft[8] = {
0x0,0x4,0x8,0x1f,0x8,0x4,0x0};
uint8_t arrowBack[8] = {
0x1,0x1,0x5,0x9,0x1f,0x8,0x4};
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Ustawienie adresu LCD na 0x27
volatile int zm =-1; // to dla kontroli zmiany stanu klawiatury
volatile int x=-1; // zmienna pomocnicza
volatile int stan_Analog; // wartość na wejściu analogowym dla klawiaturki analogowej
char *linia1; // pierwsza linia wyświetlanego tekstu na LCD
char *linia2; // druga linia wyświetlanego tekstu na LCD
int a = 0; //zmienna pomocnicza ustawienie początkowe (odczyt PIN-u analogowego)
unsigned long czas; // Zmienna mierząca czas po naciśnięciu przycisku
const int okres_sygn_38kHz = 26; // obsługa IR: okres trwania 1 taktu sygnału o częstotliwości 38kHz [mikrosek.] (T=1/f)
// DOMYŚLNE USTAWIENIA FOTOGRAFICZNE SEKWENCJI
long interwal=3000; // Interwał: 3 sek.
long czas_sekw=5000; // Czas sekwencji: 5 sek.
// -----------------------------opcje Menu: ---------------------------------------
// tworzy obiekty klasy MenuItem, które dziedziczą po klasie MenuBackend
MenuBackend menu = MenuBackend(menuUseEvent,menuChangeEvent); // konstruktor
MenuItem P1 = MenuItem("ZDJECIE",1); // Menu trybu zdjęć pojedynczych
MenuItem P11 = MenuItem("Zrob zdjecie",2); // Wykonanie zdjęcia
MenuItem P2 = MenuItem("SEKWENCJA",1); // Menu trybu zdjęć seryjnych (sekwencji)
MenuItem P21 = MenuItem("Param. sekw.",2); // Wyświetlanie aktualnych parametrów serii (interwału pomiedzy zdjęciami i czasu rejestracji sekwencji)
MenuItem P22 = MenuItem("Zrob sekwencje",2); // Wykonanie sekwencji
MenuItem P23 = MenuItem("Ustawienia",2); // Menu Ustawienia
MenuItem P231 = MenuItem("Manualne",3); // Menu ustawienia manualne
MenuItem P2311 = MenuItem("Interwal",4); // Ustawienia manualne: Interwał
MenuItem P2312 = MenuItem("Czas sekw.",4); // Ustawienia manualne: Czas sekwencji
MenuItem P232 = MenuItem("Predef.",3); // Menu ustawienia predefiniowane
MenuItem P2321 = MenuItem("co 1s (5s)",4); // zdjęcie co 1 sek., sekwencja 5 sek. (125 zdjęć)
MenuItem P2322 = MenuItem("co 1s (10s)",4); // zdjęcie co 1 sek., sekwencja 10 sek. (250 zdjęcia)
MenuItem P2323 = MenuItem("co 1s (15s)",4); // zdjęcie co 1 sek., sekwencja 15 sek. (375 zdjęć)
MenuItem P2324 = MenuItem("co 3s (5s)",4); // zdjęcie co 3 sek., sekwencja 5 sek. (125 zdjęć)
MenuItem P2325 = MenuItem("co 3s (10s)",4); // zdjęcie co 3 sek., sekwencja 10 sek. (250 zdjęć)
MenuItem P2326 = MenuItem("co 3s (15s)",4); // zdjęcie co 3 sek., sekwencja 15 sek. (375 zdjęć)
MenuItem P2327 = MenuItem("co 5s (5s)",4); // zdjęcie co 5 sek., sekwencja 5 sek. (125 zdjęć)
MenuItem P2328 = MenuItem("co 5s (10s)",4); // zdjęcie co 5 sek., sekwencja 10 sek. (250 zdjęć)
MenuItem P2329 = MenuItem("co 5s (15s)",4); // zdjęcie co 5 sek., sekwencja 15 sek. (375 zdjęć)
MenuItem P3 = MenuItem("INFO",1); // Menu info o aplikacji
/* --- Pozycjonowanie menu ( zgodnie z ustawieniem powyżej) ------------
add - dodaje w pionie, addRight - dodaje w poziomie z prawej, addLeft dodaje z lewej
*/
void menuSetup() // Funkcja klasy MenuBackend
{
menu.getRoot().add(P1); // Ustawienie korzenia Menu
P1.add(P11); // rodzic ZDJECIE ma dziecko "Zrob zdjecie"
P11.addLeft(P1);
P1.addRight(P2); // po prawej dla ZDJECIE jest SEKWENCJA
P2.add(P21); // rodzic SEKWENCJA ma dziecko "Parametry sekw"
P21.add(P22);
P21.addLeft(P2); // poniżej "Parametry sekw" jest "Zrob sekwencje"
P22.add(P23);
P22.addLeft(P2); // poniżej "Zrob sekwencje" jest "Ustawienia"
P23.addLeft(P2);
P23.add(P21); // tutaj zamykamy pętlę i wracamy do pierwszej podopcji
P23.addRight(P231); // a tu dziecko "Ustawienia" ma już własne dziecko "Manualne"
// dodajemy z prawej, ponieważ gdybyśmy dali poniżej to zrobilibyśmy
// kolejne dziecko dla SZKIC, a w projekcie jest inaczej
P231.add(P232);
P231.addLeft(P23); // poniżej Manualne jest Predefiniowane
P232.addLeft(P23);
P232.add(P231); // tutaj zamykamy pętlę i wracamy do pierwszej podopcji
P231.addRight(P2311); // INTERWAŁ
P2311.add(P2312); //
P2311.addLeft(P231); // CZAS SEKWENCJI
P2312.addLeft(P231);
P2312.add(P2311); // tutaj zamykamy pętlę i wracamy do pierwszej podopcji
P232.addRight(P2321); // w prawo od Ustawienie predefiniowane przechodzimy do "co 1s (5s)"
P2321.add(P2322); // poniżej "co 1s (5s)" jest "co 1s (10s)"
P2321.addLeft(P232); // w lewo od "co 1s (5s)" przechodzimy do Predefiniowane
P2322.add(P2323); // poniżej "co 1s (10s)" jest "co 1s (15s)"
P2322.addLeft(P232); // w lewo od "co 1s (10s)" przechodzimy do Predefiniowane
P2323.add(P2324); // poniżej "co 1s (15s)" jest "co 3s (5s)"
P2323.addLeft(P232); // w lewo od "co 1s (15s)" przechodzimy do Predefiniowane
P2324.add(P2325); // poniżej "co 3s (5s)" jest "co 3s (10s)"
P2324.addLeft(P232); // w lewo od "co 3s (5s)" przechodzimy do Predefiniowane
P2325.add(P2326); // poniżej "co 3s (10s)" jest "co 3s (15s)"
P2325.addLeft(P232); // w lewo od "co 3s (10s)" przechodzimy do Predefiniowane
P2326.add(P2327); // poniżej "co 3s (15s)" jest "co 5s (5s)"
P2326.addLeft(P232); // w lewo od "co 3s (15s)" przechodzimy do Predefiniowane
P2327.add(P2328); // poniżej "co 5s (5s)" jest "co 5s (10s)"
P2327.addLeft(P232); // w lewo od "co 5s (5s)" przechodzimy do Predefiniowane
P2328.add(P2329); // poniżej "co 5s (10s)" jest "co 5s (15s)"
P2328.addLeft(P232); // w lewo od "co 5s (10s)" przechodzimy do Predefiniowane
P2329.add(P2321); // zamykamy pętlę i wracamy do pierwszej podopcji "co 1s (5s)"
P2.addRight(P3); // na prawo od SEKWENCJA jest INFO
P3.addRight(P1); // zamkniecie pętli głównej, czyli poziomej - z INFO do ZDJECIE
}
// ============================================================================================
// = Funkcje obsługi IR =
// ============================================================================================
// Funkcja opisująca działanie pojedynczego taktu sygnału
void sygnal_38kHZ(){
digitalWrite(8, HIGH);
delayMicroseconds(okres_sygn_38kHz / 2); // połowa okresu => współczynnik wypełnienia = 0,5
digitalWrite(8, LOW);
delayMicroseconds(okres_sygn_38kHz / 2);
}
// ----------------------------------------------------------------------------------
// Funkcja opisująca stan niski
void stan_1(int ilosc_cykli){
for(int i = 0; i < ilosc_cykli; i++)
sygnal_38kHZ();
}
// ----------------------------------------------------------------------------------
// Funkcja opisująca stan wysoki
void stan_0(int ilosc_cykli){
delayMicroseconds(ilosc_cykli * okres_sygn_38kHz);
}
// ----------------------------------------------------------------------------------
// Funkcja generująca sygnał wyzwalający aparaty Nikon
void zrobZdjecie(){
digitalWrite(7, HIGH); // sygnalizacja LED wykonywania zdjęcia
for (int i=0; i < 2; i++) { // sygnał wzrocowy ma być powtórzony 2x
stan_1(76); // 76 cykli "1"
stan_0(1064); // 1064 cykli "0"
stan_1(15); // 15 cykli "1"
stan_0(60); // 60 cykli "0"
stan_1(15); // 15 cykli "1"
stan_0(136); // 136 cykli "0"
stan_1(15); // 15 cykli "1"
stan_0(2423*2); // 2423 cykli "0"
}
delay(100); // trzeba było przedłużyć, gdyż czas wysyłania informacji do aparatu był za krótki
digitalWrite(7, LOW);
}
// ============================================================================================
// Reakcja na wciśnięcie przycisku "OK"
void menuUseEvent(MenuUseEvent used) // funkcja klasy MenuBackend
{
Serial.print("wybrano: ");
Serial.println(used.item.getName()); // do testów, potem niepotrzebne
// AKCJA: ZDJECIE > zrob zdjecie
if (used.item.getName() == "Zrob zdjecie")
{
digitalWrite(7, HIGH);
zrobZdjecie();
delay(400); // szybciej może aparat odmówić posłuszeństwa
digitalWrite(7, LOW);
}
// AKCJA: SEKWENCJA > param. sekw.
if (used.item.getName() == "Param. sekw.")
{
lcd.setCursor(1,0);
lcd.print(" ");
lcd.setCursor(1,0);
lcd.print("co ");
lcd.print(interwal/1000);
lcd.print("s (");
lcd.print(czas_sekw/1000);
lcd.print("s)"); //poprzedni stan LCD
}
// Akcja SEKWENCJA > Zrob sekwencje
if (used.item.getName() == "Zrob sekwencje")
{
int liczba_zdjec = ((czas_sekw/1000)*25);
for(int zdjecie = 0; zdjecie < liczba_zdjec; zdjecie++)
{
digitalWrite(9, LOW);
lcd.setCursor(1,0);
lcd.print(" ");
lcd.setCursor(1,0);
lcd.print("fot.: ");
lcd.print(zdjecie);
lcd.print("(/");
lcd.print(liczba_zdjec);
lcd.print(")");
zrobZdjecie();
delay(interwal);
}
lcd.setCursor(1,0);
lcd.print(" ");
lcd.setCursor(1,0);
lcd.print("Sekw. wykonana");
digitalWrite(7, HIGH); // Zielona dioda LED informuje o wykonaniu sekwencji
delay(5000); // po 5 sek.
digitalWrite(7, LOW); // dioda LED gaśnie
}
// AKCJA: SEKWENCJA > USTAWIENIA > PREDEFINIOWANE > co 1s (5s)
if (used.item.getName() == "co 1s (5s)")
{
interwal=1000;
czas_sekw=5000;
lcd.setCursor(1,0);
lcd.print(" ");
lcd.setCursor(1,0);
lcd.print("ustawiono...");
}
// AKCJA: SEKWENCJA > USTAWIENIA > PREDEFINIOWANE > co 1s (10s)
if (used.item.getName() == "co 1s (10s)")
{
interwal=1000;
czas_sekw=10000;
lcd.setCursor(1,0);
lcd.print(" ");
lcd.setCursor(1,0);
lcd.print("ustawiono...");
}
// AKCJA: SEKWENCJA > USTAWIENIA > PREDEFINIOWANE > co 1s (15s)
if (used.item.getName() == "co 1s (15s)")
{
interwal=1000;
czas_sekw=15000;
lcd.setCursor(1,0);
lcd.print(" ");
lcd.setCursor(1,0);
lcd.print("ustawiono...");
}
// AKCJA: SEKWENCJA > USTAWIENIA > PREDEFINIOWANE > co 3s (5s)
if (used.item.getName() == "co 3s (5s)")
{
interwal=3000;
czas_sekw=5000;
lcd.setCursor(1,0);
lcd.print(" ");
lcd.setCursor(1,0);
lcd.print("ustawiono...");
}
// AKCJA: SEKWENCJA > USTAWIENIA > PREDEFINIOWANE > co 3s (10s)
if (used.item.getName() == "co 3s (10s)")
{
interwal=3000;
czas_sekw=10000;
lcd.setCursor(1,0);
lcd.print(" ");
lcd.setCursor(1,0);
lcd.print("ustawiono...");
}
// AKCJA: SEKWENCJA > USTAWIENIA > PREDEFINIOWANE > co 3s (15s)
if (used.item.getName() == "co 3s (15s)")
{
interwal=3000;
czas_sekw=15000;
lcd.setCursor(1,0);
lcd.print(" ");
lcd.setCursor(1,0);
lcd.print("ustawiono...");
}
// AKCJA: SEKWENCJA > USTAWIENIA > PREDEFINIOWANE > co 5s (5s)
if (used.item.getName() == "co 5s (5s)")
{
interwal=5000;
czas_sekw=5000;
lcd.setCursor(1,0);
lcd.print(" ");
lcd.setCursor(1,0);
lcd.print("ustawiono...");
}
// AKCJA: SEKWENCJA > USTAWIENIA > PREDEFINIOWANE > co 5s (10s)
if (used.item.getName() == "co 5s (10s)")
{
interwal=5000;
czas_sekw=10000;
lcd.setCursor(1,0);
lcd.print(" ");
lcd.setCursor(1,0);
lcd.print("ustawiono...");
}
// AKCJA: SEKWENCJA > USTAWIENIA > PREDEFINIOWANE > co 5s (15s)
if (used.item.getName() == "co 5s (15s)")
{
interwal=5000;
czas_sekw=15000;
lcd.setCursor(1,0);
lcd.print(" ");
lcd.setCursor(1,0);
lcd.print("ustawiono...");
}
// AKCJA: SEKWENCJA > USTAWIENIA > MANUALNE > interwal
if (used.item.getName() == "Interwal")
{
lcd.setCursor(0,0);
lcd.write(7); // Wyswietl symbol strzałki góra-dół
lcd.print(" ");
lcd.setCursor(1,0);
lcd.print("Interwal = ");
lcd.setCursor(13,0);
lcd.print(interwal/1000); // wyświetl akt. interwal
long interwal_sek = interwal/1000;
int akcja=-1;
delay(1000); // zmienna pomocnicza, sterująca dla petli while
// jesli nie puścisz klawisza OK w ciągu 1 sek. to powrót do menu
while(akcja!=4) // ta pętla trwa tak długo aż wciśniesz klawisz OK
{
zm=-1;
akcja=czytaj_1(5); //delay(300); // odczyt stanu klawiatury - funkcja czytaj_1 lub czytaj_2 lub czytaj_3
// opis poniżej przy 3 różnych definicjach funkcji czytaj
if(zm!=akcja) // ruszamy do pracy tylko wtedy gdy zmienił sie stan klawiatury
{
if (akcja==1) { // jesli akcja=1 (wciśnieto klawisz w górę)
interwal_sek++; // zwiększono interwal_sek
if(interwal_sek>99) interwal_sek=99; // ustawiono max próg i wyświetlono obecną interwal_sek
lcd.setCursor(13,0);
lcd.print(interwal_sek);
delay(300);
}
if(akcja==2) { // jesli akcja=2 (wciśnieto klawisz w dół)
interwal_sek--; // Zmniejszono interwal_sek
if(interwal_sek<1) interwal_sek=1; // ustawiono min próg
lcd.setCursor(13,0);
lcd.print(interwal_sek); // wyświetlono aktualny interwal_sek
delay(300);
}
if(akcja==4) // jeśli wciśnieto OK
{
lcd.setCursor(0,0);
lcd.print("*Interwal OK");
delay(2000); // pokazujemy "*Interwal OK" przez 2 sek.
lcd.setCursor(1,0);
lcd.print(" "); // czyścimy linię
lcd.setCursor(1,0);
lcd.print(linia1); // odtwarzamy poprzedni stan na LCD
interwal=interwal_sek*1000; // Aktualizacja zmiennej interwal
}
}
}
zm=akcja; // aktualizacja zmiennej zm, po to aby reagować tylko na zmiany stanu klawiatury
// tu WAŻNY MOMENT - kończy się pętla while i zwracamy sterowanie do głównej pętli loop()
}
// AKCJA: SEKWENCJA > USTAWIENIA > MANUALNE > Czas sekw.
if (used.item.getName() == "Czas sekw.")
{
lcd.setCursor(0,0);
lcd.write(7); // wyswietlamy symbol strzałki góra-dół
lcd.print(" ");
lcd.setCursor(1,0);
lcd.print("Czas sekw. = "); // tekst dla użytkownika
lcd.setCursor(13,0);
lcd.print(czas_sekw/1000); // wyświetlamy akt. interwal
long czas_sekw_sek = czas_sekw/1000;
int akcja=-1;
delay(1000); // zmienna pomocnicza, sterująca dla petli while
// jesli nie puścisz klawisza OK w ciągu 1 sek. to powrót do menu
while(akcja!=4) // ta pętla trwa tak długo aż wciśniesz klawisz OK
{
zm=-1;
akcja=czytaj_1(5); // odczyt stanu klawiatury - funkcja czytaj_1
if(zm!=akcja) // ruszamy do pracy tylko wtedy gdy zmienił sie stan klawiatury
{
if (akcja==1) { // jesli akcja=1 (wciśnieto klawisz w górę)
czas_sekw_sek++; // zwiększonie czas_sekw_sek
if(czas_sekw_sek>99) czas_sekw_sek=99; // ustawiono max próg
lcd.setCursor(13,0);
lcd.print(czas_sekw_sek); // wyświetlono aktualny czas_sekw_sek
delay(300);
}
if(akcja==2) { // jesli akcja=2 (wciśnieto klawisz w dół)
czas_sekw_sek--; // zmniejszono czas_sekw_sek
if(czas_sekw_sek<1) czas_sekw_sek=1; // ustawiono min próg
lcd.setCursor(13,0);
lcd.print(czas_sekw_sek); // wyświetlono aktualny czas_sekw_sek
delay(300);
}
if(akcja==4) // jeśli wciśnieto OK
{
lcd.setCursor(0,0);
lcd.print("*Interwal OK");
delay(2000); // pokazujemy "*Interwal OK" przez 2 sek.
lcd.setCursor(1,0);
lcd.print(" "); // czyścimy linię
lcd.setCursor(1,0);
lcd.print(linia1); // odtwarzamy poprzedni stan na LCD
czas_sekw=czas_sekw_sek*1000; // Aktualizacja zmiennej czas_sekw [ms]
}
}
}
zm=akcja; // aktualizacja zmiennej zm, po to aby reagować tylko na zmiany stanu klawiatury
// tu WAŻNY MOMENT - kończy się pętla while i zwracamy sterowanie do głównej pętli loop()
}
// AKCJA: INFO
if (used.item.getName() == "INFO")
{
lcd.setCursor(1,0);
lcd.print(" ");
lcd.setCursor(1,0);
lcd.print("Ver.1.0 (2016)");
}
}
// --- Reakcja na wciśnięcie klawisza -----------------------------------------------------------------
void menuChangeEvent(MenuChangeEvent changed) // funkcja klasy MenuBackend
{
/* tak naprawdę to tylko tutaj przydaje się ów shortkey i służy przede wszystkim do wzbogacenia menu
o symbole strzałek w zależności co wybrano. Wszystko co tutaj się wyprawia jest pokazywane na LCD.
*/
int c=changed.to.getShortkey(); // pobieramy shortkey (1,2,3, lub 4)
lcd.clear();
lcd.setCursor(0,0);
if(c==1) // jeśli to menu głowne (shortkey=1) to:
{
lcd.write(3); // strzałka w lewo
strcpy(linia1,changed.to.getName()); // tworzymy napis w pierwszej linii
lcd.print(linia1); // wyświetlamy ją
lcd.setCursor(15,0);
lcd.write(4); // strzałka w prawo
lcd.setCursor(0,1);
lcd.write(5); // strzałka w dół
lcd.setCursor(15,1);
lcd.write(5); // strzałka w dół
}
if(c==2) // jeśli to podmenu dla dziecka - (shortkey=2) to:
{
lcd.print("*"); // rysujemy gwiazdkę
strcpy(linia2,changed.to.getName()); // tworzymy napis w pierwszej linii
lcd.print(linia1); // wyświetlamy ją
lcd.setCursor(15,0);
lcd.print("*"); // gwiazdka
lcd.setCursor(0,1);
lcd.write(6); // druga linia i strzałka powrotu (arrowBack)
lcd.print(changed.to.getName()); // wyświetlamy nazwe "dziecka"
lcd.setCursor(15,1);
lcd.write(7); // strzałka góra-dół
}
if(c==3) // jeśli dziecko ma dziecko - (shortkey =3) to:
{
lcd.print("*"); // gwiazdka
strcpy(linia2,changed.to.getName()); // kopiujemy akt. nazwe opcji menu do zmiennej linia2
lcd.print(linia1); // i wyświetlamy pierwszą linię
lcd.setCursor(15,0);
lcd.print("*"); // gwiazdka
lcd.setCursor(0,1);
lcd.write(6); // druga linia i strzałka arrowBack
lcd.print(changed.to.getName()); // wyświetlamy wnuka w drugiej linii
lcd.setCursor(15,1);
lcd.write(4); // strzałka w prawo bo są wnuki
}
if(c==4) // jeśli to wnuk (shortkey =4) to:
{
lcd.print("*"); // gwaizdka
lcd.print(linia2); // w pierwszej linii wyświetlamy dziecko ( czyli rodzica wnuka)
lcd.setCursor(15,0);
lcd.print("*"); // gwaizdka
lcd.setCursor(0,1);
lcd.write(6); // druga linia i strzałka arrowBack
lcd.print(changed.to.getName()); // wyświetlamy wnuka
lcd.setCursor(15,1);
lcd.write(7); // strzałka góra-dół
}
}
// --- Funkcja odczytująca stan klawiatury --------------------------------------------------
// --- wersja dla klawiatury 5-cio przyciskowej ---------------------------------------------
volatile int czytaj_1(int analog)
{
int stan_Analog = analogRead(analog);
delay(30); //Serial.println(stan_Analog);
if (stan_Analog > 1000) return -1; // dla wartosci poza zakresem
if (stan_Analog < 140) return 2; // "w dół"
if (stan_Analog < 600) return 1; // "do góry"
if (stan_Analog < 700) return 3; // "w lewo"
if (stan_Analog < 800) return 0; // "w prawo"
if (stan_Analog < 900) return 4; // "OK"
return -1; // nic nie wciśnięto
}
void setup()
{
linia1=new char[16]; // zainicjowanie dynamicznego wskaźnika do tekstu
linia2=new char[16]; // to BARDZO WAŻNE, bo wskażnik dynamiczny musi wskazywać na
// z góry określone miejsce w pamieci. Gdybyśmy tego nie zrobili
// to wcześniej czy później programik mógłby wskoczyć w nieokreślony
// bliżej obszar pamięci, co może skutkować nieodwracalnymi konsekwencjami
// łącznie z przestawieniem Fuse Bitów !!!
// Proszę uważać na wszystkie dynamiczne wskaźniki, TAKA DOBRA RADA :-)
Serial.begin(9600); // inicjacja Serial portu (testy)
lcd.begin(16, 2); // inicjacja LCD
lcd.createChar(3,arrowLeft); // tworzymy w pamięci LCD 5 własnych znaków dla strzałek
lcd.createChar(4,arrowRight);
lcd.createChar(5,arrowDown);
lcd.createChar(6,arrowBack);
lcd.createChar(7,arrowUpDown);
lcd.noBacklight(); // Wyłączenie podświetlenia LCD
pinMode(A5, INPUT_PULLUP); // Pin analogowy przycisków
pinMode(9, OUTPUT); // Pin cyfrowy LED sygnalzujący naciśnięcie przycisku
digitalWrite(9, LOW); // wygaszenie LED sygnalizującej naciśnięcie dowolnego przycisku
pinMode(8, OUTPUT); // Pin cyfrowy diody IR sterujący wyzwalaczem
pinMode(7, OUTPUT); // Pin cyfrowy LED sygnalzujący wykonanie zdjęcia
menuSetup(); // funkcja klasy MenuBackend - tu tak naprawdę tworzymy nasze menu
menu.moveDown(); // idziemy do pierwszej opcji - PLIK, moveDown bo pierwotnie byliśmy w root
// to tak jak w Awatarze drzewa rosną korzeniami do góry :-)
}
void loop()
{
x=czytaj_1(5);
delay(30); // odczytujemy stan klawiatury:
/*
Ja używam funkcji czytaj_1() bo mam akurat klawiaturkę podpiętą pod A5
Jeśli masz inna klawiaturkę to użyj funkcji czytaj_2 lub czytaj_3 - patrz opis
Ponadto musisz pamietać że w funkcji obsługo klawisza OK - menuUseEvent(MenuUseEvent used)
także musisz użyć odpowiedniej wersji funkcji czytaj !!!
*/
if(zm!=x) // jesli była zmiana stanu to :
{
switch(x) // sprawdzamy co nacisnieto
{
case 0:
menu.moveRight();
break; // jesli naciśnięto klawisz w Prawo to przesuń menu w prawo
case 1:
menu.moveUp();
break; // menu do góry
case 2:
menu.moveDown();
break; // menu w dół
case 3:
menu.moveLeft();
break; // menu w lewo
case 4:
menu.use();
break; // wciśnięto "OK" więc skok do funkcji menuUseEvent(MenuUseEvend used)
// to w tej funkcji właśnie obsługujemy nasze Menu, tu sprawdzamy
// jaką opcję wybrano i tutaj tworzymy kod do obslugi zdarzenia.
}
}
zm=x; // przypisanie zmiennej zm wartości x po to, aby dluższe wciskanie tego
// samego klawisza nie powodowało ponownej generacji zdarzenia.
// program reaguje na zmianę stanu klawiatury.
a = analogRead(5);
if (a < 900){ // Zdarzenie polegające na naciśnięciu dowolnego przycisku
digitalWrite(9, HIGH); // Włączenie LED sygnalizującej naciśnięcie przycisku
czas = millis(); // zaczynamy zliczać czas ()
}
else{
digitalWrite(9, LOW);
}
if (millis() - czas <= 5000){ // Jeżeli nie upłynęło 5 sek:
lcd.backlight(); // Podświetl ekran LCD
}
else
{
lcd.noBacklight(); // Wyłącz podświetlenie LCD
}
// Serial.println(analogRead(5));
// Serial.print("Interwal: ");
// Serial.print(interwal/1000);
// Serial.print("s, czas: ");
// Serial.print(czas_sekw/1000);
// Serial.println("s, ");
}