Tulisan ini saya buat setelah membaca bahwa NIST menyediakan jam atom yang paling akurat didunia, pemakaian time server NIST yang dapat diakses melalui alamat time.nist.gov dengan menggunakan Day Time Protocol (RFC-867) pada port 13, maupun melalui Network Time Protocol (RFC-1305) pada port 113.
Pada tulisan ini saya mencoba men-sinkron software RTC pada WeMos D1 mini dengan time.nist.gov melalui Day Time Protocol.
Response dari NIST time server adalah berupa suatu string sepanjang 49 karakter yang diawali dengan suatu karakter line feed (LF) dengan gambaran format sebagai berikut
--x57700 16-11-08 12:33:48 00 0 0 454.5 UTC(NIST) *
--xJJJJJ YR-MO-DA HH:MM:SS TT L H msADV UTC(NIST) *
-- 1 2 3 4
--1234567890123456789012345678901234567890123456789
--first characater x is LF
Karena response adalah berupa string sehingga kita harus memisahkan masing-masing bagian menjadi tahun (YR), bulan (MO), hari (DA), jam(HH), menit(MM), detik(SS), dan milidetik (msADV) dengan menggunakan fungsi bantu string.sub(). Sebelum melakukan pemisahan, maka adalah penting untuk memastikan respon memiliki signature NIST.
Sesuatu hal yang menjadi masalah untuk melakukan setting waktu pada software RTC pada NodeMCU LUA adalah perintah rtctimer.set(unix epoch time, mikrodetik) menggunakan unix epoch time yang merupakan jumlah detik sejak 1 Januari 1970.
Setelah melakukan pencarian diinternet, ternyata NodeMCU Lua tidak menyediakan fungsi untuk konversi datetime menjadi unix epoch time, sehingga dalam hal ini kita perlu membuat library sendiri. Hal pertama yang perlu menjadi perhatian adalah adanya perbedaan jumlah hari pada tahun kabisat dengan tahun biasa khususnya pada bulan February.
Berikut ini adalah library yang saya siapkan untuk fungsi konversi cal2epoch yang beberapa bagian saya adaptasi dari rtctime.c yang ada di source code NodeMCU dengan rincian koding berikut ini.
mylib.lua
-- berberapa bagian saya adaptasi dari rtctime.c
-- dikoding oleh: hendra soewarno (0119067305)
local moduleName = ...Adapun contoh unit test untuk module mylib.lua:
local M = {}
_G[moduleName] = M
--day per month/jumlah hari perbulan?
local dpm = {
0,
(31),
(31+28),
(31+28+31),
(31+28+31+30),
(31+28+31+30+31),
(31+28+31+30+31+30),
(31+28+31+30+31+30+31),
(31+28+31+30+31+30+31+31),
(31+28+31+30+31+30+31+31+30),
(31+28+31+30+31+30+31+31+30+31),
(31+28+31+30+31+30+31+31+30+31+30),
(31+28+31+30+31+30+31+31+30+31+30+31)
}
function mod(a, b)
return a - (math.floor(a/b))*b
end
function isleap (year)
-- every fourth year is a leap year except for century years that are
-- not divisible by 400.
return (mod(year,4)==0 and (mod(year,100)~=0 or mod(year,400)==0))
end
function M.cal2epoch(yyyy, mo, dd, hh, mm, ss)
days = 0
for year = 1970, yyyy-1 do
if isleap(year) then
days = days + 366
else
days = days + 365
end
end
days = days + dpm[mo]
if isleap(yyyy) and mo > 2 then days = days + 1 end
days = days + dd - 1
seconds = days*24*60*60 + hh*60*60+ mm*60 + ss
return seconds
end
return M
require("mylib")Yang harus menghasil nilai true.
--unit test
print(mylib.cal2epoch(2016,11,8,21,15,25)==1478639725)
Selanjutnya adalah menyiapkan koding untuk koneksi wifi, dan sikronisasi waktu.
mylib.lua
Silakan mengacu pada koding diatascredentials.lua
Silakan mengacu pada koding tulisan saya sebelumnyainit.lua
Silakan mengacu pada koding tulisan saya sebelumnya
app.lua
--x57700 16-11-08 12:33:48 00 0 0 454.5 UTC(NIST) *
--xJJJJJ YR-MO-DA HH:MM:SS TT L H msADV UTC(NIST) *
-- 1 2 3 4
--1234567890123456789012345678901234567890123456789
--first characater x is LF
require("mylib")
retried = 1
conn=net.createConnection(net.TCP, false)
function setTime(conn, pl)
-- contain NIS signature
if string.find(pl, "NIST") > 0 then
tmr.stop(0)
print(pl)
year = 2000+tonumber(string.sub(pl,8,9))
month = tonumber(string.sub(pl,11,12))
day = tonumber(string.sub(pl,14,15))
hour = tonumber(string.sub(pl,17,18))
minute = tonumber(string.sub(pl,20,21))
second = tonumber(string.sub(pl,23,24))
mikrosecond = tonumber(string.sub(pl,33,37))*1000
-- datetime to unix epoch time
unixtime = mylib.cal2epoch(year, month, day, hour, minute, second)
-- update software RTC
rtctime.set(unixtime, mikrosecond)
utc = rtctime.epoch2cal(rtctime.get())
print("Waktu adalah UTC:" .. string.format("%04d/%02d/%02d %02d:%02d:%02d", utc["year"], utc["mon"], utc["day"], utc["hour"], utc["min"], utc["sec"]))
-- WIB UTC + 7
wib = rtctime.epoch2cal(rtctime.get()+7*60*60) -- tambah 7
print("Waktu adalah WIB:" .. string.format("%04d/%02d/%02d %02d:%02d:%02d", wib["year"], wib["mon"], wib["day"], wib["hour"], wib["min"], wib["sec"]))
end
end
function syncTime()
print("trying time.nist.gov " .. retried)
conn:on("receive", setTime)
conn:connect(13,"time.nist.gov")
retried = retried + 1
-- close connection if not got response in minute
tmr.alarm(1,1000,0, function() conn:close() end)
end
syncTime()
--no more frequently than once every 4 seconds
tmr.alarm(0,5000,1, syncTime)
Catatan:
- NIST time server menyarankan agar tidak melakukan lebih dari satu koneksi dalam jangka waktu 4 detik, jika terdeteksi maka koneksi anda berpotensi untuk diblokir.
- Pada algoritma diatas saya menggunakan strategi jika tidak ada respon dalam 1 detik, maka koneksi otomatis ditutup.
- Algoritma diatas akan terus mencoba sinkronisasi waktu per 4 detik sampai berhasil dengan menggunakan fungsi tmr.alarm.
Gambar 1. Hasil sikronisasi RTC software ke NIST time server
Tidak ada komentar:
Posting Komentar