Skip to content
This repository was archived by the owner on Sep 27, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/esphome/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,10 @@ HomeAssistantTime *Application::make_homeassistant_time_component() {
}
#endif

#ifdef USE_GPS_COMPONENT
GPSComponent *Application::make_gps_component(UARTComponent *parent) { return this->register_component(new GPSComponent(parent)); }
#endif

#ifdef USE_HLW8012
sensor::HLW8012Component *Application::make_hlw8012(const GPIOOutputPin &sel_pin, uint8_t cf_pin, uint8_t cf1_pin,
uint32_t update_interval) {
Expand Down
5 changes: 5 additions & 0 deletions src/esphome/application.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@
#include "esphome/time/rtc_component.h"
#include "esphome/time/sntp_component.h"
#include "esphome/time/homeassistant_time.h"
#include "esphome/time/gps_component.h"

ESPHOME_NAMESPACE_BEGIN

Expand Down Expand Up @@ -769,6 +770,10 @@ class Application {
time::HomeAssistantTime *make_homeassistant_time_component();
#endif

#ifdef USE_GPS_COMPONENT
time::GPSComponent *make_gps_component(UARTComponent *parent);
#endif

#ifdef USE_HLW8012
sensor::HLW8012Component *make_hlw8012(const GPIOOutputPin &sel_pin, uint8_t cf_pin, uint8_t cf1_pin,
uint32_t update_interval = 60000);
Expand Down
98 changes: 98 additions & 0 deletions src/esphome/time/gps_component.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#include "esphome/defines.h"

#ifdef USE_GPS_COMPONENT

#include "esphome/log.h"
#include "esphome/helpers.h"
#include "esphome/component.h"
#include "esphome/time/gps_component.h"

ESPHOME_NAMESPACE_BEGIN

namespace time {

#define SECONDS_FROM_1970_TO_2000 946684800

static const char *TAG = "time.gps";

// utility code, some of this could be exposed i

const uint8_t daysInMonth [] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 };

// number of days since 2000/01/01, valid for 2001..2099
static uint16_t date2days(uint16_t y, uint8_t m, uint8_t d) {
if (y >= 2000)
y -= 2000;
uint16_t days = d;
for (uint8_t i = 1; i < m; ++i)
days += pgm_read_byte(daysInMonth + i - 1);
if (m > 2 && y % 4 == 0)
++days;
return days + 365 * y + (y + 3) / 4 - 1;
}

static long time2long(uint16_t days, uint8_t h, uint8_t m, uint8_t s) {
return ((days * 24L + h) * 60 + m) * 60 + s;
}

uint32_t to_unixtime(uint16_t yOff, uint8_t m, uint8_t d, uint8_t hh, uint8_t mm, uint8_t ss) {
uint32_t t;
uint16_t days = date2days(yOff, m, d);
t = time2long(days, hh, mm, ss);
t += SECONDS_FROM_1970_TO_2000; // seconds from 1970 to 2000
return t;
}

GPSComponent::GPSComponent(UARTComponent *parent)
: RealTimeClockComponent(), UARTDevice(parent) {}

void GPSComponent::get_time_via_uart_(){
while (this->available() && !this->has_time_)
if (gps.encode(this->read()))
if (gps.time.isValid())
{
auto utc_t_ = to_unixtime(gps.date.year(), gps.date.month(), gps.date.day(), gps.time.hour(), gps.time.minute(), gps.time.second());
struct timeval timev {
.tv_sec = static_cast<time_t>(utc_t_), .tv_usec = 0,
};
timezone tz = {0, 0};
settimeofday(&timev, &tz);
this->print_datetime();
this->has_time_ = true;
}
}

void GPSComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up GPS...");
unsigned long start = millis();
do
{
this->get_time_via_uart_();
} while (millis() - start < 1000);
}

void GPSComponent::print_datetime(){
auto time = this->now();
char buf[128];
time.strftime(buf, sizeof(buf), "%c");
ESP_LOGD(TAG, "Synchronized time: %s", buf);
}

void GPSComponent::dump_config() {
ESP_LOGCONFIG(TAG, "GPS Time:");
ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str());
this->print_datetime();
}

float GPSComponent::get_setup_priority() const { return setup_priority::HARDWARE_LATE; }

void GPSComponent::loop() {
if (this->has_time_)
return;
this->get_time_via_uart_();
}
} // namespace time

ESPHOME_NAMESPACE_END

#endif // USE_GPS_COMPONENT
46 changes: 46 additions & 0 deletions src/esphome/time/gps_component.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#ifndef ESPHOME_GPS_COMPONENT_H
#define ESPHOME_GPS_COMPONENT_H

#include "esphome/defines.h"
#include <TinyGPS++.h>
#include "sys/time.h"

#ifdef USE_GPS_COMPONENT

#include "esphome/component.h"
#include "esphome/time/rtc_component.h"
#include "esphome/uart_component.h"

ESPHOME_NAMESPACE_BEGIN

namespace time {

/// The GPS component allows you to configure local timekeeping via Simple Network Time Protocol.
///
/// \note
/// The C library (newlib) available on ESPs only supports TZ strings that specify an offset and DST info;
/// you cannot specify zone names or paths to zoneinfo files.
/// \see https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
class GPSComponent : public RealTimeClockComponent, public UARTDevice {

public:
GPSComponent(UARTComponent *parent);
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void loop() override;

protected:
bool has_time_{false};
TinyGPSPlus gps;
void get_time_via_uart_();
void print_datetime();
};

} // namespace time

ESPHOME_NAMESPACE_END

#endif // USE_GPS_COMPONENT

#endif // ESPHOME_GPS_COMPONENT_H