SNTP for esp-idf

* report syncronization events
 * force restart
 * server list runtime update
 * server list setup checks
 * real update interval reported
 * update interval runtime change
This commit is contained in:
Anton Sergunov 2024-06-07 05:28:33 +00:00
parent 2635c31d7e
commit 12e5d6e7a9
2 changed files with 96 additions and 42 deletions

View file

@ -14,15 +14,17 @@
#include "lwip/apps/sntp.h"
#endif
// Yes, the server names are leaked, but that's fine.
#ifdef CLANG_TIDY
#define strdup(x) (const_cast<char *>(x))
#endif
namespace esphome {
namespace sntp {
static const char *const TAG = "sntp";
static const std::string FORCE_UPDATE_SCHEDULE = "force_update_schedule";
const char *server_name_buffer(const auto &server) { return this->server.empty() ? nullptr : this->server.c_str(); }
#ifdef USE_ESP_IDF
static time_t sync_time_to_report_ = 0;
#else
void SNTPComponent::setup() {
#ifndef USE_HOST
@ -37,25 +39,24 @@ void SNTPComponent::setup() {
sntp_stop();
#endif
sntp_setservername(0, strdup(this->server_1_.c_str()));
if (!this->server_2_.empty()) {
sntp_setservername(1, strdup(this->server_2_.c_str()));
}
if (!this->server_3_.empty()) {
sntp_setservername(2, strdup(this->server_3_.c_str()));
setup_servers_();
this->servers_was_setup_ = true;
for(uint8_t i = 0; i<3; ++i) {
const auto& buff = server_name_buffer(server_[i]);
if(buff != nullptr && buff != sntp_getservername()) {
ESP_LOGCONFIG(TAG, "Can't set server %d", i+1);
}
}
#ifdef USE_ESP_IDF
if (sntp_get_sync_interval() != this->get_update_interval()) {
sntp_set_sync_interval(this->get_update_interval());
sntp_restart();
}
// Stop pooler but ler the user update by the hands
this->stop_poller();
#endif
sntp_set_time_sync_notification_cb([](struct timeval *tv){
sync_time_to_report_ = tv->tv_sec;
});
#endif // USE_ESP_IDF
sntp_init();
#endif
#endif // USE_HOST
}
void SNTPComponent::dump_config() {
ESP_LOGCONFIG(TAG, "SNTP Time:");
@ -64,29 +65,52 @@ void SNTPComponent::dump_config() {
ESP_LOGCONFIG(TAG, " Server 3: '%s'", this->server_3_.c_str());
ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str());
}
void set_servers(const std::string &server_1, const std::string &server_2, const std::string &server_3);
{
#if !defined(USE_HOST)
if(servers_was_setup_) {
for(uint8_t i = 0; i<3; ++i) {
const auto& buff = this->servers_[i].empty()
? nullptr
: this->servers_[i].c_str();
if(buff != nullptr && buff == sntp_getservername(i))
sntp_setservername(i, nullptr);
max_setup_server = max(max_setup_server, i);
}
}
#endif
this->servers_ = {server_1, server_2, server_3};
#if !defined(USE_HOST)
if(this->servers_was_setup_) {
setup_servers_();
}
#endif
}
void SNTPComponent::update() {
#if !defined(USE_HOST)
#if defined(USE_ESP_IDF)
if (sntp_enabled()) {
ESP_LOGD(TAG, "Restart SNTP");
this->has_time_ = false;
if (!sntp_restart()) {
ESP_LOGD(TAG, "Can't restart SNTP");
}
} else {
ESP_LOGD(TAG, "SNTP is not enabled");
}
#else
// force resync
if (sntp_enabled()) {
#if defined(USE_ESP_IDF)
sntp_restart();
#else
sntp_stop();
this->has_time_ = false;
sntp_init();
}
#endif
}
#endif
}
void SNTPComponent::loop() {
#ifdef USE_ESP_IDF
if (sync_time_to_report_ != 0) {
this->cancel_timeout(FORCE_UPDATE_SCHEDULE);
time_t time_to_report = 0;
swap(sync_time_to_report_, time_to_report);
const ESPTime time = ESPTime::from_epoch_local(time_to_report);
ESP_LOGD(TAG, "Synchronized time: %04d-%02d-%02d %02d:%02d:%02d", time.year, time.month, time.day_of_month, time.hour,
time.minute, time.second);
}
#else
if (this->has_time_)
return;
@ -96,11 +120,37 @@ void SNTPComponent::loop() {
ESP_LOGD(TAG, "Synchronized time: %04d-%02d-%02d %02d:%02d:%02d", time.year, time.month, time.day_of_month, time.hour,
time.minute, time.second);
#endif
this->time_sync_callback_.call();
this->has_time_ = true;
}
#ifdef USE_ESP_IDF
void SNTPComponent::set_update_interval(uint32_t update_interval) {
time::RealTimeClock::set_update_interval(update_interval);
const auto previous_sync_interval = sntp_get_sync_interval();
bool SNTPComponent::is_in_progress() const { return sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET; }
sntp_set_sync_interval(update_interval);
const auto new_sync_interval = sntp_get_sync_interval();
if(previous_sync_interval > new_sync_interval) {
this->set_timeout(FORCE_UPDATE_SCHEDULE, new_sync_interval, []{
sntp_restart();
});
}
}
uint32_t SNTPComponent::get_update_interval() const {
return sntp_get_sync_interval();
}
#endif
void SNTPComponent::setup_servers_() {
for(uint8_t i = 0; i<3; ++i) {
const auto& buff = server_name_buffer(this->servers_[i]);
sntp_setservername(i, buff);
if(buff != nullptr && buff != sntp_getservername()) {
ESP_LOGE(TAG, "Can't set server %d", i+1);
}
}
}
} // namespace sntp
} // namespace esphome

View file

@ -17,23 +17,27 @@ class SNTPComponent : public time::RealTimeClock {
void setup() override;
void dump_config() override;
/// Change the servers used by SNTP for timekeeping
void set_servers(const std::string &server_1, const std::string &server_2, const std::string &server_3) {
this->server_1_ = server_1;
this->server_2_ = server_2;
this->server_3_ = server_3;
}
void set_servers(const std::string &server_1, const std::string &server_2, const std::string &server_3);
float get_setup_priority() const override { return setup_priority::BEFORE_CONNECTION; }
void update() override;
void loop() override;
bool is_in_progress() const;
#ifdef USE_ESP_IDF
void set_update_interval(uint32_t update_interval) override;
uint32_t get_update_interval() const override;
#endif
protected:
void setup_servers_();
private:
// Private because buffer address should stay unchanged
std::string servers_[3];
protected:
std::string server_1_;
std::string server_2_;
std::string server_3_;
bool has_time_{false};
#endif
bool servers_was_setup_{false};
};
} // namespace sntp