Post your best Lua script examples here

User avatar
By jim121049
#43705 Using snippets of code that I've found on this forum and elsewhere, here are three methods of obtaining the time and date that I've used.

The first method (and the one I prefer) uses the NIST Internet Time Service (ITS) and the Daytime Protocol. You'll have to do a little arithmetic to convert UTC to local time. I prefer using the ITS since the Daytime Protocol incorporates a two digit code (00 to 99) that indicates whether the United States is on Standard Time (ST) or Daylight Saving Time (DST).
Code: Select all-- connects to a NIST Daytime server to get the current date and time

TZ=-5       -- my time zone is Eastern Standard Time
year=0      -- global year
month=0     -- global month
day=0       -- global day
hour=0      -- global hour
minute=0    -- global minute
second=0    -- global second

function getDayTime()
   local tt=0
   
   local conn=net.createConnection(net.TCP,0)
   conn:connect(13,"time.nist.gov")
   
   -- on connection event handler
   conn:on("connection",
      function(conn, payload)
         --print("Connected...")
      end -- function
   ) -- end of on "connecttion" event handler
         
   -- on receive event handler         
   conn:on("receive",
      function(conn,payload)
        --print(payload)
        --1234567890123456789012345678901234567890123456789
        -- JJJJJ YR-MO-DA HH:MM:SS TT L H msADV UTC(NIST) *
        if string.sub(payload,39,47)=="UTC(NIST)" then
           year=tonumber(string.sub(payload,8,9))+2000
           month=tonumber(string.sub(payload,11,12))
           day=tonumber(string.sub(payload,14,15))
           hour=tonumber(string.sub(payload,17,18))
           minute=tonumber(string.sub(payload,20,21))
           second=tonumber(string.sub(payload,23,24))
           tt=tonumber(string.sub(payload,26,27))

           hour=hour+TZ    -- convert from UTC to local time
         
           if ((tt>1) and (tt<51)) or ((tt==51) and (hour>1)) or ((tt==1) and (hour<2)) then
              hour=hour+1  -- daylight savings time currently in effect, add one hour
           end
         
           hour=hour%24
        end -- if string.sub(payload,39,47)=="UTC(NIST)" then
      end -- function
   ) -- end of on "receive" event handler

   -- on disconnect event handler           
   conn:on("disconnection",
      function(conn,payload)
         --print("Disconnected...")
         conn=nil
         payload=nil
      end -- function
   )  -- end of on "disconnecttion" event handler
end -- function getDayTime()

-- Execution starts here...
print("\ncontacting NIST server...")
getDayTime() -- contact the NIST daytime server for the current time and date
tmr.alarm(5,500,0,        -- after a half second...
   function()
     if year~=0 then
        print(string.format("%02d:%02d:%02d  %02d/%02d/%04d",hour,minute,second,month,day,year))
     else
        print("Unable to get time and date from the NIST server.")
     end
   end
)
 


The second method uses the Network Time Protocol (RFC-1305). This requires that your ESP8266 firmware incorporate the sntp and rtctime modules. See NodeMCU custom builds for firmware incorporating these modules. Again, you'll have to do the conversion from UTC to local time. You'll also have to determine when to adjust for Daylight Saving Time.

Code: Select all-- sync the ESP8266 Real Time Clock (RTC) to an NTP server
-- retrieve and display the time and date from the ESP8266 RTC
-- requires the sntp and rtctime firmware modules

timeZone=-4 -- time zone (Eastern Daylight Time); -5 for Eastern Standard Time
hour=0
minute=0
second=0
day=0
month=0
year=0

-- returns the hour, minute, second, day, month and year from the ESP8266 RTC seconds count (corrected to local time by tz)
function getRTCtime(tz)
   function isleapyear(y) if ((y%4)==0) or (((y%100)==0) and ((y%400)==0)) == true then return 2 else return 1 end end
   function daysperyear(y) if isleapyear(y)==2 then return 366 else return 365 end end           
   local monthtable = {{31,28,31,30,31,30,31,31,30,31,30,31},{31,29,31,30,31,30,31,31,30,31,30,31}} -- days in each month
   local secs=rtctime.get()
   local d=secs/86400
   local y=1970   
   local m=1
   while (d>=daysperyear(y)) do d=d-daysperyear(y) y=y+1 end   -- subtract the number of seconds in a year
   while (d>=monthtable[isleapyear(y)][m]) do d=d-monthtable[isleapyear(y)][m] m=m+1 end -- subtract the number of days in a month
   secs=secs-1104494400-1104494400+(tz*3600) -- convert from NTP to Unix (01/01/1900 to 01/01/1970)   
   return (secs%86400)/3600,(secs%3600)/60,secs%60,m,d+1,y   --hour, minute, second, month, day, year
end

print("\ncontacting NTP server...")
sntp.sync("0.pool.ntp.org")             -- sync the ESP8266 RTC with the NTP server
tmr.alarm(0,500,0,
   function()
      -- get the hour, minute, second, day, month and year from the ESP8266 RTC
      hour,minute,second,month,day,year=getRTCtime(timeZone)
      if year ~= 0 then
         -- format and print the hour, minute second, month, day and year retrieved from the ESP8266 RTC
         print(string.format("%02d:%02d:%02d  %02d/%02d/%04d",hour,minute,second,month,day,year))
         --print(string.format("%02d:%02d:%02d  %02d/%02d/%04d",getRTCtime(timeZone)))   
      else
         print("Unable to get time and date from the NTP server.")
      end
   end
)   
 


The third method retrieves the local date and time from the web site Time.is. This site looks up your location based on your IP address and provides your local time already compensated for Daylight Saving Time (if applicable). Although this method is probably the easiest of the three, its use is somewhat problematic since the terms of use for the site state: "Automatic refresh and any usage from within scripts and apps is forbidden."

Code: Select all-- connects to the web site "time.is" to retrieve the local time and date

year=0                  -- global year
month=0                 -- global month
day=0                   -- global day
dow=0                   -- global day of week
hour=0                  -- global hour
minute=0                -- global minute
second=0                -- global second

--connect to time.is to get the current time and date
function getTime()

   -- return the number corresponding to the three letter abbreviation of the month
   function nmonth(month) return (string.find("JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC",string.upper(month))+2)/3 end

   -- return the number corresponding to the three letter abbreviation of the day of week
   function ndow(dow) return (string.find("SUNMONTUEWEDTHUFRISAT",string.upper(dow))+2)/3 end

   local conn=net.createConnection(net.TCP,0)
   conn:connect(80,"time.is")
   
   conn:on("connection",
      function(conn, payload)
         conn:send("HEAD / HTTP/1.1\r\n"..
                   "Host: time.is\r\n"..
                   "Accept: */*\r\n"..
                   "User-Agent: Mozilla/4.0 (compatible; esp8266 Lua;)"..
                   "\r\n\r\n")
      end --function
   ) --end of on "connection" event handler
               
   conn:on("receive",
      function(conn,payload)
         --print(payload)
         conn:close()
         local p=string.find(payload,"Expires: ") -- find the time and date string in the payload from time.is
         if p~=nil then
            -- print(string.sub(payload,p,p+39))
            -- extract numbers corresponfing to the hour, minute, second, day, month
            -- and year from the payload received from time.is in response to our HEAD request
            -- p+0123456789012345678901234567890123456789
            --   Expires: Sat, 30 Jan 2016 06:15:11 -0500
            dow=ndow(string.sub(payload,p+9,p+11))
            hour=tonumber(string.sub(payload,p+26,p+27))
            minute=tonumber(string.sub(payload,p+29,p+30))
            second=tonumber(string.sub(payload,p+32,p+33))
            day=tonumber(string.sub(payload,p+14,p+15))
            month=nmonth(string.sub(payload,p+17,p+19))
            year=tonumber(string.sub(payload,p+21,p+24))
         else
            -- print("time.is update failed!")
         end
      end --function
   ) --end of on "receive" event handler
   
   conn:on("disconnection",
      function(conn,payload)
         conn=nil
         payload=nil
      end --function
   )  -- end of on "disconnecttion" event handler
end   
-----------------------------------------------------------------------------
-- script execution starts here...
-----------------------------------------------------------------------------
print("\nContacting time.is...")
getTime()           -- contact time.is for the current time and date
tmr.alarm(0,500,0,  -- after a half second...
   function()     
       if year ~= 0 then   
          print(string.format("%02d:%02d:%02d  %02d/%02d/%04d",hour,minute,second,month,day,year))
       else
          print("Unable to get time and date from the time.is.")       
       end
 
   end
)
 


Again, my thanks to you for your good work if you recognise some of your code here.
User avatar
By TerryE
#43744 What's wrong with the SNTP library or if to want realtime then RTC time?
User avatar
By jim121049
#43796 I've tweaked the SNTP example ( the second one) to automatically determine whether Daylight Saving Time is in effect (in the United States).
Code: Select all-- sync the ESP8266 Real Time Clock (RTC) to an NTP server
-- retrieve and display the time and date from the ESP8266 RTC
-- requires the sntp and rtctime firmware modules

timeZone=-5 -- for me Eastern Time Zone of the US, GMT-5 hours
hour=0
minute=0
second=0
day=0
month=0
year=0

-- returns the hour, minute, second, day, month and year from the ESP8266 RTC seconds count (corrected to local time by tz)
function getRTCtime(tz)

    -- returns 2 if a given year is a leap year, else returns 1
    local function isleapyear(y)
       if ((y%4)==0) or (((y%100)==0) and ((y%400)==0)) then
          return 2
       else
          return 1
       end
    end
   
    -- returns 366 is a given year is a leap year else returns 365
    local function daysperyear(y)
       if isleapyear(y)==2 then
          return 366
       else
          return 365
       end
    end       

    -- determines whether Daylight Saving Time is in effect for a given hour, day, month and year
    local function isDST(hour,day,month,year)

        -- returns the date for Nth day of the month.
        -- for example: nthDate(2016,3,0,2) will return the date of the 2nd Sunday in March 2016 (the 13th) when DST begins
        --              nthDate(2016,11,0,1) will return the date of the 1st Sunday in November 2016 (the 6th) when DST ends
        local function nthDate(year,month,DOW,week)
       
            -- returns a number corresponding to the day of the week (0-7 is Sunday to Saturday)
            local function dow(year,month,day)
               local t={0,3,2,5,0,3,5,1,4,6,2,4}
               if month<3 then
                  year=year-1
               end
               return (year + year/4 - year/100 + year/400 + t[month] + day) % 7
            end -- function dow
       
            targetDate=1
            firstDOW = dow(year,month,targetDate);
            while (firstDOW ~= DOW) do
               firstDOW = (firstDOW+1)%7
               targetDate=targetDate+1
            end
            targetDate = targetDate+(week-1)*7
            return targetDate
        end -- function nthDate
   
        local startDate=nthDate(year,3,0,2) -- day when DST starts this year (2nd Sunday in March)
        local endDate=nthDate(year,11,0,1)  -- day when DST ends this year (1st Sunday in November)
        if ((month>3) and (month<11)) or                      -- is the date between April and October?
           ((month==3) and (day>startDate)) or                -- is the date in March but after the 2nd Sunday in March?
           ((month==3) and (day==startDate) and (hour>1)) or  -- is it after 2AM on the 2nd Sunday in March?
           ((month==11) and (day<endDate)) or                 -- is the date in November but before the 1st Sunday?
           ((month==11) and (day==endDate) and (hour<2)) then -- is it before 2AM on the 1st Sunday in November?
           return true
        else
           return false
        end
    end -- function isDST
   
    local monthtable = {{31,28,31,30,31,30,31,31,30,31,30,31},{31,29,31,30,31,30,31,31,30,31,30,31}} -- days in each month
    local secs=rtctime.get()
    local d=secs/86400
    local y=1970   
    local m=1
    while (d>=daysperyear(y)) do d=d-daysperyear(y) y=y+1 end   -- subtract the number of seconds in a year
    while (d>=monthtable[isleapyear(y)][m]) do d=d-monthtable[isleapyear(y)][m] m=m+1 end -- subtract the number of days in a month
    secs=secs-1104494400-1104494400+(tz*3600) -- convert from NTP to Unix (01/01/1900 to 01/01/1970)
    local hr=(secs%86400)/3600   -- hours
    local mn=(secs%3600)/60      -- minutes
    local sc=secs%60             -- seconds
    local mo=m                   -- month
    local dy=d+1                 -- day
    local yr=y                   -- year
    if isDST(hr,dy,mo,yr) then hr=hr+1 end -- Daylight Saving Time is in effect, add one hour
    return hr,mn,sc,mo,dy,yr
end -- function getRTCtime

print("\ncontacting NTP server...")
sntp.sync("0.pool.ntp.org")             -- sync the ESP8266 RTC with the NTP server
tmr.alarm(0,500,0,
    function()
        -- get the hour, minute, second, day, month and year from the ESP8266 RTC
        hour,minute,second,month,day,year=getRTCtime(timeZone)
       if year ~= 0 then
           -- format and print the hour, minute second, month, day and year retrieved from the ESP8266 RTC
           print(string.format("%02d:%02d:%02d  %02d/%02d/%04d",hour,minute,second,month,day,year))
           --print(string.format("%02d:%02d:%02d  %02d/%02d/%04d",getRTCtime(timeZone)))   
        else
           print("Unable to get time and date from the NTP server.")
        end
    end
)