SandBox 5 - MenuBackend - strach ma wielkie oczy? - wojtekizk - 06-02-2016
Witam
Tym razem temat całkiem poważny. Wielu z nas, łącznie ze mną ma lub miało problemy z MenuBackend.
Być może samo zrozumienie podstaw nie jest problemem (odsyłam tutaj do artykułu:
http://majsterkowo.pl/forum/menubackend-jak-sie-w-nim-odnalezc-t1549.html
gdzie starałem się wszystko opisać). Jednak samo stosowanie tego "wynalazku" jest już wyższą szkołą jazdy niestety:-)
Myślę, że jednak warto poświęcić trochę czasu na zrozumienie o co w tym wszystkim biega.
Czym jest owo MenuBackend? To biblioteka (zamieszczam w załączniku), która umożliwia w łatwy sposób tworzyć nawet bardzo rozbudowane MENU dla naszej aplikacji.
Często bowiem borykamy się z dylematem: Mamy gotowy program, który coś tam robi, coś tam mierzy, coś tam pokazuje... ale przydałoby się aby dodać do tego jakieś sensowne MENU, tak aby móc w każdej chwili na przykład zmienić temperaturę załączenia grzałki, czas załączenia timera, załączyć alarm i nie wiem co tam jeszcze.
Słowem potrzebna jest jakaś interakcja. Mamy na przykład nowiusieńką klawiaturkę, joystick, manipulator czy po prostu kilka microswitch-ów i chcemy wykorzystać ich "potencjał" :-)
No i właśnie teraz zaczynają się schody. Kurcze jeśli już coś działa, to albo nie działa tak, jak sobie wyobrażaliśmy albo sam program nie robi tego z naszym Menu, co robił poprawnie bez niego, prawda?
MenuBackend od czasu pierwszego wydania tej biblioteki przeżyło kilka poprawek i w chwili obecnej jest już całkiem fajne. Niestety nie mam wiedzy jak zachowuje się z różnymi wersjami IDE Arduino, miejmy jednak nadzieję, że nie ma niespodzianek (czego nie można powiedzieć o LiquidCrystal_I2C).
Dobra, do rzeczy. Nie wiem czy dzisiaj ukończę ten post. W każdym razie będę tu dodawał kolejne fragmenty, jak by co :-)
Jest konkretny projekt do ogarnięcia - sterowanie domowym zbiorem akwarium,( nie wiem czy dopełniacz liczby mnogiej to "akwariów"), w którym nasz kolega ma obsługę:
- kilku akwariów (ups. gramatyka)
- sterowanie temperaturą
- sterowanie oświetleniem
- sterowanie 3 wentylatorami (pompkami chyba... w sumie nie wiem o co biega, nazwijmy to wentylatory)
- sterowanie wyłącznikiem czasowym o nieznanym bliżej przeznaczeniu.
Założenia projektu - za pomocą 4 przycisków i Menu należy mieć możliwość:
1) ustawienia w dowolnym momencie:
- temperatury załączenia grzałek
- temperatury wyłączenia grzałek
- czasu załączenia oświetlenia
- czasu wyłączenia oświetlenia
- załączenia/wyłączenia każdego z 3 wentylatorów
- ustawiania czasu załączenia i wyłączenia Timera
2) To wszystko musi być niezależne od pracy głównego programu (odczyt temperatur, odmierzanie czasu RTC, sterowanie alarmami przekroczeń dopuszczalnych temperatur, sterowanie wentylatorów... i nie wiem co jeszcze.
Jak widać problem nie jest wcale trywialny. Jak zatem metodycznie podejść do tego zagadnienia?
Moim skromnym zdaniem trzeba je podzielić na 2 funkcjonalne bloki - program główny i obsługę menu.
Jeśli w dużym uproszczeniu uda nam się to zrobić w funkcji loop() to mamy pełen sukces :-)
Zacznijmy od menu:
Kod: // ---------------------------------------------------------------------------------------------------
/* Schemat menu:
root
Temperatura Oswietlenie Wentylatory Timer
T_zal Czas_zal Wentylator 1 Timer_zal.
T_wyl Czas_wyl Wentylator 2 Timer_wyl.
Wentylator 3
*/
// ---- definicja wszystkich opcji menu -------------------------------------------------------------
MenuBackend menu = MenuBackend(co_wybrano, co_zmieniono); // tworzymy obiekt klasy MenuBackend o nazwie menu
// co_wybrano i co zmieniono to funkcje menu, ktore służą do jego obsługi
// poniżej definicja wszystkich opcji menu:
MenuItem miTemperatura = MenuItem("Temperatura"); // obiekt klasy MenuItem o nazwie miTemperatura
MenuItem miT_zal = MenuItem("T zal."); // itd. dla każdej opcji tworzymy instancję (egzemplarz)
MenuItem miT_wyl = MenuItem("T wyl."); // klasy MenuItem
MenuItem miOswietlenie = MenuItem("Oswietlenie");
MenuItem miCzas_zal = MenuItem("Czas_zal");
MenuItem miCzas_wyl = MenuItem("Czas_wyl");
MenuItem miWentylatory = MenuItem("Wentylatory");
MenuItem miW1 = MenuItem("Wentylator 1");
MenuItem miW2 = MenuItem("Wentylator 2");
MenuItem miW3 = MenuItem("Wentylator 3");
MenuItem miTimer = MenuItem("Timer");
MenuItem miTimer_ON = MenuItem("Timer_ON");
MenuItem miTimer_OFF = MenuItem("Timer_OFF"); // koniec, to już wszystkie przykładowe opcje
Każdą linię opatrzyłem stosownym komentarzem, więc powinno być "z górki" :-)
linia:
Kod: MenuBackend menu = MenuBackend(co_wybrano, co_zmieniono);
... odpowiada za utworzenie obiektu klasy MenuBackend o nazwie menu.
Argumentami tego konstruktora (bo tak właśnie nazywa się funkcja która tworzy obiekt każdej klasy), są 2 funkcje, które "potrafią" przekazać informację do samego MENU jaką opcję wybrano i odpowiedzieć co było aktywne przed i po tym wyborze. Czyli w prostych, żołnierskich słowach wiemy gdzie w galęzi tego menu aktualnie jesteśmy - mamy tzw. mapkę nawigacji po menu.
Biblioteka MenuBackend zasadniczo składa się z 2 klas: MenuBackend i MenuItem. Pierwsza to mama dla drugiej.
Jednak związek mama-córka nie jest tutaj najtrafniejszy, ściśle rzecz ujmując raczej obie te klasy są zaprzyjaźnione, bo de facto tak jest właśnie :-) O dobrodziejstwach tej przyjaźni dowiesz się najwięcej i najwięcej zrozumiesz - jeśli sięgniesz po biblię pana Grębosza - Symfonię C++. Sorry ale musiałem zareklamować tę pozycję, bo jest tego warta :-)
Pierwsza klasa opisuje zasadnicze menu, sposób jego organizacji i dostęp do składników i funkcji obsługi. Druga opisuje poszczególne opcje tego menu ( stąd MenuItem właśnie).
W bibliotece MenuBackend jest opisany sposób dodawania tych opcji oraz sposób tworzenia samego menu. Odpowiada za to funkcja menuSetup:
Kod: void menuSetup() // konfiguracja menu (położeń wszystkich opcji)
{
menu.getRoot().add(miOswietlenie); // ustalam rodzica dla opcji Oświetlenie
menu.getRoot().add(miWentylatory); // ustalam rodzica dla opcji Wentylatory
menu.getRoot().add(miTimer); // ustalam rodzica dla opcji Timer
menu.getRoot().add(miTemperatura); // ustalam rodzica dla opcji Temperatura
// bo chcę, aby po wciśnięciu klawisza UP wyjść z MENU (czyli do roota - korzenia)
miTemperatura.add(miT_zal).add(miT_wyl); // Temperatura ma 2 opcje, więc je dodaję (funkcja add)
miTemperatura.addRight(miOswietlenie); // na prawo od Temperatura jest Oświetlenie (funkcja addRight)
miOswietlenie.add(miCzas_zal).add(miCzas_wyl); // Oświetlenie ma 2 opcje, więc je dodaję (funkcja add)
miOswietlenie.addRight(miWentylatory); // na prawo od Oświetlenie są Wentylatory (funkcja addRight)
miWentylatory.add(miW1).add(miW2).add(miW3); // dodaję 3 kolejne opcje dla Wentylatorów (po kolei)
miWentylatory.addRight(miTimer); // na prawo od Wentylatory jest Timer
miTimer.add(miTimer_ON).add(miTimer_OFF); // Timer ma 2 opcje, więc je dodaję
miTimer.addRight(miTemperatura); // zamykam pętlę aby przejść do Temperatura :-)
}
... i tu także jest sporo objaśnień.
Nasze menu składa się z 4 opcji głównych (parent) i szeregu opcji (children).
Jeśli opcje są równorzędne to dodajemy je w poziome za pomocą addRight, addLeft.
Jeśli dodajemy "dziecko" to za pomocą zwykłego add ( jakby w pionie).
W bibliotece MenuBackend jest jeszcze kilka innych opcji, ale o tym potem.
W każdym razie załączony przykład ilustruje jak się takie menu tworzy.
Prawdę mówiąc jest jeszcze sporo do omówienia, więc najlepiej chyba będzie jak zamieszczę cały gotowy przykładowy kod, a jutro opiszę dokładnie o co tam biega.
Do testów wykorzystuję oryginalną klawiaturkę z modułu LCD Keypad Shield (5 przycisków na A0). Mam nadzieję, że nie będzie problemu z ogarnięciem o co chodzi.
Poniżej kod:
Kod: #include <MenuBackend.h> // niezbędna biblioteka MenuBackend
// --- zmienne programu -----------------------------------------------------------------------------
volatile int start=1; // zmienna odpowiedzialna za przełączanie: program -> start=1, menu -> start=0
float Tz=20; // przykładowa temperatura zalączenia grzałki
float Tw=30; // przykładowa temperatura wyłączenia grzałki
int Gz=7; // przykładowa godzina załączenia oświetlenia
int Gw=10; // przykładowa godzina wyłączenia oświetlenia
int Mz=20; // przykładowa minuta załączenia oświetlenia
int Mw=40; // przykładowa minuta wyłączenia oświetlenia
int GMz=Gz*60+Mz; // czas załączenia oświetlenia GMz=Gz*60+Mz
int GMw=Gw*60+Mw; // czas wyłączenia oświetlenia GMw=Gw*60+Mw
int TGz=18; // przykładowa godzina zalączenia timera
int TGw=22; // przykładowa godzina wyłączenia timera
int TMz=20; // przykładowa minuta zalączenia timera
int TMw=40; // przykładowa minuta wyłączenia timera
int TGMz=TGz*60+TMz; // czas załączenia timera TGMz=TGz*60+TMz
int TGMw=TGw*60+TMw; // czas wyłączenia timera TGMw=TGw*60+TMw
int stW1=0; // stan wentylatora 1
int stW2=0; // stan wentylatora 2
int stW3=0; // stan wentylatora 3
// ---------------------------------------------------------------------------------------------------
/* Schemat menu:
root
Temperatura Oswietlenie Wentylatory Timer
T_zal Czas_zal Wentylator 1 Timer_zal.
T_wyl Czas_wyl Wentylator 2 Timer_wyl.
Wentylator 3
*/
// ---- definicja wszystkich opcji menu -------------------------------------------------------------
MenuBackend menu = MenuBackend(co_wybrano, co_zmieniono); // tworzymy obiekt klasy MenuBackend o nazwie menu
// co_wybrano i co zmieniono to funkcje menu, ktore służą do jego obsługi
// poniżej definicja wszystkich opcji menu:
MenuItem miTemperatura = MenuItem("Temperatura"); // obiekt klasy MenuItem o nazwie miTemperatura
MenuItem miT_zal = MenuItem("T zal."); // itd. dla każdej opcji tworzymy instancję (egzemplarz)
MenuItem miT_wyl = MenuItem("T wyl."); // klasy MenuItem
MenuItem miOswietlenie = MenuItem("Oswietlenie");
MenuItem miCzas_zal = MenuItem("Czas_zal");
MenuItem miCzas_wyl = MenuItem("Czas_wyl");
MenuItem miWentylatory = MenuItem("Wentylatory");
MenuItem miW1 = MenuItem("Wentylator 1");
MenuItem miW2 = MenuItem("Wentylator 2");
MenuItem miW3 = MenuItem("Wentylator 3");
MenuItem miTimer = MenuItem("Timer");
MenuItem miTimer_ON = MenuItem("Timer_ON");
MenuItem miTimer_OFF = MenuItem("Timer_OFF"); // koniec, to już wszystkie przykładowe opcje
// ---- funkcje programu -----------------------------------------------------------------------------
void menuSetup() // konfiguracja menu (położeń wszystkich opcji)
{
menu.getRoot().add(miOswietlenie); // ustalam rodzica dla opcji Oświetlenie
menu.getRoot().add(miWentylatory); // ustalam rodzica dla opcji Wentylatory
menu.getRoot().add(miTimer); // ustalam rodzica dla opcji Timer
menu.getRoot().add(miTemperatura); // ustalam rodzica dla opcji Temperatura
// bo chcę, aby po wciśnięciu klawisza UP wyjść z MENU (czyli do roota - korzenia)
miTemperatura.add(miT_zal).add(miT_wyl); // Temperatura ma 2 opcje, więc je dodaję (funkcja add)
miTemperatura.addRight(miOswietlenie); // na prawo od Temperatura jest Oświetlenie (funkcja addRight)
miOswietlenie.add(miCzas_zal).add(miCzas_wyl); // Oświetlenie ma 2 opcje, więc je dodaję (funkcja add)
miOswietlenie.addRight(miWentylatory); // na prawo od Oświetlenie są Wentylatory (funkcja addRight)
miWentylatory.add(miW1).add(miW2).add(miW3); // dodaję 3 kolejne opcje dla Wentylatorów (po kolei)
miWentylatory.addRight(miTimer); // na prawo od Wentylatory jest Timer
miTimer.add(miTimer_ON).add(miTimer_OFF); // Timer ma 2 opcje, więc je dodaję
miTimer.addRight(miTemperatura); // zamykam pętlę aby przejść do Temperatura :-)
}
// ---- funkcja pobiera nazwę wybranej opcji ----------------------------------------------------------
void co_wybrano(MenuUseEvent used)
{
if(start==0) // jeśli obsługujemy menu (start=0)
{
Serial.println("-----------------------------------------------------------------------------------");
Serial.print("Wybrano opcje: < "); // komunikat o tym co wybrano
Serial.print(used.item.getName()); // funkcja getName() zwraca nazwę wybranej opcji
Serial.println(" > To tutaj wstawisz akcje do obslugi tej opcji.");
Serial.println("Na przyklad: if(used.item.getName()==\"W1_ON\") runAkcja_W1();");
Serial.println("... gdzie runAkcja_W1() to funkcja obslugi tego zdarzenia :-)");
Serial.println("-----------------------------------------------------------------------------------");
// poniżej przykładowa obsługa wszystkich opcji Menu :
if(used.item.getName()=="T zal.")Tz=ustawTemp("T zal.",Tz,10,40.0,0.2); // obsługa temp. załącznia
if(used.item.getName()=="T wyl.")Tw=ustawTemp("T wyl.",Tw,10,40.0,0.2); // obsługa temp. wyłączenia
if(used.item.getName()=="Czas_zal")GMz=ustawCzas("Oswietl","Czas zal.",GMz,10); // obsługa czasu zal.
if(used.item.getName()=="Czas_wyl")GMw=ustawCzas("Oswietl","Czas wyl.",GMw,10); // obsługa czasu zal.
if(used.item.getName()=="Wentylator 1")stW1=ustawWent(1); // obsługa wentylatora 1
if(used.item.getName()=="Wentylator 2")stW2=ustawWent(2); // obsługa wentylatora 2
if(used.item.getName()=="Wentylator 3")stW3=ustawWent(3); // obsługa wentylatora 3
if(used.item.getName()=="Timer_ON")TGMz=ustawCzas("Timer","Czas zal.",TGMz,10); // obsługa Timer ON
if(used.item.getName()=="Timer_ON")TGMw=ustawCzas("Timer","Czas wyl.",TGMw,10); // obsługa Timer OFF
}
}
// ---- funkcja identyfikuje aktualną wybraną opcję oraz poprzedni stan --------------------------------
void co_zmieniono(MenuChangeEvent changed)
{
if(changed.to.getName()=="MenuRoot") start=1; // jeśli root to wracamy do obsługi programu głównego
else // poniżej komunikaty kontrolne o tym co było i co wybrano
{
start=0;
Serial.print("Zmiana z: "); //
Serial.print(changed.from.getName()); // skąd (z jakiej opcji)
Serial.print(" na: ");
Serial.println(changed.to.getName()); // dokąd (do jakiej opcji)
}
}
// ---- odczyt klawiatury (tutaj 4 klawisze: Dalej, Plus ,Minus i OK) -----------------------------------
int czytaj() // wersja z klawiaturą modułu LCD Keypad Schield
{
int pinAnalog=0;
int stan_Analog = analogRead(pinAnalog);delay(30);//Serial.println(stan_Analog);
if (stan_Analog > 1000) return -1; // dla wartosci poza zakresem
if (stan_Analog < 50) return 0; // w prawo
if (stan_Analog < 150) return 1; // do gĂłry
if (stan_Analog < 300) return 2; // w dół
if (stan_Analog < 500) return 3; // w lewo
if (stan_Analog < 700) return 4; // OK
return -1; // nic nie wcisnieto
/*
Uwaga!!! Te wartości dla stan_Analog mogą się różnić dla różnych wykonań (klonów tego modułu)
Należy je dobrać doświadczalnie przez odczyt wartości na A0 (funkcja analogRead(A0)).
*/
}
// --- przykładowa funkcja program główny ----------------------------------------------------------------
void program_glowny() // tutaj Twój program wykonuje swoją pracę niezależnie od MENU
{
/*
To tutaj umieszczasz:
- odczyt danych z czujników
- reakcje na przekroczenie temperatury
- wszystkie ważne funkcje wykonywane przez program
*/
Serial.println("=== WYKONUJE PROGRAM GLOWNY ====");
obsluga_menu(); //aby w każdej chwili móc przerwać program i przejść do ustawień menu
}
// --- funkcja do obsługi menu ---------------------------------------------------------------------------
void obsluga_menu()
{
switch(czytaj())
{
case 0:menu.moveRight();
break;
case 1:menu.moveUp();
break;
case 2:menu.moveDown();
break;
case 3:menu.moveLeft();
break;
case 4:menu.use();
break;
}
delay(100); // kosmetyka (dobrać doświadczalnie czas dla autopowtarzania)
}
// -------------------------------------------------------------------------------------------------------
void setup()
{
menuSetup(); // przygotowanie menu do pracy
Serial.begin(9600); // konfiguracja dla Seriala
}
// -------------------------------------------------------------------------------------------------------
void loop() // ma tylko 3 linijki kodu :-)
{
if(start==1) program_glowny(); // jeśli start=1 wykonuje się nasz program
else obsluga_menu(); // jeśli naciśniemy klawisz (tutaj Minus lub DOWN - uruchamiamy menu)
/*
Jeśli chciałbyś aby inny klawisz był odpowiedzialny za przechodzenie pomiędzy Program a Menu,
to musisz uzyć innej funkcji klasy MenuBackend w miejscu tworzenia menu, czyli zamiast:
menu.getRoot().add(miTemperatura);
... użyj na przykład: menu.getRoot().addRight(miTemperatura); lub menu.getRoot().addLeft(miTemperatura);
*/
delay(20); // kosmetyka
}
// === koniec ============================================================================================
// --- funkcja nastawy Temperatury -----------------------------------------------------------------------
float ustawTemp(char s[10], float t, float vmin, float vmax, float skok)
{
Serial.print("Ustawienie: ");Serial.print(s);Serial.print(" = ");Serial.println(t);
int x=czytaj();
while(x!=4)
{
if(x==1){t+=skok;if(t>vmax)t=vmax;}
if(x==2){t-=skok;if(t<vmin)t=vmin;}
x=czytaj();
Serial.print("Ustawienie: ");Serial.print(s);Serial.print(" = ");Serial.println(t);
delay(120);
}
Serial.println("---------------------------------------------------------");
Serial.print("< USTAWIONO ");Serial.print(s);Serial.print(" = ");Serial.print(t);Serial.println(" >");delay(100);
Serial.println("---------------------------------------------------------");
return t;
}
// --- funkcja nastawy Czasu ------------------------------------------------------------------------------
int ustawCzas(char op[14],char s[10],int tt, int skok)
{
int g=tt/60;
int m=tt%60;
Serial.print("Ustawienie: ");Serial.print(s);Serial.print(" = ");Serial.print(g);Serial.print(":");Serial.println(m);
int x=czytaj();
while(x!=4)
{
if(x==1){tt+=skok;if(tt>23*60+50)tt=0;}
if(x==2){tt-=skok;if(tt<0)tt=23*60+50;}
x=czytaj();
g=tt/60;
m=tt%60;
Serial.print("Ustawienie: ");Serial.print(s);Serial.print(" = ");Serial.print(g);Serial.print(":");Serial.println(m);
delay(100);
}
Serial.println("---------------------------------------------------------");
Serial.print("< USTAWIONO ");Serial.print(s);Serial.print(" = ");Serial.print(g);Serial.print(":");
Serial.print(m);Serial.println(" >");
Serial.println("---------------------------------------------------------");
delay(1500);
return tt;
}
// ---- funkcja nastawy Wentylatora -------------------------------------------------------------------------
int ustawWent(int nr)
{
int s;
if(nr==1)s=stW1;if(nr==2)s=stW2;if(nr==3)s=stW3;
Serial.print("Ustawienie: Wentylator ");Serial.print(nr);Serial.print(": stan = ");Serial.println(s);
int x=czytaj();
while(x!=4)
{
if(x==1){s=!s;}
if(x==2){s=!s;}
x=czytaj();
Serial.print("Ustawienie: Wentylator ");Serial.print(nr);Serial.print(": stan = ");Serial.println(s);
delay(120);
}
Serial.println("---------------------------------------------------------");
Serial.print("< USTAWIONO ");Serial.print("Wentylator ");Serial.print(nr);Serial.print(": stan = ");Serial.print(s);
Serial.println(" >");
Serial.println("---------------------------------------------------------");
delay(100);
return s;
}
//-----------------------------------------------------------------------------------------------------------
... a biblioteka w załączniku
Na końcu, za loop-em umieściłem funkcje do obsługi ustawień temperatury i czasu.
Z założenia każda z nich czyta stan klawiatury i dopóki nie wciśnięto OK (tutaj wartość =4), dopóty reagujemy na zmiany klawiszy UP-DOWN (prawo-lewo, czy Plus-Minus... jak kto woli).
Do pełni szczęścia brakuje jeszcze zapisu wartości do EEPROM-a no i całego programu głównego, ale to już zadanie dla autora projektu :-)
Trzeba jeszcze zamienić Seriale na obsługę wyświetlacza LCD - a tu może być pomocny mój poprzedni post:
Sandbox 4 :-)
Pozdrawiam
RE: SandBox 5 - MenuBackend - strach ma wielkie oczy? - Lobaf - 15-02-2016
Jak wywołać klawisze z klawiatury membranowej 1x4, próbuje, mecze się, ale nic mi nie wychodzi?
RE: SandBox 5 - MenuBackend - strach ma wielkie oczy? - wojtekizk - 15-02-2016
Witam
Gdzieś lub kiedyś popełniłem to :-)
Kod: // --- Poniżej definicje funkcji obsługi kilku różnych klawiatur ------------------------------------/
// --- klawiatura analogowa podpięta pod pin A0 np moduł LCD + 5 key firmy DFRobot ------------------/
int czytaj_1()
{
int pinAnalog=0;
int stan_Analog = analogRead(pinAnalog);delay(30);//Serial.println(stan_Analog);
if (stan_Analog > 1000) return -1; // dla wartosci poza zakresem
if (stan_Analog < 50) return 0; // w prawo
if (stan_Analog < 150) return 1; // do gĂłry
if (stan_Analog < 300) return 2; // w dół
if (stan_Analog < 500) return 3; // w lewo
if (stan_Analog < 700) return 4; // OK
return -1; // nic nie wcisnieto
}
// --- popularny joystick z allegro za ok 20 zł. -----------------------------------------------------/
int czytaj_2()
{
int poziom=0;int pion=1;int pinD=4;
// poziom - nr wejścia analogowego do którego podłączona jest manetka joysticka dla ruchu lewo-prawo
// pion - nr wejścia analogowego do którego podłączona jest manetka joysticka dla ruchu góra-dół
// pinD - nr pinu cyfrowego do którego podłączony jest przycisk OK w joysticku
int stan1= analogRead(pion); {delay(60);if(stan1>0)stan1=(stan1+50)/1024+1;}
int stan2= analogRead(poziom); {delay(60);if(stan2>0)stan2=(stan2+50)/1024+1;}
int stanD=digitalRead(pinD);
if(stanD==LOW) return 4; // OK
if(stan1==0) return 2; // w dół
if(stan1==2) return 1; // do gĂłry
if(stan2==0) return 3; // w lewo
if(stan2==2) return 0; // w prawo
return -1; // nic nie wcisnieto
}
// --- wersja dla 5-ciu przycisków cyfrowych --- (np. 2,4,5,6,8 ) -------------------------------------/
int czytaj_3()
{
int gora=2, lewo=4, ok=5, prawo=6,dol=8;
// gora - nr pinu cyfrowego do którego podłączony jest przyciski góra
// lewo - nr pinu cyfrowego do którego podłączony jest przyciski lewo
// ok - nr pinu cyfrowego do którego podłączony jest przyciski OK
// prawo - nr pinu cyfrowego do którego podłączony jest przyciski prawo
// dol - nr pinu cyfrowego do którego podłączony jest przyciski dół
if(digitalRead(gora)==LOW) return 1;
if(digitalRead(lewo)==LOW) return 3;
if(digitalRead(ok)==LOW) return 4;
if(digitalRead(prawo)==LOW) return 0;
if(digitalRead(dol)==LOW) return 2;
return -1;
}
Masz tu 3 różne funkcje do obsługi trzech różnych typów klawiatur.
W Twoim przypadku najlepiej będzie pasować czytaj_3()... jest dla 5 klawiszy ale przecież nie musisz wszystkiego używać.
Dla 4 standardem jest: OK, WSTECZ, GÓRA, DÓŁ (lub prawo lewo).
Pozdrawiam
RE: SandBox 5 - MenuBackend - strach ma wielkie oczy? - Lobaf - 15-02-2016
Niestety nic z tego, sketch się ładnie kompiluje, ale klawisze nie działają, piny oczywiście poustawiałem, nie wiem co jest.
RE: SandBox 5 - MenuBackend - strach ma wielkie oczy? - wojtekizk - 16-02-2016
Witam
Kurcze, jeśli coś nie działa to idziemy wstecz... czyli zamiast klawiatury może zwykły kabelek i zwieramy odp. pin Arduino do masy (jeśli sterujesz stanem niskim lub do +5V dla stanu wysokiego). Wtedy MUSI działać. Jeśli jest OKI to trzeba teraz zabrać się za klawiaturę - oblukać które wyprowadzenie jest wspólne i potem już z górki.
Jeden z podstawowych błędów przy obsłudze klawiatur to zamiana typu sterowania - zwieranie do masy lub do +5V.
Przy zwieraniu do masy pin MUSI być podciągnięty do +5V za pomocą rezystora lub parametru INPUT_PULLUP.
Jeśli zwieramy do + 5V to pin musi być połączony z masą za pomocą rezystora 1,0-2,2 kOHm (równolegle można dać kondensator i problem migotania styków z głowy). Ale pewnie to wszystko już wiesz :-)
Pozdrawiam
RE: SandBox 5 - MenuBackend - strach ma wielkie oczy? - Lobaf - 16-02-2016
Wiesz, w tym przykładzie ( notabene to jeden z Twoich z innego forum ) klawisze działają bez zarzutu, dodam, ze mam jeszcze czwarty przycisk na pinie 3.
Kod: #include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <OneWire.h>
#include <DallasTemperature.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
#define ONE_WIRE_BUS 12
#define prze 8
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
char *k0="Akt.Temp. = ";
char *k1=" <> Temp. = ";
char *k2=" C";
char *k3=" Ustawiono!";
int temp=23;
// int nastaw = 26; // ustaw temperature
volatile int histereza=0;
void setup(void)
{
pinMode(5,INPUT_PULLUP);
pinMode(4,INPUT_PULLUP);
pinMode(6,INPUT_PULLUP);
pinMode(prze,OUTPUT);
lcd.begin(20, 4);
sensors.begin();
Serial.begin(9600);
}
int getButton(int bG, int bD, int oK)
{
if(digitalRead(bG)==LOW){delay(30);if(digitalRead(bG)==LOW)return 2;}
if(digitalRead(bD)==LOW){delay(30);if(digitalRead(bD)==LOW)return 8;}
if(digitalRead(oK)==LOW){delay(30);if(digitalRead(oK)==LOW)return 5;}
return -1;
}
// ---------------------------------------------------------------------------------
//void ekran(char *kL, int v ,char *kP="",int wiersz=1)
//{
// if(wiersz>=0)
// {
// lcd.setCursor(0,wiersz);lcd.print(" ");
// lcd.setCursor(0,wiersz);lcd.print(kL);lcd.print(v);lcd.print(kP);
// }
// else {Serial.print(kL);Serial.print(v);Serial.println(kP);}
//}
// ---------------------------------------------------------------------------------
//void ekran(char *kK,int wiersz=1)
//{
// if(wiersz>=0)
// {
// lcd.setCursor(0,wiersz);lcd.print(" ");
// lcd.setCursor(0,wiersz);lcd.print(kK);
// }
// else Serial.print(kK);
//}
// ---------------------------------------------------------------------------------
int nastawy(int v, int vmin, int vmax)
{
lcd.setCursor (16, 1);
lcd.print(v);
lcd.write(byte(223));
// ekran(k0,v,k2);
int zm=-1;
int x=-1;
while(x!=5)
{
x=getButton(5,4,6);
if(x!=zm)
{
switch(x)
{
case 2: v++;if(v>vmax)v=vmax;lcd.setCursor (16, 1);lcd.print(v);break;
case 8: v--;if(v<vmin)v=vmin;lcd.setCursor (16, 1);lcd.print(v);break;
case 5: lcd.setCursor (16, 1);lcd.print("OK");delay(2000);return v;break;
}
}
zm=x;
}
}
// ---------------------------------------------------------------------------------
void loop() {
sensors.requestTemperatures();
lcd.setCursor(0,3);
lcd.print("T=");
lcd.print(sensors.getTempCByIndex(0));
lcd.print(" ");
lcd.print("N=");
lcd.print(temp);
if (sensors.getTempCByIndex(0) < (temp+histereza)) {
digitalWrite(prze,HIGH);
} else {
histereza=-2;
digitalWrite(prze,LOW);
}{
lcd.setCursor(0,0);lcd.print("< USTAWIENIA >");
temp=nastawy(temp,0,40);
//delay(2000);
}}
Zadziałało w ten sposób:
Kod: void setup()
{
pinMode(3,INPUT_PULLUP);
pinMode(4,INPUT_PULLUP);
pinMode(5,INPUT_PULLUP);
pinMode(6,INPUT_PULLUP);
pinMode(7,INPUT_PULLUP);
menuSetup(); // przygotowanie menu do pracy
Serial.begin(9600); // konfiguracja dla Seriala
}
Kod: int czytaj()
{
int gora=5, lewo=7, ok=6, prawo=4, dol=3;
if(digitalRead(prawo)==LOW){delay(30);if(digitalRead(prawo)==LOW)return 0;}
if(digitalRead(gora)==LOW){delay(30);if(digitalRead(gora)==LOW)return 1;}
if(digitalRead(dol)==LOW){delay(30);if(digitalRead(dol)==LOW)return 2;}
if(digitalRead(lewo)==LOW){delay(30);if(digitalRead(lewo)==LOW)return 3;}
if(digitalRead(ok)==LOW){delay(30);if(digitalRead(ok)==LOW)return 4;}
return -1;
}
No to teraz mogę się bawić! Dziękuję Wojtku!
RE: SandBox 5 - MenuBackend - strach ma wielkie oczy? - samoogon - 15-03-2016
Witam
Przy próbie kompilacji mam taki błąd:
Znaleziono wiele bibliotek w "MenuBackend.h"
Wykorzystane: C:\Users\Przemek\Documents\Arduino\libraries\MenuBackend
Nie wykorzystane: C:\Program Files\Arduino\libraries\MenuBackend
exit status 1
'co_wybrano' was not declared in this scope
W katalogu libraries nie było menubackend, choć na pasku w ide taka się znajdowała, dodałem ręcznie po rozpakowaniu ale nie pomogło.
Przykład z biblioteki również nie działa wyrzuca podobne błędy.
Pomoże ktoś?
Problem podwójnej biblioteki był z mojej winy recznie dodałem folder do libraries w program files a był równie z w moim profilu uzytkownika.
Wróciłem jak było ale nadal błędy jakby nie widział bibliotek:
m:30: error: 'co_wybrano' was not declared in this scope
MenuBackend menu = MenuBackend(co_wybrano, co_zmieniono); // tworzymy obiekt klasy MenuBackend o nazwie menu
^
m:30: error: 'co_zmieniono' was not declared in this scope
MenuBackend menu = MenuBackend(co_wybrano, co_zmieniono); // tworzymy obiekt klasy MenuBackend o nazwie menu
^
C:\Users\Przemek\Documents\Arduino\m\m.ino: In function 'void co_wybrano(MenuUseEvent)':
C:\Users\Przemek\Documents\Arduino\m\m.ino:76:74: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
if(used.item.getName()=="T zal.")Tz=ustawTemp("T zal.",Tz,10,40.0,0.2); // obsługa temp. załącznia
^
C:\Users\Przemek\Documents\Arduino\m\m.ino:77:74: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
if(used.item.getName()=="T wyl.")Tw=ustawTemp("T wyl.",Tw,10,40.0,0.2); // obsługa temp. wyłączenia
^
C:\Users\Przemek\Documents\Arduino\m\m.ino:78:82: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
if(used.item.getName()=="Czas_zal")GMz=ustawCzas("Oswietl","Czas zal.",GMz,10); // obsługa czasu zal.
^
C:\Users\Przemek\Documents\Arduino\m\m.ino:78:82: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
C:\Users\Przemek\Documents\Arduino\m\m.ino:79:82: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
if(used.item.getName()=="Czas_wyl")GMw=ustawCzas("Oswietl","Czas wyl.",GMw,10); // obsługa czasu zal.
^
C:\Users\Przemek\Documents\Arduino\m\m.ino:79:82: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
C:\Users\Przemek\Documents\Arduino\m\m.ino:83:82: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
if(used.item.getName()=="Timer_ON")TGMz=ustawCzas("Timer","Czas zal.",TGMz,10); // obsługa Timer ON
^
C:\Users\Przemek\Documents\Arduino\m\m.ino:83:82: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
C:\Users\Przemek\Documents\Arduino\m\m.ino:84:82: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
if(used.item.getName()=="Timer_ON")TGMw=ustawCzas("Timer","Czas wyl.",TGMw,10); // obsługa Timer OFF
^
C:\Users\Przemek\Documents\Arduino\m\m.ino:84:82: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
exit status 1
'co_wybrano' was not declared in this scope
Co jest grane?
RE: SandBox 5 - MenuBackend - strach ma wielkie oczy? - samoogon - 16-03-2016
Dziękuję za dotychczasową pomoc, pomogło zainstalowanie starszej wersji ide(1.5.5)
Po poprawnej kompilacji w serial monitorze cały czas wykonuje się program główny.
Nic nie jest w stanie go przerwać. Wydaje mi się że po wciśnięciu przycisku powinno się wejść do menu, przyciski I warunki sprawdziłem i troszkę zmodyfikowałem pod swojego schielda. Sam jednak nie jestem pewny jak to powinno działać. Może mi ktoś pomóc to ogarnąć?
Edit:
Już wiem o co chodziło, moje niedopatrzenie nie działał jednak jeden przycisk..... Tak to ten jedyny odpowiedzialny za przełączanie.....
Teraz już jest OK.
RE: SandBox 5 - MenuBackend - strach ma wielkie oczy? - samoogon - 17-03-2016
Następne pytanie
W założeniach program główny miał nie przerywać swojej pracy, dałem u siebie licznik i serial.println i niestety wchodząc do menu program zatrzymuje się i wznawia po wyjsciu.
Nie rozumiem tez zapisu w kodzie:
Kod: // --- przykładowa funkcja program główny ----------------------------------------------------------------
void program_glowny() // tutaj Twój program wykonuje swoją pracę niezależnie od MENU
{
/*
To tutaj umieszczasz:
- odczyt danych z czujników
- reakcje na przekroczenie temperatury
- wszystkie ważne funkcje wykonywane przez program
*/
Serial.println("=== WYKONUJE PROGRAM GLOWNY ====");
obsluga_menu(); //aby w każdej chwili móc przerwać program i przejść do ustawień menu
}
Jest napisane w komentarzu na górze, że program wykonuje swoja pracę niezależnie od menu, a na dole jest funkcja obsluga_menu(); dodana aby w każdej chwili z niego wyjść. Wg mnie jedno przeczy drugiemu. Czy to oznacza że np wchodząc do menu zgaśnie światło i wyłączą się grzałki?
Mógłby ktos mi wyjaśnić?
RE: SandBox 5 - MenuBackend - strach ma wielkie oczy? - True_Brujah - 21-10-2016
Proszę bardzo. Już objaśniam:
Kod: // --- przykładowa funkcja program główny ----------------------------------------------------------------
void program_glowny() // tutaj Twój program wykonuje swoją pracę niezależnie od MENU
{
/*
To tutaj umieszczasz:
- odczyt danych z czujników
- reakcje na przekroczenie temperatury
- wszystkie ważne funkcje wykonywane przez program
*/
Serial.println("=== WYKONUJE PROGRAM GLOWNY ====");
obsluga_menu(); //aby w każdej chwili móc przerwać program i przejść do ustawień menu
}
Jak wiemy program główny jest umieszczony w funkcji void loop () { // jakiś kod programu }
Program wykonuje się do napotkania wywołania funkcji, w naszym przypadku "obsluga_menu()".
Program wyskakuje z naszej pętli głównej i przechodzi do naszej napisanej funkcji "obsluga_menu()", wykonuje to co mu tam nakazaliśmy (obliczenia, wyświetlanie, pomiary i co nam fantazja przyniesie). Po zakończeniu funkcji wraca do pętli głównej programu i go kontynuuje aż do napotkania kolejnej funkcji lub przerwania. W momencie wyjścia z pętli głównej programu stany na wyjściach/wejściach procesora nie zmieniają się. Chyba że mu tak każemy. Jak to mawiała moja nauczycielka informatyki - " Procesor to jest taki mądry głupek. Zrobi wszystko tylko musisz mu powiedzieć jak ma to zrobić".
|