Date in milliseconds on OpenWRT on Arduino YUN
Solution 1
Actually there is also a package called coreutils-date
! Didn't know about it! There all standard functionality is included!
Solution 2
On OpenWRT, date
is busybox
, which has limitations, but this is not strictly one of them. The underlying problem is that the libc (uClibc) does not support this GNU strftime extension. (Though neither does glibc, more on that below.)
You should have lua
by default, but that won't help without some other non-default modules.
hwclock
calls gettimeofday()
for comparing/setting the RTC (hardware clock), but it won't output sub-second resolution (accessing RTCs can be sufficient slow that it might not be useful anyway). Other than that OpenWRT only provides the ancient rdate
, which only has whole-second resolution.
There appears to be no straightforward way to get an accurate time stamp directly from /proc
, the most useful time stamp is in /proc/timer_list
(3rd line) which is the uptime in nanoseconds (the resolution will depend on the platform).
If your busybox was built with CONFIG_BUSYBOX_CONFIG_ADJTIMEX
set, then you should be able to use adjtimex
to read the kernel clock (though note that the busybox version has both different arguments and different output to the standard adjtimex.
Normal version, adjtimex -p
, last line of output:
raw time: 1416419719s 146628us = 1416419719.146628
Busybox version, adjtimex
(without -p
!), last 3 lines:
[...]
time.tv_sec: 1416420386
time.tv_usec: 732653
return value: 0 (clock synchronized)
Goldilocks's is a fine solution, assuming you have your OpenWRT cross build setup (highly recommended!).
Your coreutils-date solution works because while coreutils is glibc aware, it is not exclusively glibc. It comes with its own standalone implementation of strftime
(derived from glibc), and uses that to wrap up (via strftime_case()
) the underlying strftime
so as to support various extensions (and falls back to the uClibc version otherwise).
Even glibc (up to the current 2.23) doesn't support %N
, the coreutils strftime()
derived from the canonical glibc version adds %N
and %:z
and a few other changes. Variations and patched versions of strftime()
abound (including versions in bash and gawk).
Solution 3
If you have a C compiler on it and can't find anything else, this will report, e.g.
> millitime && sleep 1 && millitime
14/11/2014 9:39:49:364
14/11/2014 9:39:50:368
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
int main (void) {
struct timeval now;
struct tm *parsed;
if (gettimeofday(&now, NULL) == -1) {
fprintf (
stderr,
"gettimeofday() failed: %s\n",
strerror(errno)
);
return 1;
}
parsed = localtime((const time_t*)&now.tv_sec);
if (!parsed) {
fprintf (
stderr,
"localtime() failed: %s\n",
strerror(errno)
);
return 1;
}
printf (
"%d/%d/%d %d:%02d:%02d:%03d\n",
parsed->tm_mday,
parsed->tm_mon + 1,
parsed->tm_year + 1900,
parsed->tm_hour,
parsed->tm_min,
parsed->tm_sec,
now.tv_usec / 1000
);
return 0;
}
With gcc, just compile it gcc whatever.c -o millitime
. If there's an error (which would be very weird), it reports to stderr and exits with a status of 1. Otherwise it reports to stdout and exits 0.
The milliseconds are rounded down from microseconds.
Solution 4
I've found that I can get timing information to the hundredth of a second from /proc/uptime
in an embedded Linux system with busybox, though converting it to an epoch time is hard because I cannot find any record of the boot time.
Here's some quick sh code:
read UPTIME < /proc/uptime
UPTIME="${UPTIME%%[^0-9.]*}"
If all you need is a timer with precision to 100ths of seconds, this is good enough (time zero, your local epoch, will merely be the time the system booted).
Otherwise, you can cheat and trigger a log entry:
printf '\u04' |nc $HOSTNAME 22 >/dev/null 2>&1 # trigger log entry by poking ssh
LOG="$(logread -l 1 -t)" # read last log entry w/ timestamp
LOG="${LOG##* 20?? \[}" # remove text before timestamp
echo "${LOG%%\]*}" # remove text after timestamp
This consumes 0.026s (median; min=0.025s, max=0.028s, out of 12 runs). Also note that it does pollute your logs ... and I don't know what happens when the logs get "too big" (for whatever value that is); keep in mind logs are stored in memory.
Therefore, if you're going to be polling the time a lot, you should probably only create one bad log entry at the beginning and combine the two above methods as follows:
get_uptime_ms() {
local MS
read UPTIME < /proc/uptime
UPTIME="${UPTIME%%[^0-9.]*}" # strip the other listed time
MS="${UPTIME##*.}0" # extra zero turns 100ths of seconds into 1000ths
UPTIME="${UPTIME%%.*}$MS" # the time since boot in milliseconds
}
get_uptime_ms
printf '\u04' |nc $HOSTNAME 22 >/dev/null 2>&1 # poke ssh to trigger log entry
NOW="$(logread -l 1 -t)" # last log entry, with timestamp
NOW="${NOW#* 20?? \[}" # remove text before timestamp
NOW="${NOW%%\]*}" # remove text after timestamp
MS="${NOW##*.}" # just the milliseconds
NOW="${NOW%%.*}$MS" # append ms to s w/out dot
BOOT=$(( NOW - UPTIME ))
date_ms() {
local S
get_uptime_ms
NOW=$(( BOOT + UPTIME))
S="${NOW%???}" # $NOW in seconds
echo "$S.${NOW#$S}" # put the dot back in, then append milliseconds
}
# now you can run date_ms as much as you like
# and it'll have the epoch time with millisecond precision.
date_ms
date +%s
date_ms
This establishes something to use as the original boot time by subtracting the uptime (to 100th of a second) from the log trigger's timestamp (precise to 1000th of a second) and then updates it with the uptime. This therefore yields 100ths of a second rather than 1000ths, but it only injects one extra entry into logd and the other method isn't much more precise since it costs 0.026s to run. (It's decently consistent on my system, so you could consider running it a bunch of times and subtracting that value.)
Related videos on Youtube
Tschwen
Updated on September 18, 2022Comments
-
Tschwen almost 2 years
I am using OpenWRT on the Arduino YUN and I am trying to get the exact date in milliseconds (DD/MM/YYYY h:min:sec:ms) by getting the time by an timeserver.
Unfortunately
date +%N
just returns%N
, but not the nanoseconds. I heard+%N
is not included in OpenWRT date.So is there any way to get the date (including milliseconds) how I want it?
-
Costas over 9 yearsDo you have
date --rfc-3339=ns
-
Tschwen over 9 yearsNo:
date: unrecognized option --rfc-3339=ns
-
-
rkagerer almost 9 yearsThanks.
awk 'NR==3 {print $3}' /proc/timer_list
came in handy for me when I needed finer resolution time than any of these utilities granted under the build I'm using. -
minghua about 6 yearsThe
/proc/timer_list
3rd line works for me. Not using openwrt, just an embedded linux on busybox. -
Adam Katz over 5 yearsMy OpenWRT router doesn't have
/proc/timer_list
and also lacks anadjtimex
command, but I was able to use/proc/uptime
for 100th-second resolution andlogread -t
for millisecond resolution; see my answer below. No extra packages or compilation needed. -
Maxattax over 2 years
adjtimex | grep -i tv | cut -d: -f2 | sed 's/^ *//g' | tr '\n' '.' | sed 's/.$/\n/'
Produces1649180458.33628