Dear Guys and Girls,
/* you can also fast forward to my last post */
How would you accomplish the above mentioned Task? (Using ESP-IDF)
I'm thinking of something similar:
1 Sync Time with NTP Service from a server and set the local clock in the ESP32 (this is well documented and working)
2 Read current usecond till next sec, register a Timer#1 to Trigger when the second arrives
3 In the callback function of the Timer#1 start Timer #2 with the interval of 1 sec.
Could you point me in an other direction? Or is this a correct approach? (I'm trying to sync several esp32 nodes to send sensor data every second).
Thank you
Trigger an action every "exact" second after syncing with NTP Server
Trigger an action every "exact" second after syncing with NTP Server
Last edited by kb3523 on Sat Mar 24, 2018 9:49 am, edited 3 times in total.
-
- Posts: 58
- Joined: Thu Mar 01, 2018 1:26 am
Re: Sync with NTP and trigger an interrupt every second the clock ticks
Check this out:kb3523 wrote:Dear ESP32 Guys and Girls,
How would you accomplish the above mentioned Task? (Using ESP-IDF)
I'm thinking of something similar:
1 Sync Time with NTP Service from a server and set the local clock in the ESP32 (this is well documented and working)
2 Read current usecond till next sec, register a Timer#1 to Trigger when the second arrives
3 In the callback function of the Timer#1 start Timer #2 with the interval of 1 sec.
Could you point me in an other direction? Or is this a correct approach? (I'm trying to sync several esp32 nodes to send sensor data every second).
Thank you
https://github.com/espressif/esp-idf/bl ... pts.h#L132
Re: Sync with NTP and trigger an interrupt every second the clock ticks
Thank you for your hint @thethinker!
Is SNTP_GET_SYSTEM_TIME under the surface the same as calling gettimeofday()?
I came up with the following solution (at last in the post) which starts the event with an accuracy of 1000usec. But I'm polling on the usec value for a short while, to get a timer started as soon the time reaches a nearby "whole" second:
Is there a nicer non blocking solution for this? As i continued reading about timers I found that the systems time keeping is done by the frc1 timer - is it possible to read out at which "count" of the timer the seconds get raised?
If someone coincidentally reads my code through and notices any other newbie programming mistakes or just anything what should be done otherwise, any information is appreciated.
Terminal Output:
Thank you!
Is SNTP_GET_SYSTEM_TIME under the surface the same as calling gettimeofday()?
Code: Select all
#define SNTP_GET_SYSTEM_TIME(sec, us) \
do { \
struct timeval tv = { .tv_sec = 0, .tv_usec = 0 }; \
gettimeofday(&tv, NULL); \
(sec) = tv.tv_sec; \
(us) = tv.tv_usec; \
} while (0);
Code: Select all
while (temp.tv_usec/10 < 99999)
{
gettimeofday(&temp, NULL);
}
If someone coincidentally reads my code through and notices any other newbie programming mistakes or just anything what should be done otherwise, any information is appreciated.
Code: Select all
#include <string.h>
#include <time.h>
#include <sys/time.h>
#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 "esp_attr.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "apps/sntp/sntp.h"
#include <sys/types.h> //contains struct timeval;
#include "apps/sntp/sntp_opts.h"
#define EXAMPLE_WIFI_SSID "mySSID" //CONFIG_WIFI_SSID
#define EXAMPLE_WIFI_PASS "myPW"//CONFIG_WIFI_PASSWORD
static EventGroupHandle_t wifi_event_group;
const int CONNECTED_BIT = BIT0;
static const char *TAG = "#Debug:";
time_t now;
struct tm timeinfo;
TaskHandle_t timer_start_task_handle;
TimerHandle_t tmr1; //Timer to Start the Event (Not Reloading)
TimerHandle_t tmr2; //Timer to Do Something every second, after the event started
int id1 = 1;
int id2 = 2;
int interval1 = 5000;
int interval2 = 1000;
char strftime_buf[64];
static void obtain_time(void);
static void initialize_sntp(void);
static void initialize_wifi(void);
static esp_err_t event_handler(void *ctx, system_event_t *event);
static void timer_start_task(void *pvParameter);
static void timerOneCallBack( TimerHandle_t xTimer );
static void timerTwoCallBack( TimerHandle_t xTimer );
void app_main()
{
time(&now);
localtime_r(&now, &timeinfo);
tmr1 = xTimerCreate("StartTimer", pdMS_TO_TICKS(interval1), pdFALSE, ( void * )id1, &timerOneCallBack);
tmr2 = xTimerCreate("SecTimer", pdMS_TO_TICKS(interval2), pdTRUE, ( void * )id2, &timerTwoCallBack);
// 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();
// update 'now' variable with current time
time(&now);
}
// Set Timezone to GMT+1
setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1);
tzset();
localtime_r(&now, &timeinfo);
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
ESP_LOGI(TAG, "The current date/time in Budapest is: %s", strftime_buf);
xTaskCreate(&timer_start_task, "time_printf_task", 2048, NULL, 5, &timer_start_task_handle);
}
static void timerOneCallBack( TimerHandle_t xTimer )
{
xTimerStart(tmr2, 0);
struct timeval tmp_time = {0};
SNTP_GET_SYSTEM_TIME(tmp_time.tv_sec, tmp_time.tv_usec); //Is it the same as using gettimeofaday(&tmp_time);
ESP_LOGI(TAG,"###Ring Ring! Timer1 Callback activated.###");
ESP_LOGI(TAG, "sec holds: %d", (int)tmp_time.tv_sec);
ESP_LOGI(TAG, "usec holds: %d", (int)tmp_time.tv_usec);
time(&now);
localtime_r(&now, &timeinfo);
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
ESP_LOGI(TAG, "The current date/time in Budapest / Vienna / Brüssel is: %s", strftime_buf);
}
static void timerTwoCallBack( TimerHandle_t xTimer )
{
ESP_LOGI(TAG,"###Ring Ring! Timer2 Callback activated.###");
}
static void obtain_time(void)
{
ESP_ERROR_CHECK( nvs_flash_init() );
initialize_wifi();
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(1000 / portTICK_PERIOD_MS);
time(&now);
localtime_r(&now, &timeinfo);
}
ESP_ERROR_CHECK( esp_wifi_stop() ); //Remove if MQTT message is implemented
}
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 initialize_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 timer_start_task(void *pvParameter)
{
while(1)
{
// The Start time of the logging event - Later it will be received via MQTT
struct tm start_time = {
.tm_year = 118,
.tm_mon = 2, //0 for January?
.tm_mday = 17,
.tm_hour = 10,
.tm_min = 29,
.tm_sec = 0,
.tm_isdst = 1,
};
mktime(&start_time);
strftime(strftime_buf, sizeof(strftime_buf), "%c", &start_time);
ESP_LOGI(TAG, "The saved start_time is: %s", strftime_buf);
time(&now);
localtime_r(&now, &timeinfo);
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
ESP_LOGI(TAG, "The current date/time is: %s", strftime_buf);
vTaskDelay(250 / portTICK_PERIOD_MS);
time_t raw_start_time;
time_t raw_now; // additional variable is not really needed
raw_start_time = mktime(&start_time);
time(&raw_now);
int difi;
signed int tmp_difi = difftime(raw_now, raw_start_time);
if (tmp_difi > 0) {
ESP_LOGI(TAG, "Start Time was in the past");
break;
}
else
{
difi = abs(tmp_difi);
}
ESP_LOGI(TAG, "Difftime in Secs until event start is: %d", difi);
struct timeval temp;
SNTP_GET_SYSTEM_TIME(temp.tv_sec, temp.tv_usec);
if ((int)temp.tv_usec/10 > 99999)
{
xTimerChangePeriod(tmr1, difi*100, 0);
if( xTimerStart(tmr1, 0 ) != pdPASS )
{
ESP_LOGI(TAG, "Timer start error");
}
else {
ESP_LOGI(TAG, "Timer started");
}
}
else {
while (temp.tv_usec/10 < 99999)
{
gettimeofday(&temp, NULL);
}
xTimerChangePeriod(tmr1, (difi-1)*100, 0);
if( xTimerStart(tmr1, 0 ) != pdPASS )
{
ESP_LOGI(TAG, "Timer start error");
}
else {
ESP_LOGI(TAG, "Timer started");
}
}
break;
}
vTaskDelete(0);
}
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 ESP32 WiFi libs don't currently
auto-reassociate. */
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
break;
default:
break;
}
return ESP_OK;
}
Code: Select all
I (3552) #Debug:: The current date/time in Budapest is: Sat Mar 17 09:28:54 2018
I (3552) #Debug:: The saved start_time is: Sat Mar 17 09:29:00 2018
I (3562) #Debug:: The current date/time is: Sat Mar 17 09:28:54 2018
I (3822) #Debug:: Difftime in Secs until event start is: 6
I (4052) #Debug:: Timer started
I (9052) #Debug:: ###Ring Ring! Timer1 Callback activated.###
I (9052) #Debug:: sec holds: 1521275339
I (9052) #Debug:: usec holds: 997817
I (9052) #Debug:: The current date/time in Budapest / Vienna / Brüssel is: Sat Mar 17 09:29:00 2018
I (10052) #Debug:: ###Ring Ring! Timer2 Callback activated.###
I (11052) #Debug:: ###Ring Ring! Timer2 Callback activated.###
Re: Trigger a Function every "exact" second after syncing with NTP Server.
Hello everyone,
I’m still looking for a better approach on how to get an action happen every second the sntp synced internal clock ticks.. I would really like to know how someone with experience in programming would try to solve this. I read everything what could have something to do with this..
SNTP_SET_SYSTEM_TIME_US(t, us)[/code] uses settimeofaday() which leads to set_boot_time() in time.c.
I see the defines, and I realize that it works differently if RTC or FRC is used.
For RTC: (This is the setting which is also available from menuconfig?)
RTC_SLOW_CLK_CAL_REG
RTC_CLK_CAL_FRACT
RTC_BOOT_TIME_LOW_REG
RTC_BOOT_TIME_HIGH_REG
which hide the name of RTC_CNTL_STORE 1 to 4 registers.
(What I understand it is possible that WITH_FRC and WITH_RTC are also set, for deep sleep and non deep sleep timekeeping? In this case is esp_set_time_from_rtc used for waking up from deep sleep?)
But I cant figure out where the time is set for FRC.
s_boot_time holds the time epoche. But how is this incremented and passed to the FRC timer? How could I trigger an interrupt / callback every time it increments a sec?
I found the frc_timer_reg.h but I do not see how this connects with time.c or sntp. It also mentions “legacy timers” - is it old and shouldnt be used? The readme of the example says FRC1 timer should be used when the ESP32 is running. (That would be my use case)
My project is not using sleep, the goal is to start several esp32s, sync them with an sntp server running on a raspberry and publish to them a start time over mqtt and they should start sending measurement data every second after the start time passed.. I have a solution already, but I feel that this is far from good solution.
Edited Later:
I just found this from ESP_Sprite int the "new chip" topic
I’m still looking for a better approach on how to get an action happen every second the sntp synced internal clock ticks.. I would really like to know how someone with experience in programming would try to solve this. I read everything what could have something to do with this..
SNTP_SET_SYSTEM_TIME_US(t, us)[/code] uses settimeofaday() which leads to set_boot_time() in time.c.
I see the defines, and I realize that it works differently if RTC or FRC is used.
For RTC: (This is the setting which is also available from menuconfig?)
RTC_SLOW_CLK_CAL_REG
RTC_CLK_CAL_FRACT
RTC_BOOT_TIME_LOW_REG
RTC_BOOT_TIME_HIGH_REG
which hide the name of RTC_CNTL_STORE 1 to 4 registers.
(What I understand it is possible that WITH_FRC and WITH_RTC are also set, for deep sleep and non deep sleep timekeeping? In this case is esp_set_time_from_rtc used for waking up from deep sleep?)
But I cant figure out where the time is set for FRC.
s_boot_time holds the time epoche. But how is this incremented and passed to the FRC timer? How could I trigger an interrupt / callback every time it increments a sec?
I found the frc_timer_reg.h but I do not see how this connects with time.c or sntp. It also mentions “legacy timers” - is it old and shouldnt be used? The readme of the example says FRC1 timer should be used when the ESP32 is running. (That would be my use case)
My project is not using sleep, the goal is to start several esp32s, sync them with an sntp server running on a raspberry and publish to them a start time over mqtt and they should start sending measurement data every second after the start time passed.. I have a solution already, but I feel that this is far from good solution.
Edited Later:
I just found this from ESP_Sprite int the "new chip" topic
now I belive that I overcomplicated this a bit... I also found for the necessary "busy wait" an other idea I can try from Kolban :You can set the RTC to a certain date using (S)NTP or another method, then use mktime() to get the Unix timestamp (aka the amount of seconds since midnight 1-1-1970) of whatever date/time you want to have an alarm on, get the current Unix timestamp by calling time() and subtracting the two to get the amount of seconds to wait. Then wait that amount, using either something like vTaskDelay or deep sleep, and you're there.
In ESP32 assembler, there is a special register called CCOUNT that increments (internally and in the hardware) every processor clock cycle.
-
- Posts: 151
- Joined: Thu Jun 15, 2017 4:54 am
- Location: New Zealand
Re: Trigger an action every "exact" second after syncing with NTP Server
You can also use vTaskDelayUntil() in ESP-IDF v3.0rc for a slightly more accurate delay, if you need to take into consideration some processing between reading the current time and the task actually going to sleep, although note that your task is only going to start running on a scheduler tick boundary, which is 10 milliseconds by default.
Re: Trigger an action every "exact" second after syncing with NTP Server
Take a look at the following example: https://github.com/K0I05/esp32-s3/tree/ ... datalogger
The task_schedule.h component is a user-defined task delay that synchronizes with the system clock and accounts for the time it takes to run the task (~10 ms resolution). In the example, the component is configured to run the task once every 10-seconds at 0-seconds into the interval (i.e. 12:00:00, 12:00:10, 12:00:20, etc.).
The task_schedule.h component is a user-defined task delay that synchronizes with the system clock and accounts for the time it takes to run the task (~10 ms resolution). In the example, the component is configured to run the task once every 10-seconds at 0-seconds into the interval (i.e. 12:00:00, 12:00:10, 12:00:20, etc.).
Who is online
Users browsing this forum: No registered users and 83 guests