Added ds3231 external component. Restored sthome-ut8.yaml

This commit is contained in:
Chris Stuurman 2026-01-18 23:57:20 +02:00
parent 783879ae2a
commit 56124beb98
6 changed files with 4330 additions and 301 deletions

View File

View File

@ -0,0 +1,111 @@
#include "ds3231.h"
#include "esphome/core/log.h"
// Datasheet:
// - https://datasheets.maximintegrated.com/en/ds/DS3231.pdf
namespace esphome {
namespace ds3231 {
static const char *const TAG = "ds3231";
void DS3231Component::setup() {
if (!this->read_rtc_()) {
this->mark_failed();
}
}
void DS3231Component::update() { this->read_time(); }
void DS3231Component::dump_config() {
ESP_LOGCONFIG(TAG, "DS3231:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str());
if (!this->read_bytes(0, this->ds3231_.raw, sizeof(this->ds3231_.raw))) {
ESP_LOGE(TAG, "Can't read I2C data.");
}
}
float DS3231Component::get_setup_priority() const { return setup_priority::DATA; }
void DS3231Component::read_time() {
if (!this->read_rtc_()) {
return;
}
if (ds3231_.reg.ch) {
ESP_LOGW(TAG, "RTC halted, not syncing to system clock.");
return;
}
ESPTime rtc_time{
.second = uint8_t(ds3231_.reg.second + 10 * ds3231_.reg.second_10),
.minute = uint8_t(ds3231_.reg.minute + 10u * ds3231_.reg.minute_10),
.hour = uint8_t(ds3231_.reg.hour + 10u * ds3231_.reg.hour_10),
.day_of_week = uint8_t(ds3231_.reg.weekday),
.day_of_month = uint8_t(ds3231_.reg.day + 10u * ds3231_.reg.day_10),
.day_of_year = 1, // ignored by recalc_timestamp_utc(false)
.month = uint8_t(ds3231_.reg.month + 10u * ds3231_.reg.month_10),
.year = uint16_t(ds3231_.reg.year + 10u * ds3231_.reg.year_10 + 2000),
.is_dst = false, // not used
.timestamp = 0 // overwritten by recalc_timestamp_utc(false)
};
rtc_time.recalc_timestamp_utc(false);
if (!rtc_time.is_valid()) {
ESP_LOGE(TAG, "Invalid RTC time, not syncing to system clock.");
return;
}
time::RealTimeClock::synchronize_epoch_(rtc_time.timestamp);
}
void DS3231Component::write_time() {
auto now = time::RealTimeClock::utcnow();
if (!now.is_valid()) {
ESP_LOGE(TAG, "Invalid system time, not syncing to RTC.");
return;
}
ds3231_.reg.year = (now.year - 2000) % 10;
ds3231_.reg.year_10 = (now.year - 2000) / 10 % 10;
ds3231_.reg.month = now.month % 10;
ds3231_.reg.month_10 = now.month / 10;
ds3231_.reg.day = now.day_of_month % 10;
ds3231_.reg.day_10 = now.day_of_month / 10;
ds3231_.reg.weekday = now.day_of_week;
ds3231_.reg.hour = now.hour % 10;
ds3231_.reg.hour_10 = now.hour / 10;
ds3231_.reg.minute = now.minute % 10;
ds3231_.reg.minute_10 = now.minute / 10;
ds3231_.reg.second = now.second % 10;
ds3231_.reg.second_10 = now.second / 10;
ds3231_.reg.ch = false;
this->write_rtc_();
}
bool DS3231Component::read_rtc_() {
if (!this->read_bytes(0, this->ds3231_.raw, sizeof(this->ds3231_.raw))) {
ESP_LOGE(TAG, "Can't read I2C data.");
return false;
}
ESP_LOGD(TAG, "Read %0u%0u:%0u%0u:%0u%0u 20%0u%0u-%0u%0u-%0u%0u CH:%s RS:%0u SQWE:%s OUT:%s", ds3231_.reg.hour_10,
ds3231_.reg.hour, ds3231_.reg.minute_10, ds3231_.reg.minute, ds3231_.reg.second_10, ds3231_.reg.second,
ds3231_.reg.year_10, ds3231_.reg.year, ds3231_.reg.month_10, ds3231_.reg.month, ds3231_.reg.day_10,
ds3231_.reg.day, ONOFF(ds3231_.reg.ch), ds3231_.reg.rs, ONOFF(ds3231_.reg.sqwe), ONOFF(ds3231_.reg.out));
return true;
}
bool DS3231Component::write_rtc_() {
if (!this->write_bytes(0, this->ds3231_.raw, sizeof(this->ds3231_.raw))) {
ESP_LOGE(TAG, "Can't write I2C data.");
return false;
}
ESP_LOGD(TAG, "Write %0u%0u:%0u%0u:%0u%0u 20%0u%0u-%0u%0u-%0u%0u CH:%s RS:%0u SQWE:%s OUT:%s", ds3231_.reg.hour_10,
ds3231_.reg.hour, ds3231_.reg.minute_10, ds3231_.reg.minute, ds3231_.reg.second_10, ds3231_.reg.second,
ds3231_.reg.year_10, ds3231_.reg.year, ds3231_.reg.month_10, ds3231_.reg.month, ds3231_.reg.day_10,
ds3231_.reg.day, ONOFF(ds3231_.reg.ch), ds3231_.reg.rs, ONOFF(ds3231_.reg.sqwe), ONOFF(ds3231_.reg.out));
return true;
}
} // namespace ds3231
} // namespace esphome

View File

@ -0,0 +1,70 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/time/real_time_clock.h"
namespace esphome {
namespace ds3231 {
class DS3231Component : public time::RealTimeClock, public i2c::I2CDevice {
public:
void setup() override;
void update() override;
void dump_config() override;
float get_setup_priority() const override;
void read_time();
void write_time();
protected:
bool read_rtc_();
bool write_rtc_();
union DS3231Reg {
struct {
uint8_t second : 4;
uint8_t second_10 : 3;
bool ch : 1;
uint8_t minute : 4;
uint8_t minute_10 : 3;
uint8_t unused_1 : 1;
uint8_t hour : 4;
uint8_t hour_10 : 2;
uint8_t unused_2 : 2;
uint8_t weekday : 3;
uint8_t unused_3 : 5;
uint8_t day : 4;
uint8_t day_10 : 2;
uint8_t unused_4 : 2;
uint8_t month : 4;
uint8_t month_10 : 1;
uint8_t unused_5 : 3;
uint8_t year : 4;
uint8_t year_10 : 4;
uint8_t rs : 2;
uint8_t unused_6 : 2;
bool sqwe : 1;
uint8_t unused_7 : 2;
bool out : 1;
} reg;
mutable uint8_t raw[sizeof(reg)];
} ds3231_;
};
template<typename... Ts> class WriteAction : public Action<Ts...>, public Parented<DS3231Component> {
public:
void play(Ts... x) override { this->parent_->write_time(); }
};
template<typename... Ts> class ReadAction : public Action<Ts...>, public Parented<DS3231Component> {
public:
void play(Ts... x) override { this->parent_->read_time(); }
};
} // namespace ds3231
} // namespace esphome

58
components/ds3231/time.py Normal file
View File

@ -0,0 +1,58 @@
from esphome import automation
import esphome.codegen as cg
from esphome.components import i2c, time
import esphome.config_validation as cv
from esphome.const import CONF_ID
# adapted from DS1307 code by badbadc0ffee
CODEOWNERS = ["@stuurmcp"]
DEPENDENCIES = ["i2c"]
ds3231_ns = cg.esphome_ns.namespace("ds3231")
DS3231Component = ds3231_ns.class_("DS3231Component", time.RealTimeClock, i2c.I2CDevice)
WriteAction = ds3231_ns.class_("WriteAction", automation.Action)
ReadAction = ds3231_ns.class_("ReadAction", automation.Action)
CONFIG_SCHEMA = time.TIME_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(DS3231Component),
}
).extend(i2c.i2c_device_schema(0x68))
@automation.register_action(
"ds3231.write_time",
WriteAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DS3231Component),
}
),
)
async def ds3231_write_time_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
return var
@automation.register_action(
"ds3231.read_time",
ReadAction,
automation.maybe_simple_id(
{
cv.GenerateID(): cv.use_id(DS3231Component),
}
),
)
async def ds3231_read_time_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
return var
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
await time.register_time(var, config)

View File

@ -182,7 +182,9 @@ void TLC59208FOutput::loop()
uint8_t reg = TLC59208F_REG_PWM0 + channel; uint8_t reg = TLC59208F_REG_PWM0 + channel;
if (!this->write_byte(reg, pwm)) { if (!this->write_byte(reg, pwm)) {
this->status_set_warning(); char buffer[128];
snprintf(buffer, sizeof(buffer), "Writing pwm value %d to channel %d failed.", pwm, channel);
this->status_set_warning(buffer);
return; return;
} }
} }

File diff suppressed because it is too large Load Diff