Rabu, 09 November 2016

Tutorial NodeMCU: Sync software RTC to google.com via Web (ESP8266, WeMos, Lua)


Tulisan ini saya buat karena menyadari bahwa lingkungan LAN kami tidak memperolehkan koneksi ke internet selain port 80 untuk keperluan browsing internet. Aplikasi akan melakukan sinkronisasi waktu ke situs web www.google.com dengan melakukan HTTP HEAD request yang biasanya digunakan untuk memeriksa apakah layanan url tersedia, atau file yang diinginkan tersedia (HTTP HEAD request adalah berbeda dengan HTTP GET request).Berikut ini adalah contoh HTTP HEAD request yang dapat dikirim sesaat setelah koneksi berhasil dilakukan.

HEAD / HTTP/1.1\r\n
Accept: */*\r\n
User-Agent: Mozilla/4.0 (compatible; ESP8266 NodeMcu Lua;)\r\n\r\n
Dimana \r\n adalah merepresentasikan CRLF, dan berikut ini adalah hasil response dari server.
Location: http://www.google.co.id/?gws_rd=cr&ei=6kYjWNOYFovvvgSzw424Ag
Cache-Control: private
Content-Type: text/html; charset=UTF-8
P3P: CP="This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info."
Date: Wed, 09 Nov 2016 15:55:22 GMT
Server: gws
Content-Length: 261
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Set-Cookie: NID=90=tP2nCmiU8y7o7oZt24v2mLxAE7WSyvUcu0hYL6kreKdKrlV5C98ut_KYTK7SljVgk7B0XdzRrwNVEXUIE4JHYI2WUh8ssyDJpebpLtNltY6F9OaF-ez5l-43PoZK_HY5; expires=Thu, 11-May-2017 15:55:22 GMT; path=/; domain=.google.com; HttpOnly
Diantara request tersebut terdapat baris Date yang nantinya akan kita parse untuk menggambil komponen tahun, bulan, tanggal, jam, menit dan detik.

Karena belum familiar dengan HTTP request dengan menggunakan Lua, maka saya memulai dengan mencoba source-code yang disediakan pada situs www.nodemcu.com dengan rangkaian koding sebagai berikut:
-- A simple http client
conn=net.createConnection(net.TCP, false) 
conn:on("receive", function(conn, pl) print(pl) end)
conn:connect(80,"www.google.com")
-- tidak ada jaminan koneksi telah berhasil
conn:send("GET / HTTP/1.1\r\nHost: www.nodemcu.com\r\n"
    .."Connection: keep-alive\r\nAccept: */*\r\n\r\n")
Yaitu conn:send langsung dibawah baris conn:connect, sehingga tidak ada suatu jaminan bahwa ketika HTTP GET request dilakukan, conn:connect telah berhasil membuka koneksi dengan server, sehingga response yang diharapkan tidak terjadi. Berdasarkan hasil surfing akhirnya saya melakukan perbaikan dengan koding sebagai berikut:
-- A simple http client
conn=net.createConnection(net.TCP, false)
conn:on("connection", function(conn, pl)
conn:send("GET / HTTP/1.1\r\nHost: www.nodemcu.com\r\n" .."Connection: keep-alive\r\nAccept: */*\r\n\r\n")end)
conn:on("receive", function(conn, pl) print(pl) end)
conn:connect(80,"www.google.com")
Dimana HTTP GET request dilakukan setelah terjadi event on connection, yang berarti bahwa koneksi ke server telah berhasil dilakukan, dan aplikasi client berhasil mendapatkan response dari server.

Berikut ini adalah koding lengkap untuk melakukan HTTP HEAD request, yang diserta dengan setting waktu rtctime.

credentials.lua
Dapat diperoleh pada tulisan sebelumnya
mylib.lua
Dapat diperoleh pada tulisan sebelumnya
init.lua
Dapat diperoleh pada tulisan sebelumnya
app.lua
--Date: Wed, 09 Nov 2016 15:57:58 GMT
--         1         2         3    
--12345678901234567890123456789012345
-- diadaptasikan kembali oleh Hendra Soewarno (0119067305)
require("mylib")

retried = 1
conn=net.createConnection(net.TCP, false)

function sendHeader(conn, pl)
    conn:send("HEAD / HTTP/1.1\r\n"
    .. "Accept: */*\r\n"
    .. "User-Agent: Mozilla/4.0 (compatible; ESP8266 NodeMcu Lua;)\r\n"
    .. "\r\n")
end

function setTime(conn, pl)
    --contain Date signature
    posisi = string.find(pl, "Date")
    if posisi > 0 then
        --cancel next alarm
        tmr.stop(0)
        year = 2000+tonumber(string.sub(pl,posisi+19,posisi+22))
        mon = string.sub(pl,posisi+14,posisi+16)
        month = math.floor(string.find("JanFebMarAprMayJunJulAugSepOctNovDec",mon)/3)+1
        day = tonumber(string.sub(pl,posisi+12,posisi+13))       
        hour = tonumber(string.sub(pl,posisi+23,posisi+24))
        minute = tonumber(string.sub(pl,posisi+26,posisi+27))
        second = tonumber(string.sub(pl,posisi+29,posisi+30))
        mikrosecond = 0
        --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 www.google.com " .. retried)
    conn:on("connection", sendHeader)
    conn:connect(80,"www.google.com")
    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)

Gambar 1. Hasil Run Aplikasi

Tidak ada komentar:

Posting Komentar