[028] Zegar cz. 1

[028] Zegar cz. 1

Zrobimy sobie budzik. Najpierw powstanie część zegarowa, w następnej dopiszemy część budzącą. Urządzenie nasze będzie używać znanego nam już ekranu lcd oraz prościutkiej klawiatury z trzema przyciskami. Pierwszym przyciskiem będziemy zwiększać wyświetlane wartości, drugim zmniejszać, a trzecim – poruszać się po menu. Szczegóły połączeń opisałem już kiedyś w tym artykule i informacji na ten temat proszę szukać tam. Oczywiście docelowo klawiatura jak i całość może wyglądać zgodnie z wizją budującego zegar.


Wbrew pozorom, mimo użycia dwóch bibliotek projekt należy to kategorii projektów o średnim stopniu zaawansowania, a dlaczego – zobaczymy za chwilę. Przy okazji przedstawię jeden ze sposobów uruchamiania takich projektów. Jest ich kilka, moim ulubionym jest tak zwana metoda małych, a pewnych kroków. Urządzenie uruchamiamy krótkimi etapami, a jeśli jakiś sprawia problemy – wycofujemy się do ostatniej klarownej sytuacji i wprowadzamy zmiany dosłownie linia po linii.

#include<LiquidCrystal.h>            // To jest biblioteka obsługi wyświetlacza.
#include<TimeLib.h>                  // To jest biblioteka obsługi zegara.
LiquidCrystal lcd(2, 3, 4, 5, 6, 7); // Tutaj dostarcza się bibliotece obsługi wyświetlacza wiedzy, gdzie co jest podłączone.
const byte pstryczekMinus = 8;       // Adres pstryczka zmniejszającego wartości.
const byte pstryczekZmien = 9;       // Adres pstryczka ustawień.
const byte pstryczekPlus = 10;       // Adres pstryczka zwiększającego wartości.
void setup() {
  lcd.begin(16, 2);                      // Inicjujemy wyświetlacz, powiadamiając go o ilości kolumn i wierszy.
  pinMode(pstryczekMinus, INPUT_PULLUP); // Deklarujemy linie pstryczków jako wejścia podciągnięte wewnętrznie do wysokiego stanu.
  pinMode(pstryczekZmien, INPUT_PULLUP);
  pinMode(pstryczekPlus, INPUT_PULLUP);
}
void loop() {
}

Początek nie różni się wiele od poprzedniego projektu, ale znajdziemy tutaj coś nowego: bibliotekę TimeLib. Jeśli dotąd nie używaliśmy jej nigdy, nie pojawi się ona w systemie i trzeba będzie ją zainstalować, choćby przez Menedżer bibliotek. Po wpisaniu jej nazwy, powinna się dać znaleźć, pobrać i zainstalować w pełni automatycznie. Biblioteka ta, najogólniej mówiąc, dostarczy nam programowy zegar czasu rzeczywistego, którego działania nie musimy rozumieć, ufając po prostu, że działa jak należy. Programowy – znaczy nie potrzebujący jakiegokolwiek dodatkowego elementu. Szczegóły przedstawiłem kiedyś w tym artykule, a przypomnimy sobie je za chwilę. Będziemy używać także trzech przycisków ze wspomnianego projektu, tylko zmienimy im nazwy: na PstryczekMinus, PstryczekZmien i PstryczekPlus.

Zakładając, że cały widoczny kod jest prawidłowy, zróbmy pierwszy mały krok: sprawdzimy czy wszystko działa, a przy okazji czy biblioteki pracują poprawnie. Najłatwiej będzie napisać te dwie linie.

void loop() {
  lcd.home();          // Ustaw kursor na początku.
  lcd.print(second()); // Rysuj liczbę sekund.
}

Pierwsza każe kursorowi lądować na początku ekranu, druga – wyświetla sekundnik i będzie to nasze pierwsze tutaj użycie biblioteki TimeLib. Jak widać, dostaliśmy po prostu zestaw nowych zmiennych, do których możemy się odwołać bezpośrednio – w tym wypadku zmienna nazywa się second i zwraca liczbę sekund pracującego w tle zegara.

Jeśli po kompilacji ujrzymy „żywy” sekundnik - wszystko jest w porządku. Wyświetlimy teraz cały zegar. Godziny, minuty i sekundy pozyskamy z biblioteki zgodnie z jej instrukcją. Pomiędzy wartości wrzucimy dwukropki, już z nowo poznanym dodatkiem F, który pozwala oszczędzać pamięć.

void loop() {
  lcd.home();          // Ustaw kursor na początku.
  lcd.print(hour());   // Rysuj liczbę godzin.
  lcd.print(F(":"));   // Rysuj dwukropek.
  lcd.print(minute()); // Rysuj liczbę minut.
  lcd.print(F(":"));   // Rysuj dwukropek.
  lcd.print(second()); // Rysuj liczbę sekund.
}

I znowu wszystko pracuje jak należy, choć… nie do końca.

Gdy minut będzie mniej niż 10, najpierw powinno pojawić się zero. Podobnie z sekundami. A w przypadku godzin winien tam tkwić odstęp albo także zero – wedle konceptu budowniczego zegara, by dwukropki nie przesuwały się w zależności od pory dnia. Jak sobie z tym poradzić? Poznamy teraz elementy formatowania liczb. Można to robić na kilka sposobów, my to zrobimy może mniej „informatycznie”, ale na tym etapie przejrzyściej. Ale za chwilę.

Wcześniej poznamy kolejną generalną zasadę informatyków. Jeśli mamy jakiekolwiek operacje używające zegarów czy ogólniej: zmiennych zmieniających się w czasie nie do przewidzenia i jest ich kilka – czyli jak tutaj, trzy: godziny, minuty i sekundy, nie wolno, a w każdym razie nie powinno się ich pobierać na raty. Czyli najpierw godziny, potem minuty i tak dalej. Dlaczego?

Załóżmy, że mamy godzinę 23:59:59. Odczytujemy liczbę godzin, coś tam z tą liczbą robimy, następnie odczytujemy liczbę minut. Ale w międzyczasie czas zmienił się na północ. Zatem zapamiętana liczba godzin to 23, ale liczba minut to już 0. Nasze działania będą interpretowane jakby była godzina 23:00, a nie północ.

Trzeba mieć dużo szczęścia, żeby tak akurat trafić. Poza tym często nic złego z tego nie wyniknie. Jednak gdyby tak pracowały komputery międzynarodowych giełd, mielibyśmy niewesoło. Rozwiązanie jest proste: należy przeczytać całą wartość czasu naraz i przenieść ją do pamięci, gdzie już się nie zmieni i będzie można poszczególne składniki analizować na raty. Biblioteka TimeLib ma do tego celu specjalną instrukcję now(). Za jednym zamachem zapisuje we własnych zmiennych wszystkie elementy zegara, łącznie z kalendarzem, którego na razie nie będziemy używać. Pozostaje teraz pozamieniać wywołania do kolejnych składników już nie bezpośrednio do zegara, a jego zapisanego stanu. Stan ten nazwałem sobie własną nazwą: CzasOdczytany.

void loop() {
  time_t czasOdczytany = now();     // Zapisz bieżący czas do zmiennej czasOdczytany.
  lcd.home();                       // Ustaw kursor na początku.
  lcd.print(hour(czasOdczytany));   // Rysuj liczbę godzin.
  lcd.print(F(":"));                // Rysuj dwukropek.
  lcd.print(minute(czasOdczytany)); // Rysuj liczbę minut.
  lcd.print(F(":"));                // Rysuj dwukropek.
  lcd.print(second(czasOdczytany)); // Rysuj liczbę sekund.
}

Możemy dla pewności wysłać program do Arduino. Będzie pracować dokładnie tak samo. Zabierzmy się teraz za te nieszczęśliwe braki zer. Zaczniemy od sekund. W tym celu musimy: po pierwsze zadeklarować nową zmienną o nazwie sekunda, na której będziemy wykonywać stosowne operacje. Pamiętajmy, by tym razem nie stawiać wcześniej wyrażenia const, bo ta wartość będzie się zmieniać. A ponieważ liczba sekund nigdy nie przekracza wartości 59, wystarczy deklaracja byte, czyli ograniczenie do 255.

byte sekunda;                        // Zmienna sekund - dla ustawień i wyświetlania.

void loop() {
  time_t czasOdczytany = now();     // Zapisz bieżący czas do zmiennej czasOdczytany.
  sekunda = second(czasOdczytany);  // Nadajemy zmiennej sekunda wartość sekund.
  lcd.home();                       // Ustaw kursor na początku.
  lcd.print(hour(czasOdczytany));   // Rysuj liczbę godzin.
  lcd.print(F(":"));                // Rysuj dwukropek.
  lcd.print(minute(czasOdczytany)); // Rysuj liczbę minut.
  lcd.print(F(":"));                // Rysuj dwukropek.
  if (sekunda < 10) {               // Jeśli liczba sekund jest niższa od 10 to...
    lcd.print(F("0"));              // Rysuj dodatkowe zero.
  }
  lcd.print(sekunda); // Rysuj liczbę sekund.
}

Tutaj: sekunda = second(czasOdczytany) nadajemy naszej zmiennej wartość zgodnie z instrukcją biblioteki TimeLib. W tym momencie w zmiennej sekunda będzie się znajdować liczba sekund. No i w końcu wprowadzamy instrukcję warunkową if. Jeżeli liczba sekund jest mniejsza od 10, należy na wyświetlacz wysłać dodatkowe zero. Wysyłamy nasz program na Arduino i – wspaniale. Teraz już sekundnik nie skacze, zajmując zawsze dwie pozycje.

No to szybciutko dopisujemy analogiczne linie, które będą dotyczyć minut i godzin. Jak widzimy, przed godzinami nie będziemy dopisywać zera, tylko spację. Zegarki elektroniczne bowiem zwykle nie wyświetlają zera przed pojedynczymi cyframi godzin.

byte godzina;                        // Zmienna godzin - dla ustawień i wyświetlania.
byte minuta;                         // Zmienna minut - jak wyżej.
byte sekunda;                        // Zmienna sekund - jak wyżej.

void loop() {
  time_t czasOdczytany = now();    // Zapisz bieżący czas do zmiennej czasOdczytany.
  godzina = hour(czasOdczytany);   // Nadajemy zmiennej godzina wartość godzin.
  minuta = minute(czasOdczytany);  // Jak wyżej, tylko dla minut.
  sekunda = second(czasOdczytany); // Jak wyżej, tylko dla sekund.
  lcd.home();                      // Ustaw kursor na początku.
  if (godzina < 10) {              // Jeśli liczba godzin jest niższa od 10 to...
    lcd.print(F(" "));             // Rysuj dodatkową spację.
  }
  lcd.print(hour(godzina); // Rysuj liczbę godzin.
  lcd.print(F(":"));       // Rysuj dwukropek.
  if (minuta < 10) {       // Jeśli liczba minut jest niższa od 10 to...
    lcd.print(F("0"));     // Rysuj dodatkowe zero.
  }
  lcd.print(minuta);   // Rysuj liczbę minut.
  lcd.print(F(":"));   // Rysuj dwukropek.
  if (sekunda < 10) {  // Jeśli liczba sekund jest niższa od 10 to...
    lcd.print(F("0")); // Rysuj dodatkowe zero.
  }
  lcd.print(sekunda); // Rysuj liczbę sekund.
}

Nasz zegar działa teraz jak należy. Z jednym zastrzeżeniem – pokazuje dane z czapy. Cóż to za zegar, którego nie można ustawić? Ale o tym już w kolejnym artykule.

Powiązane tematy

Płytka edukacyjna TME-EDU-ARD-2Płytka edukacyjna TME-EDU-ARD-2Sprawdź tutaj

Przeczytaj również

Nasi partnerzy

TMETech Master EventTME EducationPoweredby
Copyright © 2025 arduino.pl