[115] Czas uniksowy
![[115] Czas uniksowy](/_next/image?url=https%3A%2F%2Farduino.pl%2Fimgproxy%2FPC13EfZOMne28aNTxuAZRUrARA5lWdrYjCUpiPxX0h4%2Ff%3Awebp%2Fw%3A1200%2FbG9jYWw6Ly8vaW1hZ2VzLzAtNjg1L2FmNWNmLzgxOWMyLzAtNjg1YWY1Y2Y4MTljMjEwNzkwNjI2MS5wbmc%3D.webp&w=3840&q=75)
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ć.