diff --git a/components/modules/Kconfig b/components/modules/Kconfig index deb9fefc..a7f9296d 100644 --- a/components/modules/Kconfig +++ b/components/modules/Kconfig @@ -232,5 +232,11 @@ config LUA_MODULE_WS2812 default "n" help Includes the ws2812 module. + +config LUA_MODULE_TIME + bool "time module" + default "n" + help + Includes the time module. endmenu diff --git a/components/modules/time.c b/components/modules/time.c new file mode 100644 index 00000000..1afc80b3 --- /dev/null +++ b/components/modules/time.c @@ -0,0 +1,159 @@ +#include "module.h" +#include "lauxlib.h" +#include "platform.h" + +#include +#include +#include + +#include "lwip/apps/sntp.h" + + +#define ADD_TABLE_ITEM(L, key, val) \ + lua_pushinteger (L, val); \ + lua_setfield (L, -2, key); + +void inline time_tmToTable(lua_State *L, struct tm *date) +{ + lua_createtable (L, 0, 9); + + ADD_TABLE_ITEM (L, "yday", date->tm_yday + 1); + ADD_TABLE_ITEM (L, "wday", date->tm_wday + 1); + ADD_TABLE_ITEM (L, "year", date->tm_year + 1900); + ADD_TABLE_ITEM (L, "mon", date->tm_mon + 1); + ADD_TABLE_ITEM (L, "day", date->tm_mday); + ADD_TABLE_ITEM (L, "hour", date->tm_hour); + ADD_TABLE_ITEM (L, "min", date->tm_min); + ADD_TABLE_ITEM (L, "sec", date->tm_sec); + ADD_TABLE_ITEM (L, "dst", date->tm_isdst); +} + +static int time_get(lua_State *L) +{ + struct timeval tv; + gettimeofday (&tv, NULL); + lua_pushnumber (L, tv.tv_sec); + lua_pushnumber (L, tv.tv_usec); + return 2; +} + +static int time_set(lua_State *L) +{ + uint32_t sec = luaL_checknumber (L, 1); + uint32_t usec = 0; + if (lua_isnumber (L, 2)) + usec = lua_tonumber (L, 2); + + struct timeval tv = { sec, usec }; + settimeofday (&tv, NULL); + + return 0; +} + +static int time_getLocal(lua_State *L) +{ + time_t now; + struct tm date; + + time(&now); + localtime_r(&now, &date); + + /* construct Lua table */ + time_tmToTable(L, &date); + + return 1; +} + +static int time_setTimezone(lua_State *L) +{ + const char *timezone = luaL_checkstring(L, 1); + + setenv("TZ", timezone, 1); + tzset(); + + return 0; +} + +static int time_initNTP(lua_State *L) +{ + size_t l; + const char *server = luaL_optlstring (L, 1, "pool.ntp.org", &l); + + sntp_setoperatingmode(SNTP_OPMODE_POLL); + sntp_setservername(0, server); + sntp_init(); + + return 0; +} + +static int time_ntpEnabled(lua_State *L) +{ + lua_pushboolean(L, sntp_enabled()); + + return 1; +} + +static int time_ntpStop(lua_State *L) +{ + sntp_stop(); + + return 0; +} + +static int time_epoch2cal(lua_State *L) +{ + struct tm *date; + + time_t now = luaL_checkint (L, 1); + luaL_argcheck (L, now >= 0, 1, "wrong arg range"); + + date = gmtime (&now); + + /* construct Lua table */ + time_tmToTable(L, date); + + return 1; +} + +static int time_cal2epoc(lua_State *L) +{ + struct tm date; + luaL_checkanytable (L, 1); + + lua_getfield (L, 1, "year"); + date.tm_year = luaL_optinteger(L, -1, 1900) - 1900; + + lua_getfield (L, 1, "mon"); + date.tm_mon = luaL_optinteger(L, -1, 1) - 1; + + lua_getfield (L, 1, "day"); + date.tm_mday = luaL_optinteger(L, -1, 0); + + lua_getfield (L, 1, "hour"); + date.tm_hour = luaL_optinteger(L, -1, 0); + + lua_getfield (L, 1, "min"); + date.tm_min = luaL_optinteger(L, -1, 0); + + lua_getfield (L, 1, "sec"); + date.tm_sec = luaL_optinteger(L, -1, 0); + + lua_pushnumber (L, mktime(&date)); + + return 1; +} + +static const LUA_REG_TYPE time_map[] = { + { LSTRKEY("set"), LFUNCVAL(time_set) }, + { LSTRKEY("get"), LFUNCVAL(time_get) }, + { LSTRKEY("getlocal"), LFUNCVAL(time_getLocal) }, + { LSTRKEY("settimezone"), LFUNCVAL(time_setTimezone) }, + { LSTRKEY("initntp"), LFUNCVAL(time_initNTP) }, + { LSTRKEY("ntpenabled"), LFUNCVAL(time_ntpEnabled) }, + { LSTRKEY("ntpstop"), LFUNCVAL(time_ntpStop) }, + { LSTRKEY("epoch2cal"), LFUNCVAL(time_epoch2cal) }, + { LSTRKEY("cal2epoch"), LFUNCVAL(time_cal2epoc) }, + { LNILKEY, LNILVAL } +}; + +NODEMCU_MODULE(TIME, "time", time_map, NULL); diff --git a/docs/en/modules/time.md b/docs/en/modules/time.md new file mode 100644 index 00000000..4c7185ce --- /dev/null +++ b/docs/en/modules/time.md @@ -0,0 +1,206 @@ +# Timer Module +| Since | Origin / Contributor | Maintainer | Source | +| :----- | :-------------------- | :---------- | :------ | +| 2018-11-25 | [Skirmantas Lauzikas](https://github.com/x-logLT) | [Skirmantas Lauzikas](https://github.com/x-logLT) | [time.c](../../../components/modules/time.c)| + +This module offers facilities for coverting beteen unix time and calendar, setting/getting system time, locale and controll of NTP client. + +## time.set() +Sets system time to a given timestamp in the Unix epoch (i.e. seconds from midnight 1970/01/01). + +#### Syntax +`time.set(time)` + +#### Parameters +- `time` number of seconds since the Epoch + +#### Returns +`nil' + +#### Example +```lua +--set time to 2018-11-20 01:40:50 +time.set(1542678050) +``` + +#### See also +[`time.cal2epoc()`](#timecal2epoch) + +## time.get() +Returns current system time in the Unix epoch (i.e. seconds from midnight 1970/01/01). + +#### Syntax +time.get() + +#### Parameters +none + +#### Returns +A two-value timestamp consisting of: + +- `sec` seconds since the Unix epoch +- `usec` the microseconds part + +#### Example +```lua +sec, usec = time.get() +``` + +#### See also +[`time.epch2cal()`](#timeepoch2cal) + +## time.getlocal() +Returns current system time adjusted for the locale in calendar format. + +#### Syntax +time.getlocal() + +#### Parameters +none + +#### Returns +A table containing the fields: + +- `year` 1970 ~ 2038 +- `mon` month 1 ~ 12 in current year +- `day` day 1 ~ 31 in current month +- `hour` +- `min` +- `sec` +- `yday` day 1 ~ 366 in current year +- `wday` day 1 ~ 7 in current weak (Sunday is 1) +- `dst` day time adjustment: + - 1 (DST in effect, i.e. daylight time) + - 0 (DST not in effect, i.e. standard time) + - -1 (Unknown DST status) + +#### Example +```lua +localTime = time.getlocal() +print(string.format("%04d-%02d-%02d %02d:%02d:%02d DST:%d", localTime["year"], localTime["mon"], localTime["day"], localTime["hour"], localTime["min"], localTime["sec"], localTime["dst"])) +``` + +## time.settimezone() +Sets correct format for Time Zone locale + +#### Syntax +`time.settimezone(timezone)` + +#### Parameters +- `timezone` a string representing timezone, can also include DST adjustment. For full syntax see [TZ variable documentation](http://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html). + +#### Returns +`nil` + +#### Example +```lua +--set timezone to Eastern Standard Time +time.settimezone("EST+5") +``` + +## time.initntp() +Initializes and starts ntp client + +#### Syntax +`time.initntp([ntpAddr])` + +#### Parameters +- `ntpAddr` address of a ntp server, defaults to "pool.ntp.org" if none is specified + +#### Returns +`nil` + +#### Example +```lua +time.initntp("pool.ntp.org") +``` + +## time.ntpenabled() +Checks if ntp client is enabled. + +#### Syntax +`time.ntpenabled()` + +#### Parameters +none + +#### Returns +`true' if ntp client is enabled. + +## time.ntpstop() +Stops ntp client. + +#### Syntax +`time.ntpstop()` + +#### Parameters +none + +#### Returns +`nil` + +## time.epoch2cal() +Converts timestamp in Unix epoch to calendar format + +#### Syntax +`time.epoch2cal(time) + +#### Parameters +- `time` number of seconds since the Epoch + +#### Returns +A table containing the fields: + +- `year` 1970 ~ 2038 +- `mon` month 1 ~ 12 in current year +- `day` day 1 ~ 31 in current month +- `hour` +- `min` +- `sec` +- `yday` day 1 ~ 366 in current year +- `wday` day 1 ~ 7 in current weak (Sunday is 1) +- `dst` day time adjustment: + - 1 (DST in effect, i.e. daylight time) + - 0 (DST not in effect, i.e. standard time) + - -1 (Unknown DST status) + +#### Example +```lua +--Gets current time calendar format, no locale adjustment +time = time.epoch2cal(time.get()) +print(string.format("%04d-%02d-%02d %02d:%02d:%02d DST:%d", time["year"], time["mon"], time["day"], time["hour"], time["min"], time["sec"], time["dst"])) +``` + +## time.cal2epoch() +Converts calendar table to a timestamp in Unix epoch + +#### Syntax +`time.cal2epoch(calendar)` + +#### Parameters +- `calendar` Table containing calendar info. + - `year` 1970 ~ 2038 + - `mon` month 1 ~ 12 in current year + - `day` day 1 ~ 31 in current month + - `hour` + - `min` + - `sec` + +#### Returns +number of seconds since the Epoch + +#### Example + +```lua +calendar={} +calendar.year = 2018-11-20 +calendar.mon = 11 +calendar.day = 20 +calendar.hour = 1 +calendar.min = 40 +calendar.sec = 50 + +timestamp = time.cal2epoch(calendar) +time.set(timestamp) +``` + diff --git a/mkdocs.yml b/mkdocs.yml index 808cacbc..dcb2bfd2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -55,6 +55,7 @@ pages: - 'sodium': 'en/modules/sodium.md' - 'spi': 'en/modules/spi.md' - 'struct': 'en/modules/struct.md' + - 'time': 'en/modules/time.md' - 'tmr': 'en/modules/tmr.md' - 'u8g2': 'en/modules/u8g2.md' - 'uart': 'en/modules/uart.md'