[115] Czas uniksowy

[115] Czas uniksowy

Nawiązując do serii artykułów o Arduino i sieci, nie pisałem o tym nic, bo sama funkcja wprost jest mało przydatna. Za to już nie wprost – znacznie bardziej. O czym mowa? Tajemnica siedzi w tej linii:


czas = WiFi.getTime();  // Pobierz czas w formacie uniksowym.

Najpierw jednak cofniemy się w czasie, kiedy to badałem elementy biblioteki korzystając z monitora. Usunę więc wszystko, co już zostało zbadane i użyję jednej funkcji tylko: getTime.

#include "WiFiS3.h"                       // Biblioteka obsługująca WiFi na Arduino R4
char ssid[] = "Smialek";                  // Nazwa sieci WiFi.
char pass[] = "1234";                     // Hasło do sieci WiFi.
int modulWiFi = WL_IDLE_STATUS;           // Zmienna stanu WiFi.
const char* adresWWW = "www.google.com";  // Adres stromy do badania dostępności internetu.
const int opoznienie = 3000;              // Opóźnienie dla ustabilizowania połączeń z WiFi.
unsigned long czas;                       // Czas w formie uniksowym.
byte godziny;                             // Bieżąca wartość godzin.
byte minuty;                              // Bieżąca wartość minut.
byte sekundy;                             // Bieżąca wartość sekund.
const byte czasLetni = 5;                 // Przełącznik czasu letniego.

void setup() {
  Serial.begin(115200);                 // Inicjuj monitor.
  while (!Serial) {}                    // Czekaj dopóki nie będzie połączenia z portem szeregowym.
  if (WiFi.status() == WL_NO_MODULE) {  // Sprawdź połączenie z modułem WiFi na płytce.
    while (true) {}                     // Brak połączenia z modułem WiFi, zakończ pracę.
  }
  while (modulWiFi != WL_CONNECTED) {    // Czekaj, dopóki nie połączysz się z punktem dostępu WiFi.
    modulWiFi = WiFi.begin(ssid, pass);  // Procedura łączenia WiFi z punktem dostępu.
    delay(opoznienie);                   // Opóźnienie niezbędne do ustabilizowania się połączenia.
    pinMode(czasLetni, INPUT_PULLUP);    // Deklaruj przełącznik czasu letniego.
  }
}
void loop() {
  czas = WiFi.getTime();                        // Pobierz czas w formacie uniksowym.
  czas += 3600 * (1 + digitalRead(czasLetni));  // Dodaj współczynnik strefy czasowej i czasu letniego.
  Serial.println(czas);                         // Wyświetl czas w formacie uniksowym.

  sekundy = czas % 60;       // Oblicz bieżącą wartość sekund z reszty dzielenia.
  czas /= 60;                // Podziel czas uniksowy przez 60
  byte minuty = czas % 60;   // Oblicz bieżącą wartość minut z reszty dzielenia.
  czas /= 60;                // Kolejny raz podziel czas uniksowy przez 60
  byte godziny = czas % 24;  // Oblicz bieżącą wartość godzin z reszty dzielenia.

  Serial.print(godziny);  // Wyświetl czas w formacie naturalnym.
  Serial.print(":");
  Serial.print(minuty);
  Serial.print(":");
  Serial.println(sekundy);
  delay(opoznienie);
}

Co ona nam daje? Ach, dziwne to bardzo: nie mniej, ni więcej, a ilość sekund jaka minęła od pierwszego stycznia roku 1970. Co się wtedy stało? Nic, po prostu w pewnym środowisku uniksowym wystartował systemowy zegar, czy raczej powinno się powiedzieć: timer odmierzający sekundy od zera. Tak się złożyło, trochę nieszczęśliwie, że system ten stał się szeroko obowiązujący. Nieszczęśliwie, bo: ma niską rozdzielczość – w nowoczesnej bankowości na przykład sekunda to wieki. Po drugie, nie uwzględnia sekund przestępnych, których od tego czasu narosło już 27. Po trzecie i najgorsze – zegarek, który zawsze miał pokazywać unikalny czas, wyzeruje się w roku 2038, bo na jego potrzeby używa się 31 bitów. Będzie z tym prawdopodobnie dużo kłopotu, więcej niż z rokiem dwutysięcznym.

czas = WiFi.getTime();  // Pobierz czas w formacie uniksowym.
Serial.println(czas);   // Wyświetl czas w formacie uniksowym.

Na początek spróbujmy zobaczyć jak to w ogóle działa. Zadeklarujemy zmienną czas i załadujmy do niej ową tajemniczą wartość, po czym prześlemy ją na monitor. Rzeczywiście jest to już wielka liczba, przekraczająca miliard siedemset milionów i zmienia się co sekundę. Zegarek z tego marny, ale w tej postaci może być to pożyteczne do inicjacji generatorów pseudolosowych.

Spróbujmy teraz uzyskać z tego jakiś bardziej ludzki zegar. Będzie to proste, jeśli ograniczymy się do czasu, bez daty. Tak więc deklarujemy trzy zmienne: sekundy, minuty i godziny.

sekundy = czas % 60;       // Oblicz bieżącą wartość sekund z reszty dzielenia.
czas /= 60;                // Podziel czas uniksowy przez 60
byte minuty = czas % 60;   // Oblicz bieżącą wartość minut z reszty dzielenia.
czas /= 60;                // Kolejny raz podziel czas uniksowy przez 60
byte godziny = czas % 24;  // Oblicz bieżącą wartość godzin z reszty dzielenia.

Serial.print(godziny);  // Wyświetl czas w formacie naturalnym.
Serial.print(":");
Serial.print(minuty);
Serial.print(":");
Serial.println(sekundy);

Za każdym razem kolejna jednostka będzie resztą z dzielenia przez 60 bądź 24. Gdy te reszty popakujemy w zmienne i wyświetlimy, ukaże nam się zegarek ludzki. Z tym, że będzie się spóźniał dwie godziny. Dlaczego?

Cóż, czas Uniksa wyznaczono dla południka zerowego i jest czasem uniwersalnym. W naszej strefie trzeba dodać godzinę. Wystarczy więc dodać do odczytanej wartości 3600, bo licznik bije w sekundach, a tyle ich jest w godzinie.

czas = WiFi.getTime();  // Pobierz czas w formacie uniksowym.
czas += 3600;           // Dodaj współczynnik strefy czasowej.
Serial.println(czas);   // Wyświetl czas w formacie uniksowym.

Kompilujemy i… niestety, mamy lato i zegar nadal się spóźnia, teraz o godzinę. Nie uwzględnia on bowiem zmian czasu. W lecie należy dodać kolejną godzinę. Teoretycznie można to wyliczyć, najpierw obliczając dzień tygodnia, miesiąca i miesiąc. Już na tę pierwszą część algorytm jest zawiły i potrzeba tablic z latami przestępnymi oraz ilością dni w miesiącu. Mając wyciągniętą datę trzeba stworzyć kolejny algorytm, wyszukujący ostatnią niedzielę marca i października, bo wtedy następuje zmiana czasu i we wskazanym dniu, o wskazanej godzinie trzeba zmienić sposób dodawania brakującej godziny.

Używając wyłącznie czasu, na przykład do zbudowania budzika, nie będziemy potrzebować daty, więc nic się nie stanie, jeśli sami zadbamy o wprowadzenie korekty. W tym celu wykorzystałem wolne wejście, na które wstawię przełącznik. W zależności od położenia, albo będziemy mieć czas zimowy, albo letni. Proste i skuteczne, choć nieautomatyczne.

const byte czasLetni = 5;                 // Przełącznik czasu letniego.

czas = WiFi.getTime();                        // Pobierz czas w formacie uniksowym.
czas += 3600 * (1 + digitalRead(czasLetni));  // Dodaj współczynnik strefy czasowej i czasu letniego.
Serial.println(czas);                         // Wyświetl czas w formacie uniksowym.

Teraz już zegarek pracuje jak trzeba. Czy rzeczywiście? I tu napotkałem problem, którego nie rozwiązałem. Ponieważ czas Uniksa nie uwzględnia sekund przestępnych, czyli korekcyjnych, które dodaje się bądź odejmuje (choć do tej pory nie było sytuacji z odejmowaniem), gdy obroty Ziemi zwalniają lub przyspieszają na skutek różnych czynników, takie poprawki należy wprowadzić ręcznie. Do tej pory dołożono 27 sekund i teoretycznie tyle powinienem był dodać w programie. Jednak porównując ten czas zarówno z GPS-em jak i zegarem radiowym, okazało się, że niczego dodawać nie trzeba. Czyżby wartości zakodowane miały już ową korektę wprowadzoną? Nie wiem, ale skoro to działa jak należy, nie pytam dlaczego :) Na koniec dodam, że nie jest to jedyna metoda pozyskiwania czasu z internetu i przy okazji przedstawię inne – prostsze, gdzie nie trzeba niczego przeliczać.

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