A common task with embedded systems is to use the RTC to timestamp events. However, the system architect needs to find a way of synchronizing the devices RTC time with an external time source. Additionally, the designer needs to deal with the problem of drifting RTC clocks, especially for long-running devices. This article discusses an lwIP+SNTP-based approach for STM32 devices using the ChibiOS RTOS. The lwIP-specific part of this article is also applicable to other types of microcontrollers.
For high-accuracy or long-running applications, RTC clock drift also has to be taken into account. Depending on the clock source in use, the clock frequency can deviate significantly from the nominal value.
On the STM32F4 for example, you can derive the RTC clock from: The HSE/HSI main oscillator The LSI oscillator * The LSE oscillator, i.e. a 32.768 kHz external crystal.
In most applications, the RTC is used primarily to preserve a valid time even during periods where the primary power supply is unavailable. For this reason, these systems include some type of backup battery (usually a CR2032) driving the RTC. In this case you obviously can’t derive the RTC clock from HSE/HSI as these clocks aren’t active when the primary power source isn’t available.
The LSI oscillator comes for free with the device, however it has a major problem that most users aren’t aware of: It has extraorbitant tolerance values: According to the STM32F407 datasheet (Table 34: LSI oscillator characteristics), the actual crystal frequency ranges from 17 to 47 kHz with a nominal value of 32 kHz. This is equivalent to a tolerance of -53.1 / +46.8 percent points, or more than 460000 ppm. I can’t think of any real world application that can deal with this magnitude of tolerance. Although the tolerance given is over the full -40 to +105° temperature range, I can confirm that is higher than +-25% at room temperature.
The LSE oscillator is the only option left. So remember to include a 32.768 kHz crystal in your design. For this specific use I believe that crystal oscillators are not the right choice because of their relatively high power consumption.
There are some major problems left: How to initially get the current timestamp onto the device (depends on your deployment process and is usually not an issue) and how to keep the device synced with an external clock source.
The latter is especially relevant when multiple devices need to be kept in sync and/or a near-monotonic clock source is required. Even with only one percent clock drift, the RTC datetime will drift by more than three days over a single year.
For applications connected to the internet or to any other computer network, the lwIP contrib repository provides a full SNTP client. SNTP is a less accurate version of the NTP protocol which can still be used with any normal NTP server.
However, the library is lacking the most basic documentation. Therefore, I provide an example of how to use it in conjunction with the ChibiOS
The header shown here replaces the
sntp.h header that comes with the library. It includes the compile-time configuration required to work with the
RTCD1 driver. For this example I chose the NTP server
swisstime.ethz.ch because it has a good ping from my location. I recommend you select an appropriate server from the NTP pool.
The configuration is pretty basic and
sntp.c supports some quite useful features that aren’t configure here — most notably, support for multiple NTP servers.
The second step is to configure lwIP properly. This is not a tutorial on lwIP and I assume the reader already knows how the stack works (if not, this article is probably not the best place to start). In order to avoid issues with significant deviations from ideal monotonic clocks, this sample sends SNTP requests every 90 seconds, overriding the default 60 minutes. Which one is better suited entirely depends on your application.
There are two common pitfalls that might be encountered, both of which are located in the
Firstly, the SNTP client permanently consumes an UDP PCB. Make sure to appropriately increase the
MEMP_NUM_UDP_PCB value. Needless to say, you need to enable UDP in order for SNTP to work.
Secondly, the SNTP client calls the send function repeatedly by using the lwIP
sys_timeout() feature. The default options for the number of supported concurrent timeouts is quite limited and will probably not be sufficient. I recommend to increase the
MEMP_NUM_SYS_TIMEOUT value by at least one.
Once these requirements are met, simply call
after initializing the lwIP thread and event loop. If your configuration is correct, the RTC will be updated when the SNTP responses are received. DO NOT call
sntp_init() repeatedly, it will call the send function using the lwIP eventloop by itself..
Note that STM32F4 devices have a millisecond-accuracy RTC. SNTP will only rarely provide an accuracy of better than 1 millisecond. Therefore, the millisecond RTC value might not be significant.
If the application crashes, it’s likely that some lwIP limits have been exceeded. In this case, refer to this previous TechOverflow article describing in detail how to add ARM hardware breakpointing to ChibiOS and LWIP assertions.