Public server on BPI-R3 router: the success story

I’ll start with the links:

Server information: https://ntp.immortalex.ru (A small info/statistics page is available here)
GitHub Project: GitHub - ntpservernsk/bpir3ntp (Contains all setup information with configuration examples)

In short: I’m thrilled with the result! Yes, the BPI-R3 is a powerful router, but it’s still just a router handling many other tasks. Despite this, it’s running an NTP server with the maximum load settings the pool allows. According to the stats, it’s currently serving about 1.6% of requests in the RU zone – several times higher than the zone average (100/420 ≈ 0.24%). This translates to roughly 3000-4000 requests per second. The load is only around 10%, and that’s on a single core (it has four!). So theoretically, the router could handle significantly more.

Unfortunately, right before committing and writing this post, I accidentally wiped my stats. So the graphs have just started updating. Hopefully, everything will look great in 24 hours, and I’ll post some examples on GitHub.

Any feedback or criticism is welcome!

Ideally, I’d love to add a PPS source and become a Stratum 1 server. Unfortunately, that’s beyond my current skills. I found a few guides (fairly superficial ones) for Raspberry Pi, but they aren’t very applicable to OpenWrt. If someone wrote a detailed, step-by-step guide – even if it required soldering – I’d genuinely appreciate it and would absolutely take on the challenge!

3 Likes

OpenWRT is linux based, right ? If chrony build with PPS driver it’s “should be” easy as on normal Pi.

  • Devcietree Overlay to enable GPIOPPS
  • Set an Upstream NTP Server (IIRC select or preferr is an additonl flag)
  • Add PPS Config entry to chrony (see chrony.conf PPS Section)

No hassle with NMEA or GPSD because using a NTP server for Initial time set.
That’s the theory :see_no_evil_monkey:

While I’m a long-time Linux desktop user, the nuances of single-board computers/embedded systems remain a bit of a “dark forest” for me. I grasp the concept of a devicetree overlay at a high level, but unfortunately, I lack the practical knowledge to implement it without a guide “for dummies.”

I found one thread on the Banana Pi forum about enabling PPS, but the user there was running a full Linux distribution on their router and spent most of the time wrestling with kernel recompilation. This approach isn’t directly applicable to my OpenWrt setup. On the positive side, OpenWrt already has the kmod-pps package available in its repositories, so that part shouldn’t be an issue.

Similarly, I’ve also found instructions for configuring chrony to use PPS+NMEA (to achieve complete independence from other NTP servers). So, my entire plan is currently stalled solely on this very first step: the low-level hardware/PPS signal connection setup.

No worries at all!

Don’t be afraid of “DeviceTree(Overlay)”. I have to take a look how OpenWRT handle this but on RasperryPiOs’es you just have to add a single line into /boot/config.txt
ex: dtoverlay=pps-gpio,gpiopin=18 that’s it.

Ahh good point about kpps. Kernel support must be enabled - i forgot about it. :-/
But it looks like PPS support is already packaged

But did you already checked /dev/ for pps0 or dmesg / messages / syslog ?
Maybe it’s already enabled :slight_smile:

Argghh – just saw that you are taking about an Banana Pi :see_no_evil_monkey: sorry

It usually is not part of the base image, but can be installed easily via the package manager.

Bigger challenge likely is that none of the time daemons come with PPS support enabled, I remember having to build ntpd classic and chronyd myself (enabling support for further refclocks as well, besides getting more recent versions before they were released by OpenWrt, and to remove the very bulky OpenSSL dependency of recent OpenWrt ntpd classic builds).

I moved away from OpenWrt-based time servers around the time OpenWrt switched to device tree. Images became too big for my devices, and the RPi was just so much easier to handle in that respect. So I don’t have too much experience with device trees on OpenWrt. But I know that my one OpenWrt-based RPi seems to provide support for the device tree in a similar fashion as Raspberry Pi OS (and others). I’ve never actually used it, though, since I forgot to build the PPS kernel module at the time matching the kernel build… But I would expect it to work in a similar manner on the Banana Pi as on the RPi, i.e., just add the configuration in config.txt, referring to an appropriate GPIO pin. I.e., could be quick to just try, and maybe be lucky.

If device tree is an issue, perhaps too much of a hurdle on the outset, and you are interested in the path as much as in the end result (meaning this will be a detour), using a USB-to-serial bridge might be an option, at least to get started with the stratum 1 and reference clock topic. I’ve been using FTDI FT232R-based devices for many years now, with good effect. I found the Linux drivers for other vendors’ USB-to-serial chips either don’t support the required DCD signal, even if the chip would support it. Or the signal is not readily accessible on typical available boards.

Using a USB-to-serial bridge also takes care of supplying the GNSS receiver with the typical 3.3V power from a USB port (unless the receiver saps too much). Disadvantage is that it lacks in accuracy (±1 ms due to delay and jitter caused by the USB full-speed polling interval). And some cheap knock-offs aren’t stable with PPS (though work fine for serial data only).

A bit more expensive, and rarer, are bridges based on the FTDI FT232H chip. Those seem to be stable even with PPS (though I have way less of those, so smaller sample size). And the chip supports USB hi-speed with its shorter poll interval, i.e., is more accurate than the FT232R, bringing the accuracy down to ±100 μs. Which may already be sufficient for many use cases. (Last time I looked, I couldn’t find a USB 3-to-serial adapter, which could bring down the latency even further. But I guess with the primary use case being the serial interface functionality, and the serial interface having physical limits regarding achievable data rates, probably not enough demand to warrant the likely higher cost of such units.)

I also found it more comfortable to pick the serial time information off the USB-to-serial bridge, rather than messing with the native serial port of, e.g., the RPi. I.e., even with GPIO PPS, I typically still use a USB-to-serial adapter for powering the receiver, and for getting the serial time information, while the PPS signal goes directly to a GPIO pin.

In any case, good luck, and have fun!

Re-reading your last post, I realize some of what I wrote was old news, e.g., the gpio-pps kernel module being available as package. And the need for a custom build of chronyd to enable PPS support. In that context:

In my understanding, chronyd currently doesn’t support NMEA natively. Rather, some other piece of software is needed, e.g., gpsd, or Miroslav’s ntp-refclock, with the latter wrapping just ntpd’s refclocks into a program without all the NTP protocol and timekeeping stuff, and have either of them feed the parsed information to chronyd, e.g., via its SHM refclock driver, or the SOCK one (gpsd only). gpsd has the advantage in this context that it is already packaged by the OpenWrt project. Not sure whether they carry ntp-refclock as well.

In one approach, I think that is easier than it might seem. Many GNSS modules, e.g., the cheap u-blox ones, or u-blox clones, often come with pin headers already soldered, or you might need to do a bit of soldering there. Similar for the FTDI-based USB-to-serial adapters. Then all you need is some Dupont cables to wire them up. The pins are typically labeled, so that is largely self-explanatory (except that RX from one device gets connected to TX on the other device, and vice versa - and make sure you get 3.3V power from the adapter, not 5V). Only when routing the PPS signal through the adapter to avoid fiddling with the device tree, as mentioned previously, one small challenge is that the DCD signal unfortunately is not on the pin header of the FTDI-based boards, so a bit of soldering is needed. Or you take a bit thicker cable (or a Dupont-tip bent appropriately) and squeeze it into the hole so it makes contact, and secure the cable, e.g., with some glue.

Or you connect the PPS signal directly to your chosen GPIO pin in case the device tree stuff turns out to not be as challenging as it may currently seem.

Concerning the u-blox boards, many are intended for positioning in hobby UAV applications which don’t require the PPS signal, so make sure you pick a board that explicitly breaks out the PPS signal.

Some boards come with an attached ceramic antenna, which may be insufficient when indoor reception isn’t good and you can’t place the receiver outdoors (especially with the older generations 6 or 7, but depending on circumstances also with more recent generations). Thus I prefer boards that have an antenna connector, either by itself, or in addition to an attached ceramic antenna. Then one can attach a separate wired puck antenna (or better if desired) and place it outdoors while the receiver stays inside. Often, the connector on the board is a miniature iPEX one, requiring an adapter to connect to an SMA connector common with puck antennas. But nowadays, many boards also come with an SMA connector already on the board, sometimes even in addition to an iPEX connector. Such a board obviously is most comfortable for attaching an external antenna.

Most u-blox chips also support USB natively in addition to one or more serial other interfaces, and some boards expose that interface through a USB connector. That has the advantage of providing higher-rated power directly to the receiver without going through the USB-to-serial adapter. And one can use that to access the NMEA data, fully obviating the need for the separate adapter. Or you use both to feed data to two devices (the PPS signal can typically also be fed to two devices simultaneously). Or even the same one. E.g., I sometimes connect the PPS signal both to the RPi and a USB-to-serial adapter attached to the RPi as well, just to see the lag between the two, and potentially compensate for it.

All in all, I think things don’t need to be too complicated on the hardware side. When choosing the right components, it can be as easy as plugging in one or more Dupont cables, one USB cable, and connecting one external puck antenna.

Anyway, hope some of the above will be helpful to you. Good luck, and have fun!

Thank you very much for your reply!
As for accuracy, chrony tracking already shows

System time     : 0.000025544 seconds fast of NTP time
Last offset     : +0.000056545 seconds
RMS offset      : 0.000056834 seconds

If I understand correctly, this means that my server has an accuracy of about 0.05ms relative to the stratum 1 servers it is connected to, right? So it makes sense for me to fight for a GPS source only if it gives greater accuracy, and, apparently, the options with a USB connection are definitely not suitable.

Accuracy is but one driver for running one’s own stratum 1 server. Independence from external NTP servers can be another one that, depending on circumstances (e.g., frequent network issues, or unreliable upstream servers), might be even more important than highest accuracy.

The main issue with USB is higher offset. That can mostly be compensated for.

Anyhow, PPS via USB was primarily meant to avoid dealing with the device tree from the outset, you seemed skeptical in that respect. It’s obviously up to you whether you want to go straight for that, or take it slower, albeit a detour. As said, I’d think the device tree stuff should work. It’'s simple enough to configure to just give it a shot. If it works right away, good. If not, still time to reconsider how to approach this.

And USB still seems a valid option for the NMEA part if one is looking for autonomy/resilience. If that is not a goal, using an upstream NTP server instead, as suggested by @apuls, would be even easier.

So it all depends on where you want to go, and what your preferences, interests, and constraints are as to how to get there. Just showing you some options. YMMV.

Thank you for the incredibly detailed explanations and recommendations. The goal of achieving independence from other servers is genuinely compelling to me.
While analyzing graphs generated from chronyc tracking (polled every minute), I’m seeing a concerning pattern:

  • My server often runs with remarkable stability for hours at a stretch
  • Offsets from upstream servers typically stay within 0.1ms (often averaging 0.05ms)
  • RTC drift remains consistently low (0.02–0.04 ppm)

But then - suddenly - spikes occur. Yesterday, for example, I saw offsets surging to1.5ms, which forces my server into frantic compensation mode. After some time, it eventually stabilizes again.

BTW, graphs are here (1 day) and here (3 day)

My primary stratum 1 source is vniiftri.ru (claimed to be synchronized with Russia’s official time standard). In theory, it should be highly stable, yet evidently, it isn’t. I suspect many Russian NTP servers also rely on it, which explains why chrony doesn’t switch sources during these events (since all peers “go crazy” simultaneously). The graphs paint a clear picture of this cascading effect.