437 lines
12 KiB
C
437 lines
12 KiB
C
/*
|
|
* rtc.c
|
|
*
|
|
* Created on: Jan 16, 2023
|
|
* Author: Sword
|
|
*/
|
|
#include "rtc.h"
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#include "esp_sntp.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "lwip/sockets.h"
|
|
#include "lwip/err.h"
|
|
#include "lwip/netdb.h"
|
|
#include "lwip/icmp.h"
|
|
#include "esp_system.h"
|
|
#include "esp_log.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/time.h>
|
|
#include "nvm.h"
|
|
#include "nvs_flash.h"
|
|
|
|
|
|
|
|
/** Date and time set to: 1st of December 2014, 23 hour 59 min and 31 seconds */
|
|
#define SET_HOUR 13 /*< Set hour to the RTC */
|
|
#define SET_MINUTE 0 /*< Set minute to the RTC */
|
|
#define SET_SECOND 0 /*< Set second to the RTC */
|
|
#define SET_WEEKDAY 3 /*< Set weekday to the RTC */
|
|
#define SET_YEARDAY 16 /*< day in the year, range 0 to 365 */
|
|
#define SET_DAY 26 /*< Set day to the RTC */
|
|
#define SET_MONTH 2 /*< Set month to the RTC */
|
|
#define SET_YEAR 2025 /*< Set year to the RTC */
|
|
|
|
#define RTC_SUBSECOND_COUNTER_LOAD_VALUE 32767
|
|
|
|
static const char* TAG = "RTC";
|
|
|
|
#define LOG_LOCAL_LEVEL ESP_LOG_INFO
|
|
#include "esp_log.h"
|
|
|
|
#include "esp_sntp.h"
|
|
|
|
static bool rtc_set = false;
|
|
|
|
#define PING_HOST "8.8.8.8" // Google DNS
|
|
#define CHECK_INTERVAL 1 //60000 // Check every 60 seconds
|
|
#define MAX_DISCONNECT_TIME 5 //86400 // 24 hours in seconds
|
|
#define SEC 3600
|
|
static time_t last_successful_ping = 0;
|
|
|
|
|
|
/// Internet monitor //
|
|
|
|
|
|
time_t get_sntp_time()
|
|
{
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
return tv.tv_sec; // Return UNIX timestamp
|
|
}
|
|
|
|
|
|
bool ping_test()
|
|
{
|
|
struct sockaddr_in target_addr;
|
|
target_addr.sin_addr.s_addr = inet_addr(PING_HOST);
|
|
target_addr.sin_family = AF_INET;
|
|
target_addr.sin_port = htons(0);
|
|
|
|
int sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
|
if (sock < 0)
|
|
{
|
|
ESP_LOGE(TAG, "Socket creation failed");
|
|
return false;
|
|
}
|
|
|
|
struct icmp_echo_hdr echo_req;
|
|
memset(&echo_req, 0, sizeof(echo_req));
|
|
echo_req.id = htons(1);
|
|
echo_req.seqno = htons(1);
|
|
echo_req.type = ICMP_ECHO;
|
|
|
|
if (sendto(sock, &echo_req, sizeof(echo_req), 0, (struct sockaddr *)&target_addr, sizeof(target_addr)) < 0)
|
|
{
|
|
ESP_LOGE(TAG, "Ping send failed");
|
|
close(sock);
|
|
return false;
|
|
}
|
|
|
|
fd_set read_fds;
|
|
struct timeval timeout = {1, 0}; // 1 second timeout
|
|
FD_ZERO(&read_fds);
|
|
FD_SET(sock, &read_fds);
|
|
|
|
if (select(sock + 1, &read_fds, NULL, NULL, &timeout) > 0)
|
|
{
|
|
ESP_LOGI(TAG, "Internet is available");
|
|
close(sock);
|
|
return true;
|
|
}
|
|
|
|
ESP_LOGW(TAG, "Ping timeout, no internet");
|
|
close(sock);
|
|
return false;
|
|
}
|
|
|
|
|
|
void internet_monitor_task(void *pvParameter)
|
|
{
|
|
while (1)
|
|
{
|
|
time_t current_time = get_sntp_time();
|
|
|
|
if (ping_test())
|
|
{
|
|
last_successful_ping = current_time;
|
|
}
|
|
|
|
if (last_successful_ping > 0 && (current_time - last_successful_ping) > MAX_DISCONNECT_TIME)
|
|
{
|
|
ESP_LOGE(TAG, "No internet for %d hours, restarting...", (MAX_DISCONNECT_TIME/SEC));
|
|
/* erase the NVS partition */
|
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
|
esp_restart();
|
|
}
|
|
|
|
vTaskDelay(pdMS_TO_TICKS(CHECK_INTERVAL)); // Wait before next check
|
|
}
|
|
}
|
|
|
|
|
|
// END ///
|
|
|
|
int app_rtc_init(void)
|
|
{
|
|
int retval = RTC_STATUS_OK;
|
|
|
|
//RTC_InitType RTC_Init_struct;
|
|
//RTC_StructInit(&RTC_Init_struct);
|
|
|
|
//SysCtrl_PeripheralClockCmd(CLOCK_PERIPH_RTC, ENABLE);
|
|
/* Try to enable the down-counter to use as a millisecond counter, but
|
|
* does not seem to work without this __NOP delay.
|
|
*/
|
|
//RTC_Init_struct.RTC_TLR1 = RTC_SUBSECOND_COUNTER_LOAD_VALUE;
|
|
//RTC_Init(&RTC_Init_struct);
|
|
|
|
/** Delay between two write in RTC->TCR register has to be
|
|
* at least 3 x 32k cycle + 2 CPU cycle. For that reason it
|
|
* is neccessary to add the delay.
|
|
*/
|
|
// for (volatile uint32_t i = 0; i < 600; i++) {
|
|
// __asm("NOP");
|
|
// }
|
|
|
|
//RTC_DateTimeType RTC_DateTime;
|
|
|
|
struct tm rtc_time;
|
|
|
|
/* Set the present time and date */
|
|
/*RTC_DateTime.Second = SET_SECOND;
|
|
RTC_DateTime.Minute = SET_MINUTE;
|
|
RTC_DateTime.Hour = SET_HOUR;
|
|
RTC_DateTime.WeekDay = SET_WEEKDAY;
|
|
RTC_DateTime.MonthDay = SET_DAY;
|
|
RTC_DateTime.Month = SET_MONTH;
|
|
RTC_DateTime.Year = SET_YEAR;
|
|
RTC_SetTimeDate(&RTC_DateTime);*/
|
|
|
|
rtc_time.tm_sec = SET_SECOND;
|
|
rtc_time.tm_min = SET_MINUTE;
|
|
rtc_time.tm_hour = SET_HOUR;
|
|
rtc_time.tm_wday = SET_WEEKDAY;
|
|
rtc_time.tm_mday = SET_DAY;
|
|
rtc_time.tm_mon = SET_MONTH;
|
|
rtc_time.tm_year = SET_YEAR;
|
|
rtc_time.tm_yday = SET_YEARDAY;
|
|
|
|
time_t t = mktime(&rtc_time);
|
|
struct timeval now = { .tv_sec = t };
|
|
|
|
settimeofday(&now, NULL);
|
|
|
|
/* Enable RTC */
|
|
// RTC_Cmd(ENABLE);
|
|
/* Enable RTC clockwatch */
|
|
// RTC_ClockwatchCmd(ENABLE);
|
|
|
|
rtc_set = false;
|
|
|
|
return retval;
|
|
}
|
|
|
|
void rtc_get_timestamp(rtc_timestamp_t* ts)
|
|
{
|
|
if (ts) {
|
|
//RTC_DateTimeType RTC_DateTime;
|
|
//RTC_GetTimeDate(&RTC_DateTime);
|
|
/* RTC subsecond counter is a count-down counter */
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
|
|
struct tm *info = localtime( &tv.tv_sec );
|
|
|
|
ts->Millisecond = 7+1000*(info->tm_sec); //((RTC_SUBSECOND_COUNTER_LOAD_VALUE - RTC_GetTimerValue()) * 1000 + RTC_SUBSECOND_COUNTER_LOAD_VALUE) /
|
|
ts->Second = info->tm_sec;
|
|
ts->Minute = info->tm_min;
|
|
ts->Hour = info->tm_hour;
|
|
ts->WeekDay = info->tm_wday;
|
|
ts->MonthDay = info->tm_mday;
|
|
ts->Month = info->tm_mon;
|
|
ts->Year = info->tm_year;
|
|
}
|
|
}
|
|
|
|
void rtc_set_timestamp(rtc_timestamp_t* ts)
|
|
{
|
|
if (ts) {
|
|
|
|
rtc_set_timestamp_tz(ts, 0);
|
|
}
|
|
}
|
|
|
|
/* Set timestamp including a timezone offset in seconds.
|
|
* The timezone offset may be positive or negative. It is applied
|
|
* by subtraction, so a -ve offset (e.g. UTC-5) results in the
|
|
* timezone offset being added to the local timestamp
|
|
*/
|
|
void rtc_set_timestamp_tz(rtc_timestamp_t* ts, int32_t tz_offset_sec)
|
|
{
|
|
if (ts) {
|
|
|
|
struct tm buf;
|
|
buf.tm_year = ts->Year - 1900;
|
|
buf.tm_mon = ts->Month - 1;
|
|
buf.tm_mday = ts->MonthDay;
|
|
buf.tm_hour = ts->Hour;
|
|
buf.tm_min = ts->Minute;
|
|
buf.tm_sec = ts->Second;
|
|
time_t t = mktime(&buf);
|
|
|
|
/* Apply offset */
|
|
t -= tz_offset_sec;
|
|
/*
|
|
if (!rtc_is_set()) {
|
|
rtc_set_epoch((uint32_t)t);
|
|
}
|
|
*/
|
|
rtc_set_epoch((uint32_t)t);
|
|
}
|
|
}
|
|
|
|
void rtc_set_epoch(uint32_t epoch)
|
|
{
|
|
/*
|
|
* struct tm
|
|
Member Type Meaning Range
|
|
tm_sec int seconds after the minute 0 - 60 *
|
|
tm_min int minutes after the hour 0 - 59
|
|
tm_hour int hours since midnight 0 - 23
|
|
tm_mday int day of the month 1 - 31
|
|
tm_mon int months since January 0 - 11
|
|
tm_year int years since 1900
|
|
tm_wday int days since Sunday 0 - 6
|
|
tm_yday int days since January 1 0 - 365
|
|
tm_isdst int Daylight Saving Time flag
|
|
*/
|
|
|
|
struct tm* buf;
|
|
time_t t = epoch;
|
|
|
|
buf = localtime(&t);
|
|
|
|
ESP_LOGD(TAG,"rtc_set_epoch: %s\n", asctime(buf));
|
|
|
|
//RTC_DateTimeType RTC_DateTime;
|
|
//
|
|
///* Set the present time and date */
|
|
//RTC_DateTime.Second = buf->tm_sec;
|
|
//RTC_DateTime.Minute = buf->tm_min;
|
|
//RTC_DateTime.Hour = buf->tm_hour;
|
|
//RTC_DateTime.WeekDay = buf->tm_wday;
|
|
//RTC_DateTime.MonthDay = buf->tm_mday;
|
|
//RTC_DateTime.Month = buf->tm_mon + 1;
|
|
//RTC_DateTime.Year = buf->tm_year + 1900;
|
|
//RTC_SetTimeDate(&RTC_DateTime);
|
|
|
|
time_t tmi = mktime(buf);
|
|
struct timeval now = { .tv_sec = tmi };
|
|
|
|
settimeofday(&now, NULL);
|
|
|
|
rtc_set = true;
|
|
}
|
|
|
|
uint32_t rtc_get_epoch(void)
|
|
{
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
|
|
struct tm *info = localtime( &tv.tv_sec );
|
|
|
|
struct tm buf;
|
|
buf.tm_year = info->tm_year - 1900;
|
|
buf.tm_mon = info->tm_mon - 1;
|
|
buf.tm_mday = info->tm_mday;
|
|
buf.tm_hour = info->tm_hour;
|
|
buf.tm_min = info->tm_min;
|
|
buf.tm_sec = info->tm_sec;
|
|
buf.tm_isdst = 0;
|
|
|
|
time_t t = mktime(&buf);
|
|
|
|
ESP_LOGD(TAG,"rtc_get_epoch: %s\n", ctime(&t));
|
|
|
|
return (uint32_t)t;
|
|
}
|
|
|
|
bool rtc_is_set(void)
|
|
{
|
|
return rtc_set;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////
|
|
/*
|
|
void time_sync_notification_cb(struct timeval_1 *tv) {
|
|
ESP_LOGI(TAG, "Time synchronization event received");
|
|
}
|
|
*/
|
|
|
|
|
|
void obtain_time(void) {
|
|
ESP_LOGI(TAG, "Initializing SNTP");
|
|
|
|
const char *ntp_servers[] = {
|
|
"pool.ntp.org",
|
|
"time.google.com",
|
|
"1.pool.ntp.org"
|
|
};
|
|
int server_index = 0;
|
|
int num_servers = sizeof(ntp_servers) / sizeof(ntp_servers[0]);
|
|
|
|
time_t now = 0;
|
|
struct tm timeinfo = { 0 };
|
|
|
|
while (server_index < num_servers) {
|
|
ESP_LOGI(TAG, "Using NTP server: %s", ntp_servers[server_index]);
|
|
|
|
esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
|
esp_sntp_setservername(0, ntp_servers[server_index]);
|
|
esp_sntp_init();
|
|
|
|
int retry = 0;
|
|
const int max_retries = 10;
|
|
|
|
while (timeinfo.tm_year < (2020 - 1900) && retry < max_retries) {
|
|
ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry + 1, max_retries);
|
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
|
time(&now);
|
|
localtime_r(&now, &timeinfo);
|
|
retry++;
|
|
}
|
|
|
|
if (timeinfo.tm_year >= (2020 - 1900)) {
|
|
ESP_LOGI(TAG, "Time synchronized: %s", asctime(&timeinfo));
|
|
|
|
// create internet monitor task
|
|
xTaskCreate(&internet_monitor_task, "internet_monitor_task", 4096, NULL, 5, NULL);
|
|
return; // Exit the function since time is successfully obtained
|
|
}
|
|
|
|
ESP_LOGE(TAG, "Failed to sync time with %s, switching to next server...", ntp_servers[server_index]);
|
|
esp_sntp_stop(); // Stop SNTP before switching
|
|
server_index++; // Move to the next server
|
|
}
|
|
|
|
ESP_LOGE(TAG, "All NTP servers failed! Check network settings.");
|
|
}
|
|
|
|
/*void obtain_time(void) {
|
|
ESP_LOGI(TAG, "Initializing SNTP");
|
|
|
|
esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
|
esp_sntp_setservername(0, "pool.ntp.org"); // Set NTP server
|
|
// sntp_set_time_sync_notification_cb(time_sync_notification_cb);
|
|
esp_sntp_init();
|
|
|
|
// Wait for time to be set
|
|
time_t now = 0;
|
|
struct tm timeinfo = { 0 };
|
|
int retry = 0;
|
|
const int max_retries = 10;
|
|
|
|
while (timeinfo.tm_year < (2020 - 1900) && ++retry < max_retries) {
|
|
ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, max_retries);
|
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
|
time(&now);
|
|
localtime_r(&now, &timeinfo);
|
|
}
|
|
|
|
if (retry >= max_retries) {
|
|
ESP_LOGE(TAG, "Failed to obtain time");
|
|
} else {
|
|
ESP_LOGI(TAG, "Time synchronized");
|
|
}
|
|
}*/
|
|
|
|
void get_time(char *time_str, size_t size) { // Buffer is provided by the caller
|
|
time_t now;
|
|
struct tm timeinfo;
|
|
|
|
time(&now);
|
|
// ESP_LOGI(TAG, "Time print start %ld", now);
|
|
|
|
localtime_r(&now, &timeinfo);
|
|
|
|
const char *weekdays[] = {"0", "1", "2", "3",
|
|
"4", "5", "6"};
|
|
|
|
// Format time string
|
|
snprintf(time_str, size, "%04d/%02d/%02d_%02d:%02d:%02d_%s",
|
|
timeinfo.tm_year + 1900,
|
|
timeinfo.tm_mon + 1, timeinfo.tm_mday,
|
|
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec,
|
|
weekdays[timeinfo.tm_wday]);
|
|
|
|
ESP_LOGI(TAG, "Formatted Time: %s", time_str);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////
|