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


Ocena wątku:
  • 1 głosów - średnia: 5
  • 1
  • 2
  • 3
  • 4
  • 5
Gotowiec 1 - obsługa trzech przycisków
#1
Witam
W pierwszej mojej odsłonie na tym forum temat niby banalny, jednak część kolegów ma z tym problemy. Mianowicie jak obsłużyć np. zmianę jakiegoś parametru (wartości) w swoim programiku. 
Przykład:
Masz czujnik temperatury i chciałbyś zrobić termostat do Twojej szklarni, w której rosną (mają rosnąć) Twoje ulubione zielone Maryśki :-). Zatem potrzebujesz narzędzia - programiku, który obsłuży za pomocą 3 przycisków zmianę nastaw temperatury. Ale chwila... za jakiś czas będziesz budował kolejne urządzenie i temat wróci jak bumerang.
Zatem zastanówmy się wspólnie, czy nie lepiej napisać sobie zestaw gotowych funkcji do wielokrotnego użytku?
Mogę zaproponować jedno z rozwiązań :-) Co będzie potrzebne?
1) Funkcja odczytu który klawisz został wybrany - getButton(...)
2) Funkcja wyświetlania nastaw naszego parametru na ekranie LCD lub na Serialu - ekran(...)
3) Funkcja do zmiany nastaw tej właśnie wielkości - nastawy(...)
Omówię po kolei każdą z nich:
Cytat: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;
}

-----------------
Funkcja getButton wywoływana jest z 3 parametrami - numerami pinów cyfrowych odpowiedzialnych za zmianę parametru w górę ( bG ), w dół (bD) i klawisz OK (oK)... a zwraca wartość typu int (2, 8, 5 lub -1). Czemu akurat takie?
Przez analogię do klawiatury w telefonie np. 2 - u góry, 5 w środku i 8 na dole :-)
Wartość zwracana -1 jest wtedy, kiedy niczego nie wciskamy.
Przykład wywołania funkcji:
Cytat:    x=getButton(2,3,11);

... gdzie klawisz UP jest na pinie 2, klawisz DOWN jest na pinie 3 a klawisz OK na pinie 11
-----------------
Funkcja ekran - ma wyświetlać wyniki naszej zmiany nastaw parametru (np. temperatury). Większość takich rozwiązań wyświetla po prostu w jednej linii LCD lub Seriala jakiś komunikat, np. " Temp. =", potem podaje się wartość temperatury i na koniec ew. jednostkę " C". Czyli w sumie do wyświetlenia 3 składniki:
Komunikat lewy (kL), wartość (v), i komunikat prawy (kP). Dodatkowo funkcja rozpoznaje czy wynik ma być wyświetlany na LCD czy na Serialu, poprzez 4-ty parametr funkcji (wiersz=1). 
Cytat: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);}
}

Zmienna wiersz ma domyślnie wartość =1, co oznacza, że dane są wyświetlane w drugim wierszu. Jeśli wartość wiersz =0 to wyświetlanie w pierwszym wierszu i uwaga - dla -1 dane będą wyświetlane na Serialu. dzięki temu funkcja sama wybiera gdzie mają być wyświetlane komunikaty.
Przykład wywołania funkcji:
Kod:
char *k1=" <> Temp. = "; // przykładowy komunikat Lewy
char *k2=" C";                // przykładowy komunikat prawy
int v=21;                       // przykładowa temperatura
ekran(k1,v,k2);              // w drugiej linii LCD: " <> Temp. = 21 C"
ekran(k1,v);                  // pominięto parametr k2 czyli : " <> Temp. = 21" , bo czasem nie będzie potrzebny 
ekran(k1,v,k2,-1);         // na Serialu: " <> Temp. = 21 C"

Dodatkowo jest jeszcze jedna, opcjonalna wersja funkcji ekran. Ma ona tę samą nazwę ale inną ilość parametrów, czyli wykorzystujemy tu mechanizm przeładowana nazwy (niektórzy opisują to jako przeciążenie), jednak przeładowanie dla tłumaczenia słowa overloading jest tutaj chyba bardziej odpowiednie :-)
Ta skrócona wersja funkcji ekran po prostu wyświetla w określonej linii jeden komunikat:
Cytat: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);
}    

-----------------
Funkcja nastawy do działania potrzebuje 3 parametrów: akt. wartości , wartości minimalnej dla zakresu nastaw i wartości maksymalnej, ponieważ określony parametr (np. temperaturę), trzeba będzie zmieniać w jakimś zakresie.
Funkcja sama kontroluje ten zakres. W ciele funkcji wykonywane są juz wcześniej opisane funkcje - getButton i ekran. Funkcja nastawy zwraca zaktualizowaną wartość v (temperatury).
Cytat:int nastawy(int v, int vmin, int vmax)
{
  ekran(k0,v,k2);
  int zm=-1;
  int x=-1;
  while(x!=5)
    {
    x=getButton(2,3,11);
    if(x!=zm)
      {
      switch(x)
        {
        case 2: v++;if(v>vmax)v=vmax;ekran(k1,v,k2);break;
        case 8: v--;if(v<vmin)v=vmin;ekran(k1,v,k2);break;
        case 5: ekran(k3);delay(2000);ekran("");return v;break;
        }
      }
     zm=x; 
    }
}

Opis ważniejszych fragmentów funkcji nastawy:
ekran(k0,v,k2); - k0 to np." Akt.Temp. = " , czyli pokaz aktualnej temperatury, k2 to: " C" (jednostka)
int zm=-1; - zmienna pomocnicza, dla rozpoznania zmiany stanu klawiszy
int x=-1; - zmienna pomocnicza, do niej zwraca wartość funkcja getButton
while(x!=5) - pętla wykonująca się tak długo aż wciśniesz OK (czyli x=5)
W pętli while czytamy stan klawiszy i wynik przekazujemy do switch-a, gdzie jeśli:
- wciśnięto UP -to: inkrementacja zmiennej v,sprawdzenie zakresu,wywołanie funkcji ekran
- wciśnięto DOWN to: dekrementacja zmiennej v, sprawdzenie zakresu, wywołanie funkcji ekran
- wciśnięto OK to: przeładowana wersja funkcji (ta z jednym komunikatem), czekamy 2 sek. i czyścimy ekran a następnie zaktualizowany wynik zwracamy do zmiennej v.
przypisanie zm=x; przeciwdziała autopowtarzaniu przy wciśniętym klawiszu. Dzięki temu zmiana wielkości nastaw ma miejsce tylko podczas kolejnych przyciśnięć klawiszy.
Mam nadzieję, że wszystko jasne :-)
Na koniec przykładowy kod programuSadklawisze na pinach 2,3,11; lcd na pinach 8,9,4,5,6,7)
Cytat:#include <LiquidCrystal.h>
LiquidCrystal lcd(8,9,4,5,6,7);
char *k0="Akt.Temp. = ";
char *k1=" <> Temp. = ";
char *k2=" C";
char *k3=" Ustawiono!";
int temp=20;
// --------------------------------------------------------------------------------
void setup()
{
  pinMode(2,INPUT_PULLUP);
  pinMode(3,INPUT_PULLUP);
  pinMode(11,INPUT_PULLUP);
  Serial.begin(9600);
  lcd.begin(16,2);
}
// ---------------------------------------------------------------------------------
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)
{
  ekran(k0,v,k2);
  int zm=-1;
  int x=-1;
  while(x!=5)
    {
    x=getButton(2,3,11);
    if(x!=zm)
      {
      switch(x)
        {
        case 2: v++;if(v>vmax)v=vmax;ekran(k1,v,k2);break;
        case 8: v--;if(v<vmin)v=vmin;ekran(k1,v,k2);break;
        case 5: ekran(k3);delay(2000);ekran("");return v;break;
        }
      }
     zm=x; 
    }
}
// ---------------------------------------------------------------------------------
void loop()
{
lcd.setCursor(0,0);lcd.print("< USTAWIENIA >");
temp=nastawy(temp,0,40);
//delay(2000);
}

Co można zmienić? 
Napisać te same funkcje dla zmiennych typu float lub uruchomić mechanizm autoinkrementacji przy wciśniętym na dłużej przycisku... słowem dokonać adaptacji kodu do swoich potrzeb. Czy tak rozwlekły tutorial może być przydatny w Waszych projektach? Sami oceńcie :-)
Pozdrawiam
 
Odpowiedź
#2
Fajny ten gotowiec tylko jak go podstawiłem do swojego termostatu to wyszły mi takie fanaberie że nie potrafiłem tego w ogóle ogarnąć chociaż próbowałem na kilka sposobów. Może ty dasz radę?
To mój kod termostatu.

Kod:
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
#define ONE_WIRE_BUS 11
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

void setup(void)
{
pinMode(13,OUTPUT);
lcd.begin(20, 4);
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("T.Pieca    =");
lcd.setCursor(0,1);
lcd.print("T.Boilera  =");
lcd.setCursor(0,2);
lcd.print("Stan pompy =");
sensors.begin();
}
void loop(void)
{
sensors.requestTemperatures();
lcd.setCursor(14,0);
lcd.print(sensors.getTempCByIndex(0));
lcd.setCursor(14,1);
lcd.print(sensors.getTempCByIndex(1));

if (sensors.getTempCByIndex(0)>=28.00){digitalWrite(13,HIGH);
lcd.setCursor(14,2);
lcd.print("PRACA");

}
if (sensors.getTempCByIndex(0)<=25.00){digitalWrite(13,LOW);
lcd.setCursor(14,2);

lcd.print("STOP ");
}


delay(10);
}
 
Odpowiedź
  


Skocz do:


Przeglądający: 1 gości