Znaczniki Unix w systemach wbudowanych, API i bazach danych
Projektuj znaczniki czasu czujników z RTC, NTP, zegarami monotonicznymi, JSON i SQL, z przykładem synchronizacji ESP32.
Użyteczny pomiar łączy wartość, odniesienie czasowe i niepewność. Unix ułatwia wymianę bez gwarancji dokładności lub kolejności. Sprawdź zapis w Konwerter Unix Timestamp / Epoch i podstawy w Znaczniki czasu Unix i czas Epoch.
RTC i zegar systemowy
Sprzętowy RTC zachowuje kalendarz lub licznik podczas uśpienia CPU, często dzięki zasilaniu zapasowemu. Nie musi być ustawiony na UTC ani zsynchronizowany. Zegar systemowy wiąże sprzętową podstawę czasu z epoką. Po zaniku zasilania sprawdź RTC: data domyślna nie jest pomiarem.
STM32, ESP32 i wbudowany Linux
W STM32 rejestry podsekundowe, domena zapasowa i funkcje RTC zależą od rodziny; sprawdź instrukcję układu i ST AN4759. Nie istnieje uniwersalny rejestr Unix. ESP-IDF łączy timery RTC i wysokiej rozdzielczości zgodnie z konfiguracją. Linux udostępnia CLOCK_REALTIME oraz zegary monotoniczne. Sprawdź libc i ABI niezależnie od szerokości CPU.
Synchronizacja NTP i SNTP
NTP szacuje przesunięcie zegara i opóźnienie sieci; SNTP upraszcza klienta. Asymetria pakietów nadal powoduje błąd. Epoka i ery NTP nie są sekundami Unix: konwersję pozostaw stosowi. Zachowuj stan, wiek i niepewność synchronizacji. Skok zmienia czas nagle; łagodna korekta zmienia tymczasowo tempo zegara.
Zegar cywilny a monotoniczny
Zegar cywilny podaje datę UTC i może skakać podczas korekty. Monotoniczny mierzy czas trwania od dowolnego początku, a nie datę UTC; może zerować się przy starcie, a zachowanie w uśpieniu zależy od zegara. Używaj go do limitów czasu i ponowień, dodając odniesienie UTC do porównywania urządzeń.
Znacznik chwili pomiaru czujnika
Pobierz czas blisko próbki ADC lub przerwania definiującego pomiar. Wysyłanie obejmuje kolejki, radio i ponowienia; czas odbioru przez serwer przechowuj oddzielnie. Dla bloków zapisuj początek, okres i sekwencję, uwzględniając dryft. Uzupełnieniem jest poradnik próbkowania.
Rejestrowanie zdarzeń i telemetria
Dodaj identyfikator urządzenia, uruchomienia i numer sekwencji. Korekta wstecz może powtarzać UTC bez powtarzania zdarzenia. Rozróżniaj czas zmierzony i odtworzony. Sortowanie wyłącznie według UTC nie gwarantuje przyczynowości między urządzeniami o nakładających się przedziałach niepewności.
Jednostki i budżet błędów
| Jednostka znacznika | 1 s | 1700000000 s |
|---|---|---|
| Sekundy (s) | 1 | 1700000000 |
| Milisekundy (ms) | 1000 | 1700000000000 |
| Mikrosekundy (µs) | 1000000 | 1700000000000000 |
| Nanosekundy (ns) | 1000000000 | 1700000000000000000 |
Sekundy nadają się do zgrubnych harmonogramów, milisekundy do wielu zastosowań telemetrii, mikrosekundy do odpowiedniej akwizycji, a nanosekundy do obsługującego je sprzętu. Rozdzielczość, powtarzalność i dokładność są różne. Swobodny błąd 20 ppm daje około 1,728 s w nominalnych 24 godzinach; temperatura i starzenie mogą go zmieniać.
API REST i JSON
{
"timestamp_ns": "1700000000123456789",
"captured_at": "2023-11-14T22:13:20.123456789Z",
"time_quality": "synced",
"uncertainty_ms": 20,
"boot_id": "device-session-42",
"sequence": 17
}
Dokumentuj epokę, jednostkę, znak i zakres. Wysyłaj duże wartości jako ciągi dziesiętne, jeśli klient używa binary64. JSON.stringify nie przyjmuje bezpośrednio BigInt. timestamp_ns określa jednostkę. Nieznany czas to null z informacją o jakości, zamiast pozornej daty około 1970 roku.
ISO 8601 i RFC 3339
Stosuj Z lub przesunięcie liczbowe i ustaloną precyzję ułamka. 2023-11-14T22:13:20Z i 2023-11-14T23:13:20+01:00 oznaczają to samo. Data bez sufiksu nie określa UTC. Przesunięcie nie zachowuje przyszłych zasad Europe/Paris.
Bazy SQL
CREATE TABLE sensor_measurement (
device_id text NOT NULL,
boot_id text NOT NULL,
sequence bigint NOT NULL,
captured_at timestamptz,
monotonic_us bigint NOT NULL,
time_quality text NOT NULL,
temperature_c double precision NOT NULL,
PRIMARY KEY (device_id, boot_id, sequence)
);
INSERT INTO sensor_measurement VALUES ('sensor-1', 'boot-42', 17,
TIMESTAMPTZ '2023-11-14T22:13:20.123456Z', 9812370, 'synced', 23.625);
Przykład dotyczy PostgreSQL. timestamptz zapisuje chwilę i wyświetla ją w strefie sesji, bez zachowania pierwotnego identyfikatora IANA. W razie potrzeby przechowuj go osobno. Dokładność zapisu wynosi mikrosekundy; dokładne nanosekundy wymagają osobnego pola całkowitego lub dziesiętnego. Inne silniki SQL mają własne zasady. Indeks czasu i tożsamość urządzenie/uruchomienie/sekwencja spełniają inne role.
UTC i lokalne harmonogramy
Zdarzenia zapisuj w UTC. Codzienna reguła o 08:00 w Paryżu wymaga czasu lokalnego i strefy IANA, ponieważ UTC zmienia się wraz z czasem letnim. Określ zachowanie dla pominiętej lub powtórzonej godziny. Dodanie 86400 sekund Unix nie gwarantuje tej samej lokalnej godziny następnego dnia.
Ograniczenia time_t
Nie wyprowadzaj time_t z sizeof(void*). Pole sekund ze znakiem na 32 bitach osiąga 2038 nawet w częściowo 64-bitowym systemie. ESP-IDF używa 64-bitowego time_t ze znakiem od v5.0; stare oprogramowanie i protokoły mogą pozostać wąskie. Sprawdź printf, kolumny SQL, programy rozruchowe, sterowniki RTC i migracje OTA.
ESP32: przykład czujnika z synchronizacją NTP
Kod ESP-IDF 5.x/6.x wywołuje się raz po inicjalizacji esp-netif i uzyskaniu połączenia Wi-Fi lub Ethernet. Sterownik płytki przekazuje skończoną temperaturę do log_sensor. Pierwsze oczekiwanie trwa najwyżej 15 sekund; każdy pomiar tworzy rekord JSON.
#include <stdbool.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>
#include <sys/time.h>
#include "freertos/FreeRTOS.h"
#include "esp_netif_sntp.h"
#include "esp_timer.h"
#include "esp_random.h"
static bool clock_synced;
static int64_t last_sync_us;
static uint32_t boot_id;
static uint64_t sequence;
void ntp_after_network_ready(void) {
boot_id = esp_random();
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("pool.ntp.org");
esp_netif_sntp_init(&config);
clock_synced = esp_netif_sntp_sync_wait(pdMS_TO_TICKS(15000)) == ESP_OK;
if (clock_synced) last_sync_us = esp_timer_get_time();
}
void log_sensor(double temperature_c) {
int64_t monotonic_us = esp_timer_get_time();
struct timeval tv;
bool valid = clock_synced && monotonic_us - last_sync_us < INT64_C(3600000000)
&& gettimeofday(&tv, NULL) == 0;
printf("{\"boot_id\":\"%08" PRIx32 "\",\"sequence\":%" PRIu64
",\"monotonic_us\":\"%" PRId64 "\",\"timestamp_us\":",
boot_id, sequence++, monotonic_us);
if (valid) {
int64_t utc_us = (int64_t)tv.tv_sec * INT64_C(1000000) + tv.tv_usec;
printf("\"%" PRId64 "\"", utc_us);
} else printf("null");
printf(",\"time_quality\":\"%s\",\"temperature_c\":%.3f}\n",
valid ? "synced" : "unsynced", temperature_c);
}
timestamp_us i monotonic_us są ciągami dziesiętnymi. Bez synchronizacji UTC jest null, lecz kolejność zostaje zachowana. Godzinne okno ważności to przykładowa polityka, a nie gwarancja dokładności. Przykład nie odnawia zaufania po późniejszych automatycznych synchronizacjach: produkcyjny kod powinien aktualizować last_sync_us z powiadomienia, chronić stan współdzielony i obsługiwać ponowne połączenia oraz niepewność. Losowy 32-bitowy identyfikator startu jest przykładem; przy krytycznych kolizjach użyj trwałego licznika lub silniejszego identyfikatora.
Restart, dryft i utrata synchronizacji
Restart tworzy nową monotoniczną oś czasu. Zachowaj identyfikator startu i nie łącz bez weryfikacji liczników z różnych uruchomień. Przy utracie sieci zwiększaj niepewność według budżetu oscylatora i wieku ostatniego wiarygodnego odniesienia. RTC z podtrzymaniem pomaga, ale wymaga jawnej jakości. Duże korekty powinny być widoczne bez cichego przepisywania historii.
Dobre praktyki i testy
- Określać epokę, jednostkę i znak na każdej granicy.
- UTC dla zdarzeń, zegar monotoniczny dla limitów czasu.
- Zachowywać sekwencję, identyfikator startu i jakość synchronizacji.
- Testować start bez sieci, wyczerpaną baterię RTC, korekty wstecz, zmiany czasu i granice liczb.
- Aktualizować dane stref dla przyszłych lokalnych harmonogramów.
Pytania techniczne
Czy NTP gwarantuje dokładność mikrosekundową?
Nie. Pozostają asymetria sieci, planowanie i opóźnienie akwizycji. Zmierz cały tor i podaj odpowiednią niepewność.
Czy licznik monotoniczny wystarcza do UTC?
Potrzebna jest relacja do UTC, model dryftu, wiek i niepewność. Nie obowiązuje ona automatycznie po restarcie.
Czy przechowywać wszystko lokalnie?
Chwile zapisuj w UTC, a strefę osobno dla reguł cywilnych lub oryginalnego kontekstu. Konwertuj przy wyświetlaniu.
Narzędzia i źródła pierwotne
Konwerter Unix Timestamp / Epoch · Znaczniki czasu Unix i czas Epoch · Tolerancja oscylatorów.
- IETF · RFC 3339
- IETF · RFC 5905 (NTP)
- IETF · RFC 8259 (JSON)
- Python · PEP 418
- Espressif · ESP-IDF
- STMicroelectronics · AN4759
- PostgreSQL · SQL
Powiązane narzędzia i poradniki
Kalkulator kondensatorów obciążających rezonator kwarcowy · Przeczytaj pełny poradnik projektowania