• Witaj na Forum Arduino Polska! Zapraszamy do rejestracji!
  • Znajdziesz tutaj wiele informacji na temat hardware / software.
Witaj! Logowanie Rejestracja


Ocena wątku:
  • 0 głosów - średnia: 0
  • 1
  • 2
  • 3
  • 4
  • 5
SandBox 5 - MenuBackend - strach ma wielkie oczy?
#1
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-...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


Załączone pliki
.zip   MenuBackend_1-1.zip (Rozmiar: 13 KB / Pobrań: 86)
 
Odpowiedź
#2
Jak wywołać klawisze z klawiatury membranowej 1x4, próbuje, mecze się, ale nic mi nie wychodzi? Sad
 
Odpowiedź
#3
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
 
Odpowiedź
#4
Niestety nic z tego, sketch się ładnie kompiluje, ale klawisze nie działają, piny oczywiście poustawiałem, nie wiem co jest.
 
Odpowiedź
#5
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
 
Odpowiedź
#6
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! Smile
 
Odpowiedź
#7
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?
 
Odpowiedź
#8
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.....
Smile
Teraz już jest OK.
 
Odpowiedź
#9
Następne pytanie Smile
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ć?
 
Odpowiedź
#10
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ć".
 
Odpowiedź
  


Skocz do:


Przeglądający: 1 gości