From 74ca4e04bb521c2594379ed2b674f1771395abc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Poczkodi?= <gabest11@gmail.com> Date: Fri, 11 Oct 2024 17:25:41 +0200 Subject: [PATCH] KT0803 FM Transmitter initial commit --- CODEOWNERS | 1 + esphome/components/kt0803/__init__.py | 86 ++++ esphome/components/kt0803/kt0803.cpp | 283 +++++++++++++ esphome/components/kt0803/kt0803.h | 74 ++++ esphome/components/kt0803/kt0803defs.h | 398 ++++++++++++++++++ esphome/components/kt0803/number/__init__.py | 38 ++ .../kt0803/number/frequency_number.cpp | 12 + .../kt0803/number/frequency_number.h | 18 + 8 files changed, 910 insertions(+) create mode 100644 esphome/components/kt0803/__init__.py create mode 100644 esphome/components/kt0803/kt0803.cpp create mode 100644 esphome/components/kt0803/kt0803.h create mode 100644 esphome/components/kt0803/kt0803defs.h create mode 100644 esphome/components/kt0803/number/__init__.py create mode 100644 esphome/components/kt0803/number/frequency_number.cpp create mode 100644 esphome/components/kt0803/number/frequency_number.h diff --git a/CODEOWNERS b/CODEOWNERS index ed9c13a975..8faa3526ea 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -219,6 +219,7 @@ esphome/components/json/* @OttoWinter esphome/components/kamstrup_kmp/* @cfeenstra1024 esphome/components/key_collector/* @ssieb esphome/components/key_provider/* @ssieb +esphome/components/kt0803/* @gabest11 esphome/components/kuntze/* @ssieb esphome/components/lcd_menu/* @numo68 esphome/components/ld2410/* @regevbr @sebcaps diff --git a/esphome/components/kt0803/__init__.py b/esphome/components/kt0803/__init__.py new file mode 100644 index 0000000000..77a2a214c2 --- /dev/null +++ b/esphome/components/kt0803/__init__.py @@ -0,0 +1,86 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation +from esphome.components import i2c, sensor, text_sensor, binary_sensor +from esphome.const import ( + CONF_ID, + CONF_FREQUENCY, + DEVICE_CLASS_POWER, + DEVICE_CLASS_EMPTY, +) + +CODEOWNERS = ["@gabest11"] +DEPENDENCIES = ["i2c"] +AUTO_LOAD = ["sensor", "text_sensor", "binary_sensor", "number", "switch", "select", "text"] +MULTI_CONF = True + +UNIT_MEGA_HERTZ = "MHz" +UNIT_KILO_HERTZ = "KHz" +UNIT_MILLI_VOLT = "mV" +UNIT_MICRO_AMPERE = "mA" +UNIT_DECIBEL_MICRO_VOLT = "dBµV" + +ICON_VOLUME_MUTE = "mdi:volume-mute" +ICON_EAR_HEARING = "mdi:ear-hearing" +ICON_RADIO_TOWER = "mdi:radio-tower" +ICON_SLEEP = "mdi:sleep" +ICON_SINE_WAVE = "mdi:sine-wave" +ICON_RESISTOR = "mdi:resistor" +ICON_FORMAT_TEXT = "mdi:format-text" + +kt0803_ns = cg.esphome_ns.namespace("kt0803") +KT0803Component = kt0803_ns.class_( + "KT0803Component", cg.PollingComponent, i2c.I2CDevice +) + +CONF_KT0803_ID = "kt0803_id" +CONF_CHIP_ID = "chip_id" +CONF_PW_OK = "pw_ok" +CONF_SLNCID = "slncid" + +SetFrequencyAction = kt0803_ns.class_( + "SetFrequencyAction", automation.Action, cg.Parented.template(KT0803Component) +) + +ChipId = kt0803_ns.enum("ChipId", True) +CHIP_ID = { + "KT0803": ChipId.KT0803, + "KT0803K": ChipId.KT0803K, + "KT0803M": ChipId.KT0803M, + "KT0803L": ChipId.KT0803L, +} + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(KT0803Component), + cv.Required(CONF_CHIP_ID): cv.enum(CHIP_ID), + cv.Optional(CONF_FREQUENCY, default=87.50): cv.float_range(70, 108), + cv.Optional(CONF_PW_OK): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_POWER, + icon=ICON_RADIO_TOWER, + ), + cv.Optional(CONF_SLNCID): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_EMPTY, + icon=ICON_VOLUME_MUTE, + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x3E)) +) + + +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) + cg.add(var.set_chip_id(config.get(CONF_CHIP_ID))) + if conf_frequency := config.get(CONF_FREQUENCY): + cg.add(var.set_frequency(conf_frequency)) + if conf_pw_ok := config.get(CONF_PW_OK): + s = await binary_sensor.new_binary_sensor(conf_pw_ok) + cg.add(var.set_pw_ok_binary_sensor(s)) + if conf_slncid := config.get(CONF_SLNCID): + s = await binary_sensor.new_binary_sensor(conf_slncid) + cg.add(var.set_slncid_binary_sensor(s)) diff --git a/esphome/components/kt0803/kt0803.cpp b/esphome/components/kt0803/kt0803.cpp new file mode 100644 index 0000000000..eaca52f95d --- /dev/null +++ b/esphome/components/kt0803/kt0803.cpp @@ -0,0 +1,283 @@ +#include "kt0803.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include <cstdio> +#include <cmath> + +namespace esphome { +namespace kt0803 { + +// TODO: std::clamp isn't here yet +#define clamp(v, lo, hi) std::max(std::min(v, hi), lo) + +static const char *const TAG = "kt0803"; + +KT0803Component::KT0803Component() { + this->chip_id_ = ChipId::KT0803L; + this->reset_ = false; + memset(&this->state_, 0, sizeof(this->state_)); + // set datasheet defaults, except frequency to 87.5MHz + this->state_.REG_00 = 0x6B; + this->state_.REG_01 = 0xC3; + this->state_.REG_02 = 0x40; + this->state_.REG_04 = 0x04; + this->state_.REG_0E = 0x02; + this->state_.REG_10 = 0xA8; + this->state_.REG_12 = 0x80; + this->state_.REG_13 = 0x80; + this->state_.REG_15 = 0xE0; + this->state_.REG_26 = 0xA0; +} + +bool KT0803Component::check_reg_(uint8_t addr) { + switch (addr) { // check KT0803 address range + case 0x00: + case 0x01: + case 0x02: + case 0x13: + return true; + } + + if (this->chip_id_ != ChipId::KT0803) { + switch (addr) { // check KT0803K/M address range too + case 0x00: + case 0x01: + case 0x02: + case 0x04: + case 0x0B: + case 0x0E: + case 0x0F: + case 0x10: + case 0x12: + case 0x14: + case 0x16: + return true; + } + } + + if (this->chip_id_ == ChipId::KT0803L) { + switch (addr) { // check KT0803L address range too + case 0x17: + case 0x1E: + case 0x26: + case 0x27: + return true; + } + } + + return false; +} + +void KT0803Component::write_reg_(uint8_t addr) { + if (addr >= sizeof(this->regs_) || !this->check_reg_(addr)) { + ESP_LOGE(TAG, "write_reg_(0x%02X) invalid register address", addr); + return; + } + + if (addr == 0x13 && this->state_.PA_CTRL == 1) { + ESP_LOGW(TAG, "write_reg_(0x%02X) PA_CTRL = 1 can destroy the device", addr); + return; // TODO: remove this when everything tested and works + } + + if (this->reset_) { + uint8_t value = this->regs_[addr]; + ESP_LOGV(TAG, "write_reg_(0x%02X) = 0x%02X", addr, value); + this->write_byte(addr, value); + } else { + if (this->get_component_state() & COMPONENT_STATE_LOOP) { + ESP_LOGE(TAG, "write_reg_(0x%02X) device was not reset", addr); + } + } +} + +bool KT0803Component::read_reg_(uint8_t addr) { + if (addr >= sizeof(this->regs_) || !this->check_reg_(addr)) { + ESP_LOGE(TAG, "write_reg_(0x%02X) invalid register address", addr); + return false; + } + + uint8_t c; + if (i2c::ERROR_OK == this->read_register(addr, &c, 1, false)) { + this->regs_[addr] = c; + return true; + } + + ESP_LOGE(TAG, "read_reg_(0x%02X) cannot read register", addr); + return false; +} + +// overrides + +void KT0803Component::setup() { + /* + for (size_t addr = 0; addr < 0x2F; addr++) { + uint8_t c; + if (i2c::ERROR_OK == this->read_register(addr, &c, 1, false)) { + ESP_LOGV(TAG, "setup register[%02X]: %02X", addr, c); + } + } + */ + + this->reset_ = true; + + for (size_t addr = 0; addr < sizeof(this->state_); addr++) { + if (addr != 0x0F && this->check_reg_(addr)) { + this->write_reg_(addr); + } + } + + this->publish_pw_ok(); + this->publish_slncid(); + this->publish_frequency(); +} + +void KT0803Component::dump_config() { + ESP_LOGCONFIG(TAG, "KT0803:"); + LOG_I2C_DEVICE(this); + if (this->is_failed()) { + ESP_LOGE(TAG, "failed!"); + } + ESP_LOGCONFIG(TAG, " Chip: %s", this->get_chip_string().c_str()); + ESP_LOGCONFIG(TAG, " Frequency: %.2f MHz", this->get_frequency()); + // TODO: ...and everything else... + LOG_UPDATE_INTERVAL(this); +} + +void KT0803Component::update() { + if (this->read_reg_(0x0F)) { + this->publish_pw_ok(); + this->publish_slncid(); + } +/* + for (size_t addr = 0; addr < 0x2F; addr++) { + uint8_t c; + if (i2c::ERROR_OK == this->read_register(addr, &c, 1, false)) { + ESP_LOGV(TAG, "update register[%02X]: %02X", addr, c); + } + } +*/ +} + +void KT0803Component::loop() { +} + +// config + +void KT0803Component::set_chip_id(ChipId value) { + this->chip_id_ = value; +} + +ChipId KT0803Component::get_chip_id() { + return this->chip_id_; +} + +std::string KT0803Component::get_chip_string() const { + switch (this->chip_id_) { + case ChipId::KT0803: + return "KT0803"; + case ChipId::KT0803K: + return "KT0803K"; + case ChipId::KT0803M: + return "KT0803M"; + case ChipId::KT0803L: + return "KT0803L"; + default: + return "Unknown"; + } +} + +void KT0803Component::set_frequency(float value) { + if (!(CHSEL_MIN <= value && value <= CHSEL_MAX)) { + ESP_LOGE(TAG, "set_frequency(%.2f) invalid (%.2f - %.2f)", value, CHSEL_MIN, CHSEL_MAX); + return; + } + + uint16_t ch = (uint16_t) std::lround(value * 20); + this->state_.CHSEL2 = (uint8_t) ((ch >> 9) & 0x07); + this->state_.CHSEL1 = (uint8_t) ((ch >> 1) & 0xff); + this->state_.CHSEL0 = (uint8_t) ((ch >> 0) & 0x01); + this->write_reg_(0x00); + this->write_reg_(0x01); + this->write_reg_(0x02); + + this->publish_frequency(); +} + +float KT0803Component::get_frequency() { + uint16_t ch = 0; + ch |= (uint16_t) this->state_.CHSEL2 << 9; + ch |= (uint16_t) this->state_.CHSEL1 << 1; + if (this->chip_id_ != ChipId::KT0803) { + ch |= (uint16_t) this->state_.CHSEL0 << 0; + } + return (float) ch / 20; +} + +// publish + +void KT0803Component::publish_pw_ok() { this->publish(this->pw_ok_binary_sensor_, this->state_.PW_OK == 1); } + +void KT0803Component::publish_slncid() { this->publish(this->slncid_binary_sensor_, this->state_.SLNCID == 1); } + +void KT0803Component::publish_frequency() { this->publish(this->frequency_number_, this->get_frequency()); } + +void KT0803Component::publish(text_sensor::TextSensor *s, const std::string &state) { + if (s != nullptr) { + if (!s->has_state() || s->state != state) { + s->publish_state(state); + } + } +} + +void KT0803Component::publish(binary_sensor::BinarySensor *s, bool state) { + if (s != nullptr) { + if (!s->has_state() || s->state != state) { + s->publish_state(state); + } + } +} + +void KT0803Component::publish(sensor::Sensor *s, float state) { + if (s != nullptr) { + if (!s->has_state() || s->state != state) { + s->publish_state(state); + } + } +} + +void KT0803Component::publish(number::Number *n, float state) { + if (n != nullptr) { + if (!n->has_state() || n->state != state) { + n->publish_state(state); + } + } +} + +void KT0803Component::publish(switch_::Switch *s, bool state) { + if (s != nullptr) { + if (s->state != state) { // ? + s->publish_state(state); + } + } +} + +void KT0803Component::publish(select::Select *s, size_t index) { + if (s != nullptr) { + if (auto state = s->at(index)) { + if (!s->has_state() || s->state != *state) { + s->publish_state(*state); + } + } + } +} + +void KT0803Component::publish(text::Text *t, const std::string &state) { + if (t != nullptr) { + if (!t->has_state() || t->state != state) { + t->publish_state(state); + } + } +} + +} // namespace kt0803 +} // namespace esphome diff --git a/esphome/components/kt0803/kt0803.h b/esphome/components/kt0803/kt0803.h new file mode 100644 index 0000000000..1e68810dc1 --- /dev/null +++ b/esphome/components/kt0803/kt0803.h @@ -0,0 +1,74 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/text_sensor/text_sensor.h" +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/components/number/number.h" +#include "esphome/components/switch/switch.h" +#include "esphome/components/select/select.h" +#include "esphome/components/text/text.h" +#include <string> +#include "kt0803defs.h" + +namespace esphome { +namespace kt0803 { + +#ifndef SUB_TEXT +#define SUB_TEXT(name) \ + protected: \ + text::Text *name##_text_{nullptr}; \ +\ + public: \ + void set_##name##_text(text::Text *text) { this->name##_text_ = text; } +#endif + +class KT0803Component : public PollingComponent, public i2c::I2CDevice { + ChipId chip_id_; // no way to detect it + bool reset_; + union { + struct KT0803State state_; + uint8_t regs_[sizeof(struct KT0803State)]; + }; + + bool check_reg_(uint8_t addr); + void write_reg_(uint8_t addr); + bool read_reg_(uint8_t addr); + + SUB_BINARY_SENSOR(pw_ok) + SUB_BINARY_SENSOR(slncid) + SUB_NUMBER(frequency) + + void publish_pw_ok(); + void publish_slncid(); + void publish_frequency(); + + void publish(sensor::Sensor *s, float state); + void publish(binary_sensor::BinarySensor *s, bool state); + void publish(text_sensor::TextSensor *s, const std::string &state); + void publish(number::Number *n, float state); + void publish(switch_::Switch *s, bool state); + void publish(select::Select *s, size_t index); + void publish(text::Text *t, const std::string &state); + + public: + KT0803Component(); + + // float get_setup_priority() const override { return setup_priority::HARDWARE; } + void setup() override; + void dump_config() override; + void update() override; + void loop() override; + + void set_chip_id(ChipId value); + ChipId get_chip_id(); + std::string get_chip_string() const; + + void set_frequency(float value); // MHz + float get_frequency(); +}; + +} // namespace kt0803 +} // namespace esphome diff --git a/esphome/components/kt0803/kt0803defs.h b/esphome/components/kt0803/kt0803defs.h new file mode 100644 index 0000000000..9d31c491af --- /dev/null +++ b/esphome/components/kt0803/kt0803defs.h @@ -0,0 +1,398 @@ +#pragma once + +namespace esphome { +namespace kt0803 { + +static float CHSEL_MIN = 70.0f; +static float CHSEL_MAX = 108.0f; + +static float PGA_MIN = -15; +static float PGA_MAX = 12; + +static float RFGAIN_MIN = 95.5f; +static float RFGAIN_MAX = 108.0f; + +enum class ChipId { + KT0803, + KT0803K, + KT0803M, + KT0803L, +}; + +enum class PreEmphasis { + PHTCNST_50US, + PHTCNST_75US, + LAST, +}; + +enum class PilotToneAmplitudeAdjustment { + PLTADJ_LOW, + PLTADJ_HIGH, + LAST, +}; + +enum class BassBoostControl { + BASS_DISABLED, + BASS_5DB, + BASS_11DB, + BASS_17DB, + LAST, +}; + +enum class AlcTimeSelection { + ALC_TIME_25US, + ALC_TIME_50US, + ALC_TIME_75US, + ALC_TIME_100US, + ALC_TIME_125US, + ALC_TIME_150US, + ALC_TIME_175US, + ALC_TIME_200US, + ALC_TIME_50MS, + ALC_TIME_100MS, + ALC_TIME_150MS, + ALC_TIME_200MS, + ALC_TIME_250MS, + ALC_TIME_300MS, + ALC_TIME_350MS, + ALC_TIME_40MS, + LAST, +}; + +enum class SilenceDetectionLowThreshold { + SLNCTHL_025MV, + SLNCTHL_050MV, + SLNCTHL_1MV, + SLNCTHL_2MV, + SLNCTHL_4MV, + SLNCTHL_8MV, + SLNCTHL_16MV, + SLNCTHL_32MV, + LAST, +}; + +enum class SilenceDetectionHighThreshold { + SLNCTHH_050MV, + SLNCTHH_1MV, + SLNCTHH_2MV, + SLNCTHH_4MV, + SLNCTHH_8MV, + SLNCTHH_16MV, + SLNCTHH_32MV, + SLNCTHH_64MV, + LAST, +}; + +enum class SilenceDetectionHighLevelCounterThreshold { + SLNCCNTHIGH_15, + SLNCCNTHIGH_31, + SLNCCNTHIGH_63, + SLNCCNTHIGH_127, + SLNCCNTHIGH_255, + SLNCCNTHIGH_511, + SLNCCNTHIGH_1023, + SLNCCNTHIGH_2047, + LAST, +}; + +enum class SilenceDetectionLowAndHighLevelDurationTime { + SLNCTIME_50MS, + SLNCTIME_100MS, + SLNCTIME_200MS, + SLNCTIME_400MS, + SLNCTIME_1S, + SLNCTIME_2S, + SLNCTIME_4S, + SLNCTIME_8S, + SLNCTIME_16S, + SLNCTIME_24S, + SLNCTIME_32S, + SLNCTIME_40S, + SLNCTIME_48S, + SLNCTIME_56S, + SLNCTIME_60S, + SLNCTIME_64S, + LAST, +}; + +enum class AlcCompressedGainSetting { + ALCCMPGAIN_N6DB, + ALCCMPGAIN_N9DB, + ALCCMPGAIN_N12DB, + ALCCMPGAIN_N15DB, + ALCCMPGAIN_6DB, + ALCCMPGAIN_3DB, + ALCCMPGAIN_0DB, + ALCCMPGAIN_N3DB, + LAST, +}; + +enum class SilenceLowCounter { + SLNCCNTLOW_1, + SLNCCNTLOW_2, + SLNCCNTLOW_4, + SLNCCNTLOW_8, + SLNCCNTLOW_16, + SLNCCNTLOW_32, + SLNCCNTLOW_64, + SLNCCNTLOW_128, + LAST, +}; + +enum class FrequencyDeviationAdjustment { + FDEV_75KHZ, + FDEV_112K5HZ, + FDEV_150KHZ, + FDEV_187K5HZ, + LAST, +}; + +enum class XtalSelection { + XTAL_SEL_32K768HZ, + XTAL_SEL_7M6HZ, + LAST, +}; + +enum class ReferenceClockSelection { + REF_CLK_32K768HZ, + REF_CLK_6M5HZ, + REF_CLK_7M6HZ, + REF_CLK_12MHZ, + REF_CLK_13MHZ, + REF_CLK_15M2HZ, + REF_CLK_19M2HZ, + REF_CLK_24MHZ, + REF_CLK_26MHZ, + LAST, +}; + +enum class AlcHighThresholdSelection { + ALCHIGHTH_50MS, + ALCHIGHTH_100MS, + ALCHIGHTH_150MS, + ALCHIGHTH_200MS, + ALCHIGHTH_1S, + ALCHIGHTH_5S, + ALCHIGHTH_10S, + ALCHIGHTH_15S, + LAST, +}; + +enum class AlcHoldTimeSelection { + ALCHOLD_06, + ALCHOLD_05, + ALCHOLD_04, + ALCHOLD_03, + ALCHOLD_02, + ALCHOLD_01, + ALCHOLD_005, + ALCHOLD_001, + LAST, +}; + +enum class AlcLowThreshold { + ALCLOWTH_025, + ALCLOWTH_020, + ALCLOWTH_015, + ALCLOWTH_010, + ALCLOWTH_005, + ALCLOWTH_003, + ALCLOWTH_002, + ALCLOWTH_001, + ALCLOWTH_0005, + ALCLOWTH_0001, + ALCLOWTH_00005, + ALCLOWTH_00001, + LAST, +}; + +// 0 = KT0803 +// K = KT0803K or KT0803M (not datasheet for M, it should be mostly the same as K) +// L = KT0803L + +struct KT0803State { + union { + uint8_t REG_00; + struct { + uint8_t CHSEL1 : 8; // 0 K L + }; + }; + union { + uint8_t REG_01; + struct { + uint8_t CHSEL2 : 3; // 0 K L + uint8_t PGA : 3; // 0 K L + uint8_t RFGAIN0 : 2; // 0 K L + }; + }; + union { + uint8_t REG_02; + struct { + uint8_t PHTCNST : 1; // 0 K L + uint8_t _02_1 : 1; + uint8_t PLTADJ : 1; // 0 K L + uint8_t MUTE : 1; // 0 K L + uint8_t _02_2 : 2; + uint8_t RFGAIN2 : 1; // 0 K L + uint8_t CHSEL0 : 1; // K L (no LSB on 0, step size is only 100KHz) + }; + }; + uint8_t REG_03; + union { + uint8_t REG_04; + struct { + uint8_t BASS : 2; // K L + uint8_t FDEV_K : 2; // K + uint8_t PGA_LSB : 2; // K L + uint8_t MONO : 1; // K L + uint8_t ALC_EN : 1; // L + }; + }; + uint8_t REG_05; + uint8_t REG_06; + uint8_t REG_07; + uint8_t REG_08; + uint8_t REG_09; + uint8_t REG_0A; + union { + uint8_t REG_0B; + struct { + uint8_t _0B_1 : 2; + uint8_t AUTO_PADN : 1; // L + uint8_t _0B_2 : 2; + uint8_t PDPA : 1; // K L + uint8_t _0B_3 : 1; + uint8_t Standby : 1; // L + }; + }; + union { + uint8_t REG_0C; + struct { + uint8_t ALC_ATTACK_TIME : 4; // L + uint8_t ALC_DECAY_TIME : 4; // L + }; + }; + union { + uint8_t REG_0E; + struct { + uint8_t _0E_1 : 1; + uint8_t PA_BIAS : 1; // K L + uint8_t _0E_2 : 6; + }; + }; + union { + uint8_t REG_0F; + struct { + uint8_t _0F_1 : 2; + uint8_t SLNCID : 1; // K L (ro) + uint8_t _0F_2 : 1; + uint8_t PW_OK : 1; // K L (ro) + uint8_t _0F_3 : 3; + }; + }; + union { + uint8_t REG_10; + struct { + uint8_t PGAMOD : 1; // K L + uint8_t _10_1 : 2; + uint8_t LMTLVL : 2; // K + uint8_t _10_2 : 4; + }; + }; + uint8_t REG_11; + union { + uint8_t REG_12; + struct { + uint8_t SW_MOD : 1; // K L + uint8_t SLNCTHH : 3; // K L + uint8_t SLNCTHL : 3; // K L + uint8_t SLNCDIS : 1; // K L + }; + }; + union { + uint8_t REG_13; + struct { + uint8_t _13_1 : 2; + uint8_t PA_CTRL : 1; // 0 K L + uint8_t _13_2 : 4; + uint8_t RFGAIN1 : 1; // 0 K L + }; + }; + union { + uint8_t REG_14; + struct { + uint8_t SLNCTIME_MSB : 1; // L + uint8_t _14_1 : 1; + uint8_t SLNCCNTHIGH : 3; // K L + uint8_t SLNCTIME : 3; // K L + }; + }; + union { + uint8_t REG_15; + struct { + uint8_t _15_1 : 5; + uint8_t ALCCMPGAIN : 3; // L + }; + }; + union { + uint8_t REG_16; + struct { + uint8_t SLNCCNTLOW : 3; // K L + uint8_t _16_1 : 5; + }; + }; + union { + uint8_t REG_17; + struct { + uint8_t _17_1 : 3; + uint8_t XTAL_SEL : 1; // L + uint8_t _17_2 : 1; + uint8_t AU_ENHANCE : 1; // L + uint8_t FDEV_L : 1; // L + uint8_t _17_3 : 1; + }; + }; + uint8_t REG_18; + uint8_t REG_19; + uint8_t REG_1A; + uint8_t REG_1B; + uint8_t REG_1C; + uint8_t REG_1D; + union { + uint8_t REG_1E; + struct { + uint8_t _1E_1 : 1; + uint8_t REF_CLK : 3; // L + uint8_t _1E_2 : 1; + uint8_t XTALD : 1; // L + uint8_t DCLK : 1; // L + uint8_t _1E_3 : 1; + }; + }; + uint8_t REG_1F; + uint8_t REG_20; + uint8_t REG_21; + uint8_t REG_22; + uint8_t REG_23; + uint8_t REG_24; + uint8_t REG_25; + union { + uint8_t REG_26; + struct { + uint8_t _26_1 : 1; + uint8_t ALCHIGHTH : 3; // L + uint8_t _26_2 : 1; + uint8_t ALCHOLD : 3; // L + }; + }; + union { + uint8_t REG_27; + struct { + uint8_t ALCLOWTH : 4; // L + uint8_t _27_1 : 4; + }; + }; +}; + +} // namespace kt0803 +} // namespace esphome diff --git a/esphome/components/kt0803/number/__init__.py b/esphome/components/kt0803/number/__init__.py new file mode 100644 index 0000000000..68b84b0695 --- /dev/null +++ b/esphome/components/kt0803/number/__init__.py @@ -0,0 +1,38 @@ +import esphome.codegen as cg +from esphome.components import number +import esphome.config_validation as cv +from esphome.const import ( + CONF_FREQUENCY, + DEVICE_CLASS_FREQUENCY, + ENTITY_CATEGORY_CONFIG, +) +from .. import ( + CONF_KT0803_ID, + KT0803Component, + kt0803_ns, + UNIT_MEGA_HERTZ, +) + +FrequencyNumber = kt0803_ns.class_("FrequencyNumber", number.Number) + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_KT0803_ID): cv.use_id(KT0803Component), + cv.Optional(CONF_FREQUENCY): number.number_schema( + FrequencyNumber, + unit_of_measurement=UNIT_MEGA_HERTZ, + device_class=DEVICE_CLASS_FREQUENCY, + entity_category=ENTITY_CATEGORY_CONFIG, + ), + } +) + + +async def to_code(config): + kt0803_component = await cg.get_variable(config[CONF_KT0803_ID]) + if frequency_config := config.get(CONF_FREQUENCY): + n = await number.new_number( + frequency_config, min_value=70, max_value=108, step=0.05 + ) + await cg.register_parented(n, config[CONF_KT0803_ID]) + cg.add(kt0803_component.set_frequency_number(n)) diff --git a/esphome/components/kt0803/number/frequency_number.cpp b/esphome/components/kt0803/number/frequency_number.cpp new file mode 100644 index 0000000000..30cb39e8b5 --- /dev/null +++ b/esphome/components/kt0803/number/frequency_number.cpp @@ -0,0 +1,12 @@ +#include "frequency_number.h" + +namespace esphome { +namespace kt0803 { + +void FrequencyNumber::control(float value) { + this->publish_state(value); + this->parent_->set_frequency(value); +} + +} // namespace kt0803 +} // namespace esphome diff --git a/esphome/components/kt0803/number/frequency_number.h b/esphome/components/kt0803/number/frequency_number.h new file mode 100644 index 0000000000..ba7c69b49b --- /dev/null +++ b/esphome/components/kt0803/number/frequency_number.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/number/number.h" +#include "../kt0803.h" + +namespace esphome { +namespace kt0803 { + +class FrequencyNumber : public number::Number, public Parented<KT0803Component> { + public: + FrequencyNumber() = default; + + protected: + void control(float value) override; +}; + +} // namespace kt0803 +} // namespace esphome