Zpět na domů

NTP server na ESP8266: přesný čas bez internetu | Návod

Článek popisuje implementaci autonomního NTP serveru na bázi ESP8266 s mikrosekundovou přesností. Podrobně jsou rozebrány algoritmy kompenzace síťových zpoždění a udržování přesnosti času mezi synchronizacemi. Řešení je aktuální pro podmínky blokování externích NTP serverů.

Autonomní NTP server: jak ESP8266 řeší problém blokování času
Advertisement 728x90

# Autonomní NTP server na ESP8266: jak zajistit přesný čas bez externích závislostí

V podmínkách nestabilního internetového připojení a blokování externích NTP serverů se lokální zdroj přesného času stává klíčově důležitým. Probereme realizaci autonomního NTP serveru na ESP8266 s mikrosekundovou přesností, který pokračuje v práci i při absenci trvalého přístupu k internetu.

Proč standardní řešení přestávají fungovat

Současné infrastruktury se potýkají s problémem nedostupnosti globálních NTP serverů kvůli síťovým omezením a politice „bílých seznamů“. Když jsou externí zdroje času blokovány, synchronizace systémů se naruší, což je kritické pro:

  • Finanční transakce
  • Logování bezpečnosti
  • Distribuované výpočetní systémy
  • IoT zařízení s přísnými časovými požadavky

Řešení spočívá v vytvoření lokálního NTP serveru, který dokáže autonomně udržovat přesný čas mezi synchronizacemi. ESP8266 je pro tuto úlohu ideální: nízká spotřeba energie, vestavěné WiFi a dostatečný výkon pro zpracování časových značek.

Google AdInline article slot

Architektura časového systému

Klíčový problém – překonání omezení standardní knihovny času Arduino, která pracuje s přesností na sekundy. Pro NTP protokol (RFC 5905) je nutná mikrosekundová přesnost. Implementujeme vlastní třídu JbTime, která odstraňuje dva zásadní nedostatky:

  • Absence podpory zlomkových sekund v systémovém čase
  • Nahromadění chyby mezi synchronizacemi

Princip fungování:

  • Při přijetí času od externího serveru se zaznamená mikrosekundová značka procesoru
  • Při každém požadavku se vypočítá delta mezi aktuálním stavem a okamžikem synchronizace
  • Korekce zohledňuje síťovou zpoždění podle vzorce: offset = ((t2 - t1) + (t3 - t4)) / 2
#ifndef jb_time
#define jb_time

#include <stdint.h>

#define JB_TIME_MAX_AGE  600

class JbTime {
private:
  uint64_t _last;
  uint64_t _sec;
  uint32_t _usec;
  uint32_t _mark;

public:
  bool ok;
  bool fresh;

  JbTime(){
    _sec = 0;
    _usec = 0;
    _mark = 0;
    _last = 0;
    ok = false;
    fresh = false;
  }

  inline bool old() {
    if(_sec == 0) return true;
    if((_sec - _last) > JB_TIME_MAX_AGE) return true;
    return false;
  }

  inline void settime(uint64_t sec, uint32_t usec, uint32_t mark = micros()){
    _mark = mark;
    _sec = sec;
    _sec += usec / 1000000;
    _usec = usec % 1000000;
    ok = true;
    _last = sec;
  }

  inline void gettime(uint64_t *o_sec, uint32_t *o_usec){
    if(ok){
      uint32_t now = micros();
      uint32_t delta = now - _mark;
      _mark = now;

      uint64_t total = (uint64_t)_usec + delta;
      _sec += total / 1000000;
      _usec = total % 1000000;

      *o_sec = _sec;
      *o_usec = _usec;
    } else {
      *o_sec = 0;
      *o_usec = 0;
    }
  }
};
#endif

Zpracování NTP požadavků

NTP protokol používá UDP port 123 a přenáší čas ve formátu 64bitové hodnoty (32 bitů pro sekundy, 32 pro zlomky). Kritická složitost – kompenzace síťového zpoždění. Algoritmus zpracování:

Google AdInline article slot
  • Klient odešle požadavek se značkou T1 (lokální čas)
  • Server přijme požadavek v T2 a odešle odpověď v T3
  • Klient přijme odpověď v T4
  • Vypočítá se offset: offset = ((T2 - T1) + (T3 - T4)) / 2

Třída JbNTPClient implementuje tento mechanismus s ohledem na specifika ESP8266:

  • Zpracování paketů ve formátu popsaném v RFC 5905
  • Automatická korekce časové epochy (NTP používá epochu 1900, Unix 1970)
  • Filtrování nekorektních odpovědí
bool JbNTPClient::requestTime(const char* server, JbTime * mytime) {
  // ... inicializace
  
  // Výpočet T1
  uint64_t t1_sec = 0;
  uint32_t t1_usec = 0;
  systime->gettime(&t1_sec, &t1_usec);
  t1_sec += NTP_UNIX_EPOCH_DIFF;
  
  // Odeslání požadavku
  udp.beginPacket(timeServerIP, ntpPort);
  udp.write((uint8_t*)&packet, sizeof(NTPPacket));
  udp.endPacket();

  // Čekání na odpověď s timeoutem 2000 ms
  while (udp.parsePacket() == 0) {
    if (millis() > timeout) return false;
    delay(10);
  }

  // Výpočet T4 a síťového zpoždění
  double t4 = ntpToDouble(t4_sec, t4_frac);
  _networkDelay = (t4 - t1) - (t3 - t2);
  double offset = ((t2 - t1) + (t3 - t4)) / 2;

  // Nastavení skorigovaného času
  mytime->settime(sec, usec, t4_mark);
  return true;
}

Serverová část: distribuce času v lokální síti

Po synchronizaci s externími servery zařízení začne fungovat jako plnohodnotný NTP zdroj. Zpracování klientových požadavků je realizováno přes metodu serve():

  • Analýza příchozího UDP paketu
  • Kontrola módu (očekává se mode=3 – klientový požadavek)
  • Vytvoření odpovědi s aktuálním časem z JbTime
  • Zohlednění času zpracování požadavku pro zvýšení přesnosti

Peculiarita implementace – použití časové značky v okamžiku odeslání odpovědi (T3), což minimalizuje chybu. Na rozdíl od standardních řešení se zde čas bere přímo z vysoce přesného čítače, ne ze systémových funkcí s přesností na sekundy.

Google AdInline article slot

Co je důležité

  • Autonomnost: Server udržuje přesnost do 20-30 mikrosekund mezi synchronizacemi, i při absenci internetu po hodiny
  • Úspora zdrojů: ESP8266 spotřebovává méně než 70 mA v aktivním režimu, což umožňuje napájení z akumulátoru
  • Kompatibilita: Plná shoda s RFC 5905 zajišťuje fungování s jakýmkoli NTP klientem bez úprav
  • Flexibilita: Možnost nastavení maximálního věku dat (JB_TIME_MAX_AGE) podle specifických požadavků
  • Odolnost vůči selhání: Automatické přepnutí na záložní servery při nedostupnosti primárního

— Editorial Team

Advertisement 728x90

Číst dál