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

Published by

Leave a Reply

Your email address will not be published. Required fields are marked *