Skip to content

Commit 2e82fc3

Browse files
author
Samuel Sieb
committed
add GPS time component
1 parent 3c3e46b commit 2e82fc3

File tree

5 files changed

+176
-0
lines changed

5 files changed

+176
-0
lines changed

components/gpstime/README.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# GPS component that only gets the time
2+
3+
A configured uart is required.
4+
5+
Example:
6+
```yaml
7+
time:
8+
- platform: gpstime
9+
uart_id: my_uart # optional
10+
```
11+

components/gpstime/__init__.py

Whitespace-only changes.

components/gpstime/gpstime.cpp

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#include "gpstime.h"
2+
#include "esphome/core/log.h"
3+
4+
namespace esphome {
5+
namespace gpstime {
6+
7+
static const char *TAG = "GPS.time";
8+
9+
void GPSTime::loop() {
10+
while (this->available()) {
11+
uint8_t c;
12+
this->read_byte(&c);
13+
if (this->have_time_)
14+
continue;
15+
if (c == '$') {
16+
this->receiving_ = true;
17+
this->ending_ = false;
18+
this->rx_buffer_.clear();
19+
continue;
20+
}
21+
if (!this->receiving_)
22+
continue;
23+
if (c == '\r') {
24+
if (this->ending_) {
25+
ESP_LOGW(TAG, "unexpected return");
26+
this->receiving_ = false;
27+
} else {
28+
this->ending_ = true;
29+
}
30+
continue;
31+
}
32+
if (c == '\n') {
33+
this->receiving_ = false;
34+
if (!this->ending_) {
35+
ESP_LOGW(TAG, "unexpected newline");
36+
continue;
37+
}
38+
this->handle_message_();
39+
continue;
40+
}
41+
if (this->ending_) {
42+
ESP_LOGW(TAG, "unexpected character");
43+
this->receiving_ = false;
44+
continue;
45+
}
46+
if (this->rx_buffer_.size() < 90)
47+
this->rx_buffer_.push_back(c);
48+
}
49+
}
50+
51+
void GPSTime::handle_message_() {
52+
std::vector<std::string> terms;
53+
std::string data(this->rx_buffer_.begin(), this->rx_buffer_.end());
54+
ESP_LOGD(TAG, "received: %s", data.c_str());
55+
int start = 0;
56+
int end;
57+
int csum_pos = data.find('*');
58+
if (csum_pos != std::string::npos) {
59+
if (data.size() - csum_pos != 3) {
60+
ESP_LOGE(TAG, "invalid checksum data");
61+
return;
62+
}
63+
uint8_t csum = 0;
64+
parse_hex(data.c_str() + csum_pos + 1, &csum, 1);
65+
data.resize(csum_pos);
66+
uint8_t ccsum = 0;
67+
for (auto c : data)
68+
ccsum ^= c;
69+
if (csum != ccsum) {
70+
ESP_LOGE(TAG, "checksum failed: %02x != %02x", csum, ccsum);
71+
return;
72+
}
73+
}
74+
do {
75+
end = data.find(',', start);
76+
int len = end != std::string::npos ? end - start : end;
77+
terms.push_back(data.substr(start, len));
78+
start = end + 1;
79+
} while (end != std::string::npos);
80+
ESP_LOGD(TAG, "message: %s", terms[0].c_str());
81+
if (terms[0] != "GPRMC")
82+
return;
83+
ESP_LOGD(TAG, "time: '%s', date: '%s'", terms[1].c_str(), terms[9].c_str());
84+
if ((terms[1].size() < 6) || (terms[9].size() < 6))
85+
return;
86+
uint32_t timeinfo = int(parse_number<float>(terms[1]).value());
87+
uint32_t dateinfo = int(parse_number<uint32_t>(terms[9]).value());
88+
ESP_LOGD(TAG, "processing %d %d", timeinfo, dateinfo);
89+
ESPTime now{};
90+
now.year = dateinfo % 100 + 2000;
91+
now.month = dateinfo / 100 % 100;
92+
now.day_of_month = dateinfo / 10000;
93+
// Set these to valid value for recalc_timestamp_utc - it's not used for calculation
94+
now.day_of_week = 1;
95+
now.day_of_year = 1;
96+
97+
now.hour = timeinfo / 10000;
98+
now.minute = timeinfo / 100 % 100;
99+
now.second = timeinfo % 100;
100+
now.recalc_timestamp_utc(false);
101+
this->synchronize_epoch_(now.timestamp);
102+
this->have_time_ = true;
103+
}
104+
105+
void GPSTime::update() { this->have_time_ = false; }
106+
107+
void GPSTime::dump_config() { ESP_LOGCONFIG("", "GPS time", this); }
108+
109+
} // namespace gpstime
110+
} // namespace esphome

components/gpstime/gpstime.h

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#pragma once
2+
3+
#include "esphome/components/time/real_time_clock.h"
4+
#include "esphome/components/uart/uart.h"
5+
6+
namespace esphome {
7+
namespace gpstime {
8+
9+
class GPSTime : public time::RealTimeClock, public uart::UARTDevice {
10+
public:
11+
float get_setup_priority() const override { return setup_priority::LATE; }
12+
void loop() override;
13+
void update() override;
14+
void dump_config() override;
15+
16+
protected:
17+
bool receiving_{false};
18+
bool ending_{false};
19+
void handle_message_();
20+
std::vector<uint8_t> rx_buffer_;
21+
bool have_time_{false};
22+
};
23+
24+
} // namespace gpstime
25+
} // namespace esphome

components/gpstime/time.py

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from esphome.components import time as time_
2+
import esphome.config_validation as cv
3+
import esphome.codegen as cg
4+
from esphome.components import uart
5+
from esphome.const import CONF_ID
6+
7+
CODEOWNERS = ["@ssieb"]
8+
9+
DEPENDENCIES = ['uart']
10+
11+
gpstime_ns = cg.esphome_ns.namespace('gpstime')
12+
13+
GPSTime = gpstime_ns.class_('GPSTime', time_.RealTimeClock, uart.UARTDevice)
14+
15+
CONFIG_SCHEMA = (
16+
time_.TIME_SCHEMA.extend(
17+
{
18+
cv.GenerateID(): cv.declare_id(GPSTime),
19+
}
20+
)
21+
.extend(uart.UART_DEVICE_SCHEMA)
22+
.extend(cv.polling_component_schema("15min"))
23+
)
24+
25+
async def to_code(config):
26+
var = cg.new_Pvariable(config[CONF_ID])
27+
await cg.register_component(var, config)
28+
await time_.register_time(var, config)
29+
await uart.register_uart_device(var, config)
30+

0 commit comments

Comments
 (0)