diff --git a/examples/protocols/sntp/Makefile b/examples/protocols/sntp/Makefile new file mode 100644 index 00000000..e6ef17be --- /dev/null +++ b/examples/protocols/sntp/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := sntp + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/protocols/sntp/README.md b/examples/protocols/sntp/README.md new file mode 100644 index 00000000..8b19d72d --- /dev/null +++ b/examples/protocols/sntp/README.md @@ -0,0 +1,29 @@ +# Example: using LwIP SNTP module and time functions + +This example demonstrates the use of LwIP SNTP module to obtain time from Internet servers. See the README.md file in the upper level 'examples' directory for more information about examples. + +## Obtaining time using LwIP SNTP module + +ESP8266 connects to WiFi and obtains time using SNTP. +See `initialize_sntp` function for details. + +## Working with time + +To get current time, [`gettimeofday`](http://man7.org/linux/man-pages/man2/gettimeofday.2.html) function may be used. Additionally the following [standard C library functions](https://en.cppreference.com/w/cpp/header/ctime) can be used to obtain time and manipulate it: + + gettimeofday + time + asctime + clock + ctime + difftime + gmtime + localtime + mktime + strftime + +To set time, [`settimeofday`](http://man7.org/linux/man-pages/man2/settimeofday.2.html) POSIX function can be used. It is used internally in LwIP SNTP library to set current time when response from NTP server is received. + +## Timezones + +To set local timezone, use [`setenv`](http://man7.org/linux/man-pages/man3/setenv.3.html) and [`tzset`](http://man7.org/linux/man-pages/man3/tzset.3.html) POSIX functions. First, call `setenv` to set `TZ` environment variable to the correct value depending on device location. Format of the time string is described in [libc documentation](https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html). Next, call `tzset` to update C library runtime data for the new time zone. Once these steps are done, `localtime` function will return correct local time, taking time zone offset and daylight saving time into account. diff --git a/examples/protocols/sntp/main/Kconfig.projbuild b/examples/protocols/sntp/main/Kconfig.projbuild new file mode 100644 index 00000000..92a75195 --- /dev/null +++ b/examples/protocols/sntp/main/Kconfig.projbuild @@ -0,0 +1,17 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + + Can be left blank if the network has no security set. + +endmenu diff --git a/examples/protocols/sntp/main/component.mk b/examples/protocols/sntp/main/component.mk new file mode 100644 index 00000000..a98f634e --- /dev/null +++ b/examples/protocols/sntp/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/protocols/sntp/main/sntp_example_main.c b/examples/protocols/sntp/main/sntp_example_main.c new file mode 100644 index 00000000..b6e8c903 --- /dev/null +++ b/examples/protocols/sntp/main/sntp_example_main.c @@ -0,0 +1,174 @@ +/* LwIP SNTP example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" + +#include "nvs_flash.h" + +#include "lwip/apps/sntp.h" + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +/* FreeRTOS event group to signal when we are connected & ready to make a request */ +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const int CONNECTED_BIT = BIT0; + +static const char *TAG = "sntp_example"; + +static void initialize_sntp(void) +{ + ESP_LOGI(TAG, "Initializing SNTP"); + sntp_setoperatingmode(SNTP_OPMODE_POLL); + sntp_setservername(0, "pool.ntp.org"); + sntp_init(); +} + +static void obtain_time(void) +{ + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, + false, true, portMAX_DELAY); + initialize_sntp(); + + // wait for time to be set + time_t now = 0; + struct tm timeinfo = { 0 }; + int retry = 0; + const int retry_count = 10; + + while (timeinfo.tm_year < (2016 - 1900) && ++retry < retry_count) { + ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count); + vTaskDelay(2000 / portTICK_PERIOD_MS); + time(&now); + localtime_r(&now, &timeinfo); + } +} + +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + switch (event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + break; + + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP8266 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + + default: + break; + } + + return ESP_OK; +} + +static void sntp_example_task(void *arg) +{ + time_t now; + struct tm timeinfo; + char strftime_buf[64]; + + time(&now); + localtime_r(&now, &timeinfo); + + // Is time set? If not, tm_year will be (1970 - 1900). + if (timeinfo.tm_year < (2016 - 1900)) { + ESP_LOGI(TAG, "Time is not set yet. Connecting to WiFi and getting time over NTP."); + obtain_time(); + } + + // Set timezone to Eastern Standard Time and print local time + // setenv("TZ", "EST5EDT,M3.2.0/2,M11.1.0", 1); + // tzset(); + + // Set timezone to China Standard Time + setenv("TZ", "CST-8", 1); + tzset(); + + while (1) { + // update 'now' variable with current time + time(&now); + localtime_r(&now, &timeinfo); + + if (timeinfo.tm_year < (2016 - 1900)) { + ESP_LOGE(TAG, "The current date/time error"); + } else { + strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); + ESP_LOGI(TAG, "The current date/time in Shanghai is: %s", strftime_buf); + } + + ESP_LOGI(TAG, "Free heap size: %d\n", esp_get_free_heap_size()); + vTaskDelay(1000 / portTICK_RATE_MS); + } +} + +static void initialise_wifi(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); +} + +void app_main() +{ + //Initialize NVS + esp_err_t ret = nvs_flash_init(); + + if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + + ESP_ERROR_CHECK(ret); + + initialise_wifi(); + + // SNTP service uses LwIP, please allocate large stack space. + xTaskCreate(sntp_example_task, "sntp_example_task", 2048, NULL, 10, NULL); +}