Rpi NTP server and PPM test

  • So pi has 1PPM error rate on it’s clock. it should dirft ~1second per day
systemctl status systemd-timesyncd

root@lp-arm-5:~# timedatectl timesync-status
       Server: 192.168.10.196 (192.168.10.196)
Poll interval: 34min 8s (min: 32s; max 34min 8s)
         Leap: normal
      Version: 4
      Stratum: 1
    Reference: GPS
    Precision: 1us (-20)
Root distance: 0 (max: 5s)
       Offset: -79.759ms
        Delay: 177.920ms
       Jitter: 392.723ms
 Packet count: 437

root@lp-arm-5:~# timedatectl show-timesync
SystemNTPServers=192.168.10.196
FallbackNTPServers=0.debian.pool.ntp.org 1.debian.pool.ntp.org
ServerName=192.168.10.196
ServerAddress=192.168.10.196
RootDistanceMaxUSec=5s
PollIntervalMinUSec=32s
PollIntervalMaxUSec=34min 8s
PollIntervalUSec=34min 8s
NTPMessage={ Leap=0, Version=4, Mode=4, Stratum=1, Precision=-20, RootDelay=0, RootDispersion=0, Reference=GPS, OriginateTimestamp=Sat 2026-08-22 13:55:50 IST, ReceiveTimestamp=Sat 2026-08-22 13:55:50 IST, TransmitTimestamp=Sat 2026-08-22 13:55:50 IST, DestinationTimestamp=Sat 2026-08-22 13:55:50 IST, Ignored=yes, PacketCount=437, Jitter=392.723ms }
Frequency=278345

root@lp-arm-5:~# cat  /etc/systemd/timesyncd.conf
[Time]
NTP=192.168.10.196
FallbackNTP=0.debian.pool.ntp.org 1.debian.pool.ntp.org
[Time]
#NTP=
#FallbackNTP=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org
#RootDistanceMaxSec=5
#PollIntervalMinSec=32
#PollIntervalMaxSec=2048
#ConnectionRetrySec=30
#SaveIntervalSec=60

  • stoped ntp service
root@lp-arm-5:~# timedatectl  && ntpdate -q 192.168.10.90
               Local time: Sat 2026-08-22 14:55:11 IST
           Universal time: Sat 2026-08-22 09:25:11 UTC
                 RTC time: Sat 2026-08-22 09:25:11
                Time zone: Asia/Kolkata (IST, +0530)
System clock synchronized: yes
              NTP service: inactive
          RTC in local TZ: no
2026-08-22 14:55:11.120999 (+0530) +0.017578 +/- 0.004635 192.168.10.90 s1 no-leap



root@lp-arm-5:~# while true; do     ntpdate -q 192.168.10.90;     sleep 1; done
2026-08-22 14:56:07.223000 (+0530) +0.014346 +/- 0.009613 192.168.10.90 s1 no-leap
2026-08-22 14:56:08.310000 (+0530) +0.016642 +/- 0.008483 192.168.10.90 s1 no-leap
2026-08-22 14:56:09.393000 (+0530) +0.019201 +/- 0.004101 192.168.10.90 s1 no-leap
2026-08-22 14:56:10.473000 (+0530) +0.016223 +/- 0.012315 192.168.10.90 s1 no-leap
2026-08-22 14:56:11.563000 (+0530) +0.022523 +/- 0.005409 192.168.10.90 s1 no-leap
2026-08-22 14:56:12.657000 (+0530) +0.020756 +/- 0.004506 192.168.10.90 s1 no-leap
2026-08-22 14:56:13.748000 (+0530) +0.027691 +/- 0.013002 192.168.10.90 s1 no-leap
2026-08-22 14:56:14.824999 (+0530) +0.020858 +/- 0.004934 192.168.10.90 s1 no-leap
2026-08-22 14:56:15.902000 (+0530) +0.020752 +/- 0.006207 192.168.10.90 s1 no-leap
2026-08-22 14:56:16.980000 (+0530) +0.022216 +/- 0.004206 192.168.10.90 s1 no-leap
2026-08-22 14:56:18.081999 (+0530) +0.021030 +/- 0.007626 192.168.10.90 s1 no-leap
2026-08-22 14:56:19.162000 (+0530) +0.022143 +/- 0.005573 192.168.10.90 s1 no-leap
2026-08-22 14:56:20.237000 (+0530) +0.020466 +/- 0.005176 192.168.10.90 s1 no-leap
2026-08-22 14:56:21.313000 (+0530) +0.018130 +/- 0.005373 192.168.10.90 s1 no-leap
2026-08-22 14:56:22.394000 (+0530) +0.019911 +/- 0.005312 192.168.10.90 s1 no-leap
2026-08-22 14:56:23.501999 (+0530) +0.022654 +/- 0.006490 192.168.10.90 s1 no-leap
2026-08-22 14:56:24.587999 (+0530) +0.023260 +/- 0.011551 192.168.10.90 s1 no-leap

Local DNS server and Upstream forward

  • Get the DNS server names and config
@home:~$ resolvectl status
Global
         Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
  resolv.conf mode: stub

Link 2 (enp3s0)
    Current Scopes: none
         Protocols: -DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported

Link 3 (wlo1)
    Current Scopes: DNS
         Protocols: +DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 1.1.1.1
       DNS Servers: 192.168.1.1 1.1.1.1

@home:~$ resolvectl dns
Global:
Link 2 (enp3s0):
Link 3 (wlo1): 192.168.1.1


@home:~$ cat /etc/resolv.conf 
nameserver 127.0.0.53
options edns0 trust-ad
search .
  • to use your local DNS make only 1 DNS server in config and the other DNS server will upstream server of local dns so that they don’t interfere with each other

esp32 s3 issues with USB serial print – CDC/ JTAG

  • USB port can act as USB serial print port
[env:esp32-s3-devkitc-1]
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino
build_flags =
	-DARDUINO_USB_MODE=1
	-DARDUINO_USB_CDC_ON_BOOT=1
upload_port = /dev/ttyACM0
monitor_port = /dev/ttyACM0
monitor_speed = 115200
monitor_dtr = 0
monitor_rts = 0
  • ESP32-S3 USB CDC not being enabled at boot
  • UART with chip CP2102 is not working on my esp32-s3
@home:~$ ls  /dev/ttyUSB*
/dev/ttyUSB0

@home:~$ esptool --chip esp32s3 --port /dev/ttyUSB0 chip_id
Warning: Deprecated: Command 'chip_id' is deprecated. Use 'chip-id' instead.
esptool v5.3.0
Serial port /dev/ttyUSB0:
Connecting......................................

A fatal error occurred: Failed to connect to ESP32-S3: No serial data received.
For troubleshooting steps visit: https://docs.espressif.com/projects/esptool/en/latest/troubleshooting.html

@home:~$ esptool --chip esp32s3 --port /dev/ttyUSB0 chip_id
Warning: Deprecated: Command 'chip_id' is deprecated. Use 'chip-id' instead.
esptool v5.3.0
Connected to ESP32-S3 on /dev/ttyUSB0:
Chip type:          ESP32-S3 (QFN56) (revision v0.2)
Features:           Wi-Fi, BT 5 (LE), Dual Core + LP Core, 240MHz, Embedded PSRAM 8MB (AP_3v3)
Crystal frequency:  40MHz
MAC:                28:84:85:xx:xx:xx

Stub flasher running.

Warning: ESP32-S3 has no chip ID. Reading MAC address instead.
MAC:                28:84:85:xx:xx:xx

Hard resetting via RTS pin...

sdc41 with bmp280 issue – wrong ppm value – zero ppm

  • Today got to know why my sdc41 sensor was showing bad data earlier it used to show 500 in day and 600-800ppm in night when door was closed. #sdc41 #esp32
    • It was paired with bmp280 and it was using pressure to Compensate the co2. but bmp 280 was showing bad value randomly about -149 hpa
    • As it was using BMP280 value it was started showing double and after few days it started showing 0 ppm
    • Now i ran simple code to just get value of sdc41 it was showing 23000 which more than current limit of sdc41 which is 5000
    • Ran frc 420 to self calibrate the sensor with current value to 420 reference
  • Ran auto calibration , everything looks good now.
#include <Arduino.h>
#include <Wire.h>
#include <SparkFun_SCD4x_Arduino_Library.h>

// NodeMCU/ESP8266 I2C pins: D2 = SDA, D1 = SCL
static const uint8_t SDA_PIN = D2;
static const uint8_t SCL_PIN = D1;

static const uint16_t MAX_VALID_CO2_PPM = 5000;
static const uint16_t DEFAULT_FRC_REFERENCE_PPM = 420; // Typical fresh-air reference
static const uint32_t FRC_STABILIZE_MS = 180000;       // >= 3 minutes per datasheet
static const float DEFAULT_AMBIENT_PRESSURE_HPA = 1006.0f;

SCD4x scd4x(SCD4x_SENSOR_SCD41);

bool applyAmbientPressureCompensation(float pressureHpa, bool persist = true) {
  if (pressureHpa < 700.0f || pressureHpa > 1200.0f) {
    Serial.println("ERROR: pressure out of expected range (700..1200 hPa).");
    return false;
  }

  const float pressurePa = pressureHpa * 100.0f;

  scd4x.stopPeriodicMeasurement();
  delay(500);

  if (!scd4x.setAmbientPressure(pressurePa)) {
    Serial.println("ERROR: Failed to set ambient pressure compensation.");
    scd4x.startPeriodicMeasurement();
    return false;
  }

  if (persist && !scd4x.persistSettings()) {
    Serial.println("WARNING: Ambient pressure set but persistSettings failed.");
  }

  if (!scd4x.startPeriodicMeasurement()) {
    Serial.println("ERROR: Failed to restart periodic measurement after pressure update.");
    return false;
  }

  Serial.print("Ambient pressure compensation set to ");
  Serial.print(pressureHpa, 1);
  Serial.println(" hPa");
  return true;
}

bool configureAutoSelfCalibration(bool enabled) {
  scd4x.stopPeriodicMeasurement();
  delay(500);

  if (!scd4x.setAutomaticSelfCalibrationEnabled(enabled)) {
    Serial.println("ERROR: Failed to set ASC state.");
    return false;
  }

  if (!scd4x.persistSettings()) {
    Serial.println("WARNING: ASC updated but persistSettings failed.");
  }

  if (!scd4x.startPeriodicMeasurement()) {
    Serial.println("ERROR: Failed to restart periodic measurement after ASC config.");
    return false;
  }

  Serial.print("ASC is now ");
  Serial.println(enabled ? "ENABLED" : "DISABLED");
  return true;
}

bool runForcedRecalibration(uint16_t referencePpm) {
  if (referencePpm < 350 || referencePpm > 2000) {
    Serial.println("ERROR: Reference ppm out of expected FRC range (350..2000).");
    return false;
  }

  Serial.println();
  Serial.println("Starting Forced Recalibration (FRC)...");
  Serial.println("Keep sensor in stable, known CO2 environment.");
  Serial.print("Reference CO2 (ppm): ");
  Serial.println(referencePpm);

  scd4x.stopPeriodicMeasurement();
  delay(500);
  if (!scd4x.startPeriodicMeasurement()) {
    Serial.println("ERROR: Could not start periodic measurement for FRC preconditioning.");
    return false;
  }

  // Datasheet requirement: run in normal mode for at least 3 minutes before FRC.
  const uint32_t startMs = millis();
  while (millis() - startMs < FRC_STABILIZE_MS) {
    const uint32_t remaining = (FRC_STABILIZE_MS - (millis() - startMs)) / 1000;
    Serial.print("FRC preconditioning... ");
    Serial.print(remaining);
    Serial.println(" s remaining");

    if (scd4x.readMeasurement()) {
      Serial.print("  sample CO2(ppm): ");
      Serial.println(scd4x.getCO2());
    }

    delay(5000);
  }

  scd4x.stopPeriodicMeasurement();
  delay(500);

  float correction = 0.0f;
  if (!scd4x.performForcedRecalibration(referencePpm, &correction)) {
    Serial.println("ERROR: FRC failed.");
    scd4x.startPeriodicMeasurement();
    return false;
  }

  Serial.print("FRC correction applied (ppm): ");
  Serial.println(correction, 1);

  if (!scd4x.persistSettings()) {
    Serial.println("WARNING: FRC succeeded but persistSettings failed.");
  }

  if (!scd4x.reInit()) {
    Serial.println("WARNING: reInit failed after FRC.");
  }

  if (!scd4x.startPeriodicMeasurement()) {
    Serial.println("ERROR: Could not restart periodic measurement after FRC.");
    return false;
  }

  Serial.println("FRC complete. Sensor restarted in periodic mode.");
  return true;
}

void handleSerialCommands() {
  if (!Serial.available()) {
    return;
  }

  String cmd = Serial.readStringUntil('\n');
  cmd.trim();
  cmd.toLowerCase();

  if (cmd == "help") {
    Serial.println("Commands:");
    Serial.println("  help         -> show commands");
    Serial.println("  asc on       -> enable automatic self-calibration");
    Serial.println("  asc off      -> disable automatic self-calibration");
    Serial.println("  frc          -> run forced recalibration with 420 ppm reference");
    Serial.println("  frc <ppm>    -> run forced recalibration with custom reference");
    Serial.println("  pressure     -> set pressure compensation to 1006 hPa");
    return;
  }

  if (cmd == "asc on") {
    configureAutoSelfCalibration(true);
    return;
  }

  if (cmd == "asc off") {
    configureAutoSelfCalibration(false);
    return;
  }

  if (cmd == "frc") {
    runForcedRecalibration(DEFAULT_FRC_REFERENCE_PPM);
    return;
  }

  if (cmd.startsWith("frc ")) {
    int ppm = cmd.substring(4).toInt();
    if (ppm <= 0) {
      Serial.println("ERROR: Invalid ppm. Example: frc 420");
      return;
    }
    runForcedRecalibration((uint16_t)ppm);
    return;
  }

  if (cmd == "pressure") {
    applyAmbientPressureCompensation(DEFAULT_AMBIENT_PRESSURE_HPA, true);
    return;
  }

  Serial.print("Unknown command: ");
  Serial.println(cmd);
  Serial.println("Type 'help' for available commands.");
}

void setup() {
  Serial.begin(115200);
  delay(200);

  Serial.println();
  Serial.println("SCD41 minimal CO2 test starting...");

  Wire.begin(SDA_PIN, SCL_PIN);
  Wire.setClock(100000);

  if (!scd4x.begin(true, true, false)) {
    Serial.println("ERROR: SCD41 init failed. Check wiring, power, and I2C address.");
    while (true) {
      delay(1000);
    }
  }

  // Ensure periodic mode starts from a clean state.
  scd4x.stopPeriodicMeasurement();
  delay(500);

  if (!scd4x.startPeriodicMeasurement()) {
    Serial.println("ERROR: Could not start low-power periodic measurement.");
    while (true) {
      delay(1000);
    }
  }

  // Recommended for SCD41 long-term stability in real deployments.
  configureAutoSelfCalibration(true);

  // Overwrite any previously saved bad pressure setting (e.g. from a faulty upstream sensor)
  // and persist the correct local pressure compensation.
  applyAmbientPressureCompensation(DEFAULT_AMBIENT_PRESSURE_HPA, true);

  Serial.println("SCD41 initialized. First reading can take ~30s.");
  Serial.println("Type 'help' in Serial Monitor for calibration commands.");
}

void loop() {
  handleSerialCommands();

  if (!scd4x.readMeasurement()) {
    Serial.println("Measurement not ready yet...");
    delay(5000);
    return;
  }

  const uint16_t co2 = scd4x.getCO2();
  const float temperature = scd4x.getTemperature();
  const float humidity = scd4x.getHumidity();

  Serial.print("CO2(ppm): ");
  Serial.print(co2);
  Serial.print("  Temp(C): ");
  Serial.print(temperature, 2);
  Serial.print("  RH(%): ");
  Serial.println(humidity, 2);

  if (co2 == 0) {
    Serial.println("WARNING: CO2 is 0 ppm (invalid on SCD41). Sensor may still be stabilizing or faulty.");
  } else if (co2 > MAX_VALID_CO2_PPM) {
    Serial.println("WARNING: CO2 is above SCD41 range (5000 ppm). Reading likely invalid; run 'frc 420' in fresh air.");
  }

  delay(5000);
}
FRC preconditioning... 24 s remaining
  sample CO2(ppm): 23076
FRC preconditioning... 19 s remaining
  sample CO2(ppm): 23076
FRC preconditioning... 14 s remaining
  sample CO2(ppm): 23077
FRC preconditioning... 9 s remaining
  sample CO2(ppm): 23075
FRC preconditioning... 4 s remaining
  sample CO2(ppm): 23078
FRC correction applied (ppm): -22518.0
FRC complete. Sensor restarted in periodic mode.
Measurement not ready yet...
CO2(ppm): 471  Temp(C): 30.83  RH(%): 73.06
CO2(ppm): 472  Temp(C): 30.39  RH(%): 74.56
CO2(ppm): 488  Temp(C): 30.14  RH(%): 75.75
CO2(ppm): 486  Temp(C): 29.92  RH(%): 76.85
CO2(ppm): 469  Temp(C): 29.68  RH(%): 77.88

raspberry pi useful commands

  • See voltage of all pin – can be used in TRNG
root@lp-arm-5:~# vcgencmd pmic_read_adc
 3V7_WL_SW_A current(0)=0.09271335A
   3V3_SYS_A current(1)=0.07026696A
   1V8_SYS_A current(2)=0.12589500A
  DDR_VDD2_A current(3)=0.00292779A
  DDR_VDDQ_A current(4)=0.00000000A
   1V1_SYS_A current(5)=0.20299340A
    0V8_SW_A current(6)=0.35523850A
  VDD_CORE_A current(7)=0.68761000A
   3V3_DAC_A current(17)=0.00000000A
   3V3_ADC_A current(18)=0.00006105A
   0V8_AON_A current(16)=0.00323565A
      HDMI_A current(22)=0.01172160A
 3V7_WL_SW_V volt(8)=3.69977600V
   3V3_SYS_V volt(9)=3.30632200V
   1V8_SYS_V volt(10)=1.79975400V
  DDR_VDD2_V volt(11)=1.10732500V
  DDR_VDDQ_V volt(12)=0.60402870V
   1V1_SYS_V volt(13)=1.10256300V
    0V8_SW_V volt(14)=0.79926660V
  VDD_CORE_V volt(15)=0.72097620V
   3V3_DAC_V volt(20)=3.30768900V
   3V3_ADC_V volt(21)=3.30585700V
   0V8_AON_V volt(19)=0.79736190V
      HDMI_V volt(23)=5.02902000V
     EXT5V_V volt(24)=5.02098000V
      BATT_V volt(25)=0.00000000V

Openwrt openvpn client with local network

  • Zone – Add

Example (/etc/config/firewall):

config zone
    option name 'vpn'
    list network 'vpn'
    option input 'ACCEPT'
    option output 'ACCEPT'
    option forward 'ACCEPT'
    option masq '1'
    option mtu_fix '1'

config forwarding
    option src 'lan'
    option dest 'vpn'

And define the network:

config interface 'vpn'
    option proto 'none'
    option device 'tun0'

Then restart:

/etc/init.d/network restart
/etc/init.d/firewall restart

Run this on OpenWrt:

tcpdump -ni tun0 icmp

Then, from your laptop:

ping 192.168.0.1

  • Static route
  • Install openvpn
opkg install openvpn-openssl luci-app-openvpn
opkg install openvpn-easy-rsa
/etc/openvpn/client.ovpn

/etc/init.d/openvpn enable
/etc/init.d/openvpn start

/etc/init.d/openvpn status

logread -f openvpn

Openwrt and local router static route

  • static route on wifi router
  • static route on openwrt
  • network digram
  • Now i can do ssh from pi5 to ubuntu-laptop on 192.168.1.145 even when i disconnected form wifi on ubuntu-laptop.
root@lp-arm-5:~# ssh root@192.168.1.145 
root@192.168.1.145's password: 
Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.17.0-14-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

Expanded Security Maintenance for Applications is not enabled.

376 updates can be applied immediately.
364 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable

89 additional security updates can be applied with ESM Apps.
Learn more about enabling ESM Apps service at https://ubuntu.com/esm

Last login: Sat Jul 25 18:59:08 2026 from 192.168.10.118

Getting GPS location/Time using NEO-7M-0-000 Blox with ESP32 – NTP server

#include <WiFi.h>

const char *ssid = "wifi";
const char *password = "pass";

// GPS UART
HardwareSerial GPS(2); // UART2

// Standard NMEA TCP port
WiFiServer server(10110);

// Allow multiple clients
WiFiClient clients[5];

String line;

void setup() {
  Serial.begin(115200);

  // GPS on GPIO21 (RX), GPIO22 (TX)
  GPS.begin(9600, SERIAL_8N1, 21, 22);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  Serial.print("Connecting");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.print("Connected: ");
  Serial.println(WiFi.localIP());

  server.begin();
  server.setNoDelay(true);

  Serial.println("NMEA TCP server started on port 10110");
}

void loop() {

  // Accept new clients
  if (server.hasClient()) {

    for (int i = 0; i < 5; i++) {

      if (!clients[i] || !clients[i].connected()) {

        if (clients[i])
          clients[i].stop();

        clients[i] = server.available();
        clients[i].setNoDelay(true);

        Serial.printf("Client %d connected\n", i);
        break;
      }
    }

    // No room
    WiFiClient reject = server.available();
    reject.stop();
  }

  // Read GPS
  while (GPS.available()) {

    char c = GPS.read();

    // Echo to USB serial
    Serial.write(c);

    line += c;

    if (c == '\n') {

      // Send complete NMEA sentence to all clients
      for (int i = 0; i < 5; i++) {

        if (clients[i] && clients[i].connected()) {

          clients[i].print(line);

          if (clients[i].getWriteError()) {
            clients[i].stop();
          }
        }
      }

      line.clear();

      // Prevent runaway if malformed data
      if (line.length() > 256)
        line.clear();
    }
  }
}
  • xgps to view data
apt install gpsd gpsd-clients
nc ESP-IP 10110
#OR
gpsd tcp://192.168.1.150:10110

#lauch
xgps

#or
sudo systemctl stop gpsd.socket gpsd
gpsd -N -n -D 5 tcp://192.168.10.196:10110
gpspipe -r
#lauch
xgps

#check time of laptop
home@home:~$ ntpdate -q 192.168.10.196
2026-07-28 17:58:10.0 (+0530) -1.950956 +/- 0.001631 192.168.10.196 s1 no-leap

Your computer's clock is about -1.950956 seconds ahead of the GPS-based NTP server. To match the GPS time, your clock would need to move backward by about -1.950956 seconds.

systemctl enable --now chrony

@home: timedatectl 
               Local time: Tue 2026-07-28 18:09:42 IST
           Universal time: Tue 2026-07-28 12:39:42 UTC
                 RTC time: Tue 2026-07-28 12:39:42
                Time zone: Asia/Kolkata (IST, +0530)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

@home:~$ chronyc tracking
Reference ID    : 41007738 (ec2-65-0-119-56.ap-south-1.compute.amazonaws.com)
Stratum         : 5
Ref time (UTC)  : Tue Jul 28 12:41:44 2026
System time     : 0.000236698 seconds fast of NTP time
Last offset     : +0.000414969 seconds
RMS offset      : 0.000378138 seconds
Frequency       : 14.685 ppm fast
Residual freq   : +1.339 ppm
Skew            : 21.187 ppm
Root delay      : 0.010673190 seconds
Root dispersion : 0.000975857 seconds
Update interval : 64.2 seconds
Leap status     : Normal


chronyc sources -v
  • GPS time after connecting PPS pin to D7
@home:~$ chronyc sources -v

MS Name/IP address         Stratum Poll Reach LastRx Last sample               
===============================================================================
^* 192.168.10.196                1   6   377    40   -489ms[ -610ms] +/- 2671us
^- alphyn.canonical.com          2  10   367   366   -462ms[  +25ms] +/-  170ms
^- prod-ntp-4.ntp1.ps5.cano>     2  10   377    23   -486ms[ -486ms] +/-   76ms
^- prod-ntp-3.ntp4.ps5.cano>     2  10   377    54   -458ms[ -579ms] +/-   99ms
^- prod-ntp-5.ntp1.ps5.cano>     2  10   377   497   -486ms[+3998us] +/-   73ms
^- 172-233-155-39.ip.linode>     5   9   377    47   -484ms[ -605ms] +/-  154ms
^- 149.206.212.35.bc.google>     2  10   377   434   -472ms[  +18ms] +/-  167ms
^- time.cloudflare.com           3   6   377    44   -484ms[ -605ms] +/-   72ms
^- ntp2.ggsrv.de                 2   8   277   141   -442ms[ -654ms] +/-  123ms

#offset fix of 0.1 sec
home@home:~$ while true; do     ntpdate -q 192.168.10.196;     sleep 1; done
2026-07-29 12:49:02.649000 (+0530) -0.000654 +/- 0.002628 192.168.10.196 s1 no-leap
2026-07-29 12:49:03.711000 (+0530) -0.000765 +/- 0.002465 192.168.10.196 s1 no-leap
2026-07-29 12:49:04.767000 (+0530) -0.000835 +/- 0.002581 192.168.10.196 s1 no-leap
2026-07-29 12:49:05.830000 (+0530) -0.002471 +/- 0.003938 192.168.10.196 s1 no-leap
2026-07-29 12:49:06.895000 (+0530) -0.001071 +/- 0.002849 192.168.10.196 s1 no-leap
2026-07-29 12:49:07.969999 (+0530) -0.001286 +/- 0.002694 192.168.10.196 s1 no-leap
2026-07-29 12:49:09.57000 (+0530) +0.002670 +/- 0.006418 192.168.10.196 s1 no-leap
2026-07-29 12:49:10.113999 (+0530) -0.001435 +/- 0.001903 192.168.10.196 s1 no-leap
2026-07-29 12:49:11.193999 (+0530) -0.000183 +/- 0.003198 192.168.10.196 s1 no-leap
2026-07-29 12:49:12.251999 (+0530) -0.000521 +/- 0.002711 192.168.10.196 s1 no-leap
2026-07-29 12:49:13.350000 (+0530) -0.001052 +/- 0.002741 192.168.10.196 s1 no-leap
2026-07-29 12:49:14.455999 (+0530) -0.000497 +/- 0.002895 192.168.10.196 s1 no-leap
2026-07-29 12:49:15.520000 (+0530) -0.000422 +/- 0.002912 192.168.10.196 s1 no-leap
2026-07-29 12:49:16.622000 (+0530) +0.000399 +/- 0.003694 192.168.10.196 s1 no-leap
2026-07-29 12:49:17.690000 (+0530) +0.001445 +/- 0.004720 192.168.10.196 s1 no-leap
2026-07-29 12:49:18.751000 (+0530) -0.001759 +/- 0.001505 192.168.10.196 s1 no-leap
2026-07-29 12:49:19.813000 (+0530) -0.001110 +/- 0.003159 192.168.10.196 s1 no-leap
2026-07-29 12:49:20.876999 (+0530) -0.000605 +/- 0.002787 192.168.10.196 s1 no-leap


#Before correct PPS

^- 192.168.10.196                1   6   377    52    -15ms[  -15ms] +/-   17ms

^- 192.168.10.196                1   6   377    57    -67ms[  -67ms] +/-   68ms

^x 192.168.10.196                1   6   377    16   +707ms[ +707ms] +/- 3018us

^x 192.168.10.196                1   6   377     8  +1088ms[+1088ms] +/- 2698us


@home:~$ while true; do     ntpdate -q 192.168.10.196;     sleep 1; done
ntpdig: no eligible servers
2026-07-29 12:44:33.0 (+0530) -0.631612 +/- 0.002777 192.168.10.196 s1 no-leap
2026-07-29 12:44:34.0 (+0530) -0.708538 +/- 0.002867 192.168.10.196 s1 no-leap
2026-07-29 12:44:35.0 (+0530) -0.814500 +/- 0.045810 192.168.10.196 s1 no-leap
2026-07-29 12:44:36.0 (+0530) -0.925514 +/- 0.003359 192.168.10.196 s1 no-leap
2026-07-29 12:44:37.0 (+0530) -1.001159 +/- 0.003638 192.168.10.196 s1 no-leap
2026-07-29 12:44:38.0 (+0530) -1.063557 +/- 0.004615 192.168.10.196 s1 no-leap
2026-07-29 12:44:39.0 (+0530) -1.127265 +/- 0.003226 192.168.10.196 s1 no-leap
2026-07-29 12:44:41.0 (+0530) -0.272564 +/- 0.048974 192.168.10.196 s1 no-leap
2026-07-29 12:44:42.0 (+0530) -0.414035 +/- 0.002208 192.168.10.196 s1 no-leap
2026-07-29 12:44:43.0 (+0530) -0.521767 +/- 0.051125 192.168.10.196 s1 no-leap
  • What is PPM? Parts Per Million
|         Accuracy |      Drift per day |
| ---------------: | -----------------: |
|           50 ppm |       ~4.3 seconds |
|           20 ppm |       ~1.7 seconds |
|           10 ppm |      ~0.86 seconds |
|            1 ppm |   ~86 milliseconds |
|          0.5 ppm |   ~43 milliseconds |
|         0.01 ppm | ~0.86 milliseconds |
| 10⁻¹¹ (Rubidium) | ~0.86 microseconds |

So Raspberry pi has 54MHZ oscillator that can drif by 20-30ppm means 1-3 seconds per day.
  • add to /etc/chrony/chrony.conf
cat /etc/chrony/chrony.conf
server 192.168.10.196 iburst


systemctl restart chronyd
  • From Blox MAX-M10S GNSS
@home:~$ while true; do     ntpdate -q 192.168.10.90;     sleep 1; done
2026-08-22 13:50:37.818999 (+0530) -0.002587 +/- 0.006596 192.168.10.90 s1 no-leap
2026-08-22 13:50:38.942999 (+0530) -0.000916 +/- 0.004280 192.168.10.90 s1 no-leap
2026-08-22 13:50:40.10000 (+0530) -0.001542 +/- 0.005918 192.168.10.90 s1 no-leap
2026-08-22 13:50:41.117000 (+0530) -0.000863 +/- 0.004926 192.168.10.90 s1 no-leap
2026-08-22 13:50:42.184000 (+0530) +0.000060 +/- 0.003520 192.168.10.90 s1 no-leap
2026-08-22 13:50:43.288000 (+0530) -0.003916 +/- 0.007181 192.168.10.90 s1 no-leap
2026-08-22 13:50:44.396999 (+0530) -0.008009 +/- 0.011557 192.168.10.90 s1 no-leap
2026-08-22 13:50:45.473000 (+0530) +0.000258 +/- 0.003518 192.168.10.90 s1 no-leap
2026-08-22 13:50:46.559000 (+0530) +0.010864 +/- 0.014502 192.168.10.90 s1 no-leap
2026-08-22 13:50:47.663000 (+0530) -0.058083 +/- 0.061995 192.168.10.90 s1 no-leap
2026-08-22 13:50:48.852000 (+0530) -0.001831 +/- 0.010806 192.168.10.90 s1 no-leap
2026-08-22 13:50:49.929000 (+0530) -0.003046 +/- 0.008400 192.168.10.90 s1 no-leap
2026-08-22 13:50:51.4000 (+0530) -0.000831 +/- 0.007201 192.168.10.90 s1 no-leap
2026-08-22 13:50:52.71000 (+0530) -0.000914 +/- 0.004927 192.168.10.90 s1 no-leap
2026-08-22 13:50:53.153999 (+0530) -0.003625 +/- 0.007565 192.168.10.90 s1 no-leap
2026-08-22 13:50:54.234000 (+0530) -0.000141 +/- 0.006826 192.168.10.90 s1 no-leap
2026-08-22 13:50:55.302000 (+0530) +0.000081 +/- 0.003850 192.168.10.90 s1 no-leap
2026-08-22 13:50:56.368000 (+0530) +0.000933 +/- 0.004076 192.168.10.90 s1 no-leap
2026-08-22 13:50:57.439000 (+0530) -0.002185 +/- 0.008926 192.168.10.90 s1 no-leap
2026-08-22 13:50:58.517000 (+0530) -0.003229 +/- 0.012751 192.168.10.90 s1 no-leap
2026-08-22 13:50:59.598000 (+0530) +0.001274 +/- 0.005002 192.168.10.90 s1 no-leap
2026-08-22 13:51:00.672999 (+0530) -0.015312 +/- 0.025618 192.168.10.90 s1 no-leap