[SOLVED] How do I use the DS3231 RTC on NanoPi NEO3 with DietPi 7.0.2?

Hello folks,

I’m converting an NTP server (GPS controled) from a rpi zero with SPI ethernet (10Mb/s) running raspbian lite to a nanopi neo3 with DietPi. I want to use the onboard 1GBps, as that will reduce the local LAN NTP delay from 2msec to 0.5msec from my tests.

The first step of this process is to get the RTC DS3231 (I2C) to work with DietPi.
I was successful in the I2C connection and

sudo i2cdetect -y 0

correctly shows the DS3231 at address 68.

From my searches around the forums I then need to execute the following in order to create the new device:

sudo echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-0/new_device

By the way, even with sudo the command gives permission error, I can only run that as root.
After this the new hardware clock is available as it can be tested by:

sudo hwclock -r -f /dev/rtc1

I also remove the fake-clock by using the dietpi-config → RTC hardware.

Questions:

  1. Is this enough to have the DS3231 become the true and only hardware clock of the system?
  2. After reboot this is all lost. On the pi I would edit the rc.local file but that file is not present in my installation and even if I create it and add the following
sudo echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-0/new_device
hwclock -s

it still wouldn’t work (the new I2C device is not created).
So, what is the proper way to run the two command above at boot time on dietpi?

BTW I’m using DietPi v7.0.2.

Thanks a million!!!

Enzo


[SOLVED]

/lib/udev/hwclock-set became:

#!/bin/sh
# Reset the System Clock to UTC if the hardware clock from which it
# was copied by the kernel was in localtime.

dev=$1

if [ -e /run/systemd/system ] ; then
#    exit 0
echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-0/new_device

fi

#if [ -e /run/udev/hwclock-set ]; then
#    exit 0
#fi

.
.
.
HCTOSYS_DEVICE=rtc1

This will attach /dev/rtc1 to the ds3231.

In order to have the system time set from the DS3231 at boot add the following line to /etc/rc.local

sudo hwclock -s -f /dev/rtc1

If your rc.local is not being executed at boot, enable the service:
Check it with: sudo systemctl status rc.local.service
restart it with sudo systemctl restart rc.local.service

Hi,

not sure if this is fitting but there was a similar issues on GitHub. At least it was as well some challenges on RTC https://github.com/MichaIng/DietPi/issues/3393#issuecomment-798903973

G_CONFIG_INJECT 'dtoverlay=i2c-rtc,ds3231' 'dtoverlay=i2c-rtc,ds3231' /boot/config.txt
reboot

Try it with this first, adding the required device tree overlay (if you didn’t do that yet).

If that along does not make it work, try to load the related kernel module, which seems to not happen automatically (anymore):

modprobe rtc-ds1307

If that does help, we found a bug with newest firmware, I seems. But probably that got fixed already with latest master/development branch, at least there is some changelog related to this overlay: https://github.com/raspberrypi/firmware/commit/8717598c7eb443c39c28a4b3a66b4d369ae0a83f

MichaIng
But the NanoPi is not running with RPi kernel, isn’t it?

MichaIng

I don’t have a /boot/config.txt on the NanoPi Neo3. I do have two other files though:

dietpi.txt and armbianEnv.txt

Should I add ‘dtoverlay=i2c-rtc,ds3231’ into one of these files?

Joulinar
Thanks for you github link. very interesting but seems to be related to an rpi 3B, and my current experience is that the nanopi uses a different setup :frowning:

Cheers,

Enzo

Ah sorry, since on GitHub someone was asking for exactly the same RTC on RPi, I missed to have a closer look: https://github.com/MichaIng/DietPi/issues/3393#issuecomment-798903973

Not sure how it works on NEO3. There are some device tree overlays available as well: /boot/dtb/rockchip/overlay/
Check out the readme, additional I2C interfaces are available.

And check out the kernel modules found in /lib/modules/$(uname -r)/kernel/drivers/rtc/ if you find the matching one. rtc-ds1307 is not present there (on the older Linux 5.7 at least), but probably another one matches. modinfo <module_name> can be used to get more info, and modprobe <module_name> to load it.

Further developement (getting closer but not just yet…)

I added the

echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-0/new_device

into /lib/udev/hwclock-set and now the ds3231 is available once the NanoPi boots.

I also changed HCTOSYS_DEVICE=rtc0 to HCTOSYS_DEVICE=rtc1 in /lib/udev/hwclock-set.

if I do

ls /dev/RTC*

I get: /dev/rtc, /dev/rtc0 and /dev/rtc1. /dev/rtc and /dev/rtc0 are default rtc in the system while /dev/rtc1 is the proper ds3231 RTC.

The problem: At the moment, after reboot, the system time is always taken from /dev/rtc0:

dietpi@DietPiNTP:~$ dmesg | grep rtc
[    3.342126] rk808-rtc rk808-rtc: registered as rtc0
[    3.343968] rk808-rtc rk808-rtc: setting system clock to 2020-07-10T00:44:55 UTC (1594341895)
[   10.403642] rtc-ds1307 0-0068: registered as rtc1
dietpi@DietPiNTP:~$

The question is, how can I make /dev/rtc1 the default RTC? It is registered after the system time is set via /dev/rtc0 so maybe it should be made available earlier? Or how can I issue “sudo hwclock -s -f /dev/rtc1” at boot once /dev/rtc1 has become available?

Cheers,

Enzo

The following will change the /dev/rtc symlink one rtc1 is detected:

echo 'KERNEL=="rtc1", SUBSYSTEM=="rtc", ACTION="add", SYMLINK="rtc", MODE="0666"' > /etc/udev/rules.d/rtc1.rules

But I’m not sure whether this triggers an additional sync as well.

Since at a later boot stage network time sync updates the system clock anyway, do you need the RTC for an offline device and it can sync at a later boot stage, or does it need to be early?

A script in /var/lib/dietpi/postboot.d/ will be executes at late boot stage, otherwise a systemd service would be required.

MichaIng
Issue was marked as solved by OT and a solution was update on first post

Ah whoops, that is the issue when browsing the forum with a small mobile phone display :slight_smile:.

Little note if someone fails the use rc.local: It needs to be executable: chmod +x /etc/rc.local
The service runs automatically then. It cannot be enabled since it’s a static service without any install definition :wink:.

/var/lib/dietpi/postboot.d/ is the very similar alternative that allows to add individual scripts.

Hi,

Yes I am the guy who asked the question on the RTC DS3231 + RPi on github and thanks to MichaIng I succeed in connecting my DS3231 to the Rpi but it was not enough to automatically update the system time at startup before updating it via ntp.

To make it fully functional on my RPi I had to do the same kind of thing that vmiceli did.
If it can be useful for other RPi user, I followed the tutorial here : https://learn.adafruit.com/adding-a-real-time-clock-to-raspberry-pi/set-rtc-time

Yes this is an old thread but it is the one that hits top of my search results and so where I started trying to do the same. Instructions didn’t work but got me started on the path.

The biggest issue is that the Neo seems to have a RTC onboard that doesn’t have the battery powered lines connected.. This causes /dev/rtc0 to be created and the system attaches to it during boot. Adding an external clock gets added as /dev/rtc1 and the system doesn’t use it.

Helps but a) didn’t work (newer version years later I suspect) and b) during boot the clock attaches to the first found, so sets from the built-in rtc, making the system time is wrong.

After lots of reading various posts online and lots of experimenting.. I have a solution that is somewhat straight forward and can be re-produced..

From a new/clean system..

First step is to kill the built in rtc, /dev/rtc0 that makes this so difficult..

install device-tree-compiler and i2c-tools

Use dtc to convert sun8i-h3-nanopi-neo.dtb to source text. Edit the source text to remove the rtc section and the rtc line near the bottom.

Convert your edited source back to the blob.

Remove the fake-clock

apt purge fake-hwclock

Create a service to initialize the ds3231,

vi /etc/systemd/system/rtc.service

[Unit]
Description=Start External RTC
Before=basic.target
After=local-fs.target sysinit.target
DefaultDependencies=no

[Service]
Type=oneshot
ExecStart=/etc/rtc.sh

[Install]
WantedBy=basic.target

vi /etc/rtc.sh

#!/bin/bash

echo "ds1307 0x68" > /sys/class/i2c-dev/i2c-0/device/new_device

Make executable,

chmod 744 /etc/rtc.sh

enable the new service,

systemctl enable rtc.service

and reboot.

I tried but it didn’t work when I tried doing it with just ExecStart.

After the system is running again leave the clock for a couple hours so the external clock updates, do your other setup if needed/wanted..

Shutdown the system and remove power, wait 5 minutes, 8 hours, however long you wish, then reapply power.

After the system boots,

dmesg | grep rtc
[   19.341579] rtc-ds1307 0-0068: registered as rtc0
[   19.342659] rtc-ds1307 0-0068: setting system clock to 2025-11-11T04:52:59 UTC (1762836779)

This will confirm it is working, the built in rtc would show at ~6 seconds, you see the correct time, a few seconds after you connected power.

If there is an overlay that could disable the onboard rtc I couldn’t find it and I don’t have the knowledge/experience to create it. Removing the RTC was simple and worked the first time. If there is ever an update that updates the device-tree files, edit the new file and make the same changes.

1 Like

The udev rule does not make /dev/rtc0 the external one, but makes /dev/rtc a symlink to /dev/rtc1. hwclock uses /dev/rtc as 2nd device node after /dev/rtc0 when used to set system clock from RTC.

But looking back at it, this does not help, and did not help that time either: As can be seen from kernel logs, RTC to system clock is done by the driver/kernel, not by any userland service. And the device it uses is hardcoded. Check via:

grep CONFIG_RTC_HCTOSYS /boot/config-

Throws =y and =rtc0 in all cases I just checked.

It can be adjusted later via userland. E.g. install util-linux-extra (only needed from Trixie on), and then run

hwclock --hctosys --rtc /dev/rtc1

or shorter

hwclock -sf /dev/rtc1

from your script, after the RTC device has been created via I2C.

But cleaner is indeed to just remove the unnecessary onboard RTC device. There is no device tree overlay for this indeed. I cannot find any hint about an onboard RTC exposed anywhere on the PCB: NanoPi NEO3 - FriendlyELEC WiKi
Maybe the RK3328 itself includes it? At least the RK3328 seems to have the same issue: SOLVED: How to add an external RTC module to ROCK64 board - Advanced users - Development - Armbian Community Forums
In that case, makes sense to disable it our end. Don’t ask me why it is enabled in the first place.

Here the device tree it uses, which does not have any RTC node: build/patch/kernel/archive/rockchip64-6.12/dt/rk3328-nanopi-neo3-rev02.dts at ebc129fa5299dd723677f591a39a5969314080fb · armbian/build · GitHub
Let’s check the R2S device tree it includes: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2s.dts?h=linux-6.12.y
None either, further the RK3328 include: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm64/boot/dts/rockchip/rk3328.dtsi?h=linux-6.12.y

Hmm, nothing there. Checking Armbian patches:

Oh … lol now I see @kevinds you do not use a NEO3 this topic is about, but the NEO (1) :smile:. Okay, looping through device trees again:

Yeah, that chip has a builtin RTC, e.g. pointed out here: https://linux-sunxi.org/RTC
Ah lol, that page, starting similarly incomplete as our instructions above with the /dev/rtc symlink and missing --rtc /dev/rtc1 argument for hwclock. But see at the bottom things are expanded pretty similarly to what I wrote above :smiley:.

The major question is: Why is that RTC node enabled OOTB in mainline Linux? I’d expect it to be present, but disabled in the H3 include of the device tree, and then enabled only in the SBC-specific device trees that do expose at least a socket or pins to attach a battery.

I’ll test on an M1 whether disabling the node generally works. If so, I’ll add a patch to disable it for all H3 in general, and loop through the boards we support, checking whether any of them has any documented way to attach an RTC battery.

Works with this overlay:

root@DietPi:~# cat /boot/overlay-user/disable-rtc.dtso
/dts-v1/;
/plugin/;
/ {
        fragment@0 {
                target = <&rtc>;
                __overlay__ {
                        status = "disabled";
                };
        };
};
root@DietPi:~# ls -l /dev/rtc*
ls: cannot access '/dev/rtc*': No such file or directory
root@DietPi:~# dmesg | grep rtc
root@DietPi:~#

Let’s check which H3 board does actually have an RTC:

Yeah, makes sense to disable it for all H3 boards, and enable it for the R1 only. Actually the node is defined in the H3/H5 combined include. Let’s check for H5 boards as well:

  • NanoPi K1 Plus :cross_mark:
  • NanoPi NEO2 :cross_mark:
  • NanoPi NEO Plus2 :cross_mark:

Yeah, we’ll just patch the H3+H5 include to disable it right there: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi#n864
And then re-enable it on NanoPi R1 only: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm/boot/dts/allwinner/sun8i-h3-nanopi-r1.dts?h=linux-6.12.y

I did experiment with that however the issue with adjusting later via userland is that the clock has already been set to the wrong time.

I did search for that answer. All I could find were people fighting to disable it. :wink:

Indeed. Around then was when I decided to go nuclear and just look for a way to kill it.

:smiley: :smiley:

Thank you.

Yeah, you’d need a service which does that very early, with DefaultDependencies=no, but after the node exists. Probably easiest with a udev rule that reacts on the I2C node, and calls the script to create the I2C device and set the clock from there. But before fiddling with this, I’m about to create a test kernel build.

EDIT: Ah you mean the external RTC has been set to the wrong clock obtained from the onbaord RTC? Okay I see, that makes it even harder to reliably use an external RTC then. I was not thinking about that. The more an argument that we do the right thing to disable it by default.

Oh, can you link that? I was thinking to suggest just this via Linux mailing list. But if it was tried and failed already, I batter save that time :sweat_smile:.

Here the patch: sunxi: disable RTC for all H3/H5 boards but NanoPi R1 · MichaIng/build@15315cf · GitHub
Here the kernel build: Armbian · MichaIng/DietPi@ccdda4f · GitHub
Will appear here once done in ~30 minutes: Index of /downloads/binaries/testing
But before testing it, I need to check the logs whether the patch has been applied correctly. For sunxi, patches are distributed via dedicated subdirs, and I am not 100% sure they are applied when just put into the parent dir, like for other kernel families.

Sorry, no. I was meaning I searched for the why and my only results were for people trying to disable the (near useless) onboard RTC.

Best I can figure is that it is/was included by FriendlyARM/FriendlyELEC so it was just accepted as is.

Ah okay, misunderstanding my end. Probably you are right. So chances are good the change will be accepted as well. But the problem is that I am too lazy to check all H3+H5 boards supported by Linux right now. At least we have the one’s DietPi officially support as starting point already.

Kernel build done, and the patch was applied correctly. Good to know that using the subdir series is not needed. To test:

cd /tmp
wget https://dietpi.com/downloads/binaries/testing/linux-{dtb,image}-current-sunxi.deb
sudo dpkg -i linux-{dtb,image}-current-sunxi.deb
sudo reboot

@kevinds the test kernel does the job on my NanoPi M1. Let me know whether it really solves the issue at its core for you, so that you can remove all workarounds and custom services you added, and just have the kernel applying the DS3231 on early boot automatically.

I’m still waiting for a PR to be merged at Armbian for raising the upstream kernel version, since it did not raise since last month. But would skip or cherry-pick that and merge the kernel to our APT repository, if you can verify that it works as expected.